001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.js.translate.context;
018    
019    import com.google.dart.compiler.backend.js.ast.*;
020    import com.intellij.psi.PsiElement;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.descriptors.CallableDescriptor;
024    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
025    import org.jetbrains.kotlin.descriptors.MemberDescriptor;
026    import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
027    import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
028    import org.jetbrains.kotlin.js.config.Config;
029    import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
030    import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
031    import org.jetbrains.kotlin.name.FqName;
032    import org.jetbrains.kotlin.psi.JetExpression;
033    import org.jetbrains.kotlin.resolve.BindingContext;
034    import org.jetbrains.kotlin.types.reflect.ReflectionTypes;
035    
036    import java.util.HashMap;
037    import java.util.Map;
038    
039    import static org.jetbrains.kotlin.js.translate.context.ContextPackage.getNameForCapturedDescriptor;
040    import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForElement;
041    
042    /**
043     * All the info about the state of the translation process.
044     */
045    public class TranslationContext {
046        @NotNull
047        private final DynamicContext dynamicContext;
048        @NotNull
049        private final StaticContext staticContext;
050        @NotNull
051        private final AliasingContext aliasingContext;
052        @Nullable
053        private final UsageTracker usageTracker;
054        @Nullable
055        private final TranslationContext parent;
056        @Nullable
057        private final DefinitionPlace definitionPlace;
058    
059        @NotNull
060        public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
061            DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
062            AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
063            return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
064        }
065    
066        private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
067    
068        private TranslationContext(
069                @Nullable TranslationContext parent,
070                @NotNull StaticContext staticContext,
071                @NotNull DynamicContext dynamicContext,
072                @NotNull AliasingContext aliasingContext,
073                @Nullable UsageTracker usageTracker,
074                @Nullable DefinitionPlace definitionPlace
075        ) {
076            this.parent = parent;
077            this.dynamicContext = dynamicContext;
078            this.staticContext = staticContext;
079            this.aliasingContext = aliasingContext;
080            this.usageTracker = usageTracker;
081            this.definitionPlace = definitionPlace;
082        }
083    
084        @Nullable
085        public UsageTracker usageTracker() {
086            return usageTracker;
087        }
088    
089        @NotNull
090        public DynamicContext dynamicContext() {
091            return dynamicContext;
092        }
093    
094        @NotNull
095        public TranslationContext contextWithScope(@NotNull JsFunction fun) {
096            return this.newFunctionBody(fun, aliasingContext);
097        }
098    
099        @NotNull
100        public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
101            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
102            if (aliasingContext == null) {
103                aliasingContext = this.aliasingContext.inner();
104            }
105    
106            return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null);
107        }
108    
109        @NotNull
110        public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
111            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
112            UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
113            return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace);
114        }
115    
116        @NotNull
117        public TranslationContext innerBlock(@NotNull JsBlock block) {
118            return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
119        }
120    
121        @NotNull
122        public TranslationContext innerBlock() {
123            return innerBlock(new JsBlock());
124        }
125    
126        @NotNull
127        public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
128            DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
129            return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
130        }
131    
132        @NotNull
133        private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
134            return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
135        }
136    
137        @NotNull
138        public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
139            return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
140        }
141    
142        @NotNull
143        public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsExpression> aliases) {
144            return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
145        }
146    
147        @NotNull
148        public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
149            return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
150        }
151    
152        @NotNull
153        public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
154            if (descriptor instanceof CallableDescriptor) {
155                return getFunctionObject((CallableDescriptor) descriptor).getBody();
156            }
157            else {
158                return new JsBlock();
159            }
160        }
161    
162        @NotNull
163        public BindingContext bindingContext() {
164            return staticContext.getBindingContext();
165        }
166    
167        @NotNull
168        public DiagnosticSink getTrace() { return staticContext.getConfig().getTrace(); }
169    
170        @NotNull
171        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
172            return staticContext.getScopeForDescriptor(descriptor);
173        }
174    
175        @NotNull
176        public JsName getNameForElement(@NotNull PsiElement element) {
177            DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
178            return getNameForDescriptor(descriptor);
179        }
180    
181        @NotNull
182        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
183            return staticContext.getNameForDescriptor(descriptor);
184        }
185    
186        @NotNull
187        public JsName getNameForPackage(@NotNull FqName fqName) {
188            return staticContext.getNameForPackage(fqName);
189        }
190    
191        @NotNull
192        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
193            return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
194        }
195    
196        @NotNull
197        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
198            return staticContext.getQualifiedReference(descriptor);
199        }
200    
201        @NotNull
202        public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
203            return staticContext.getQualifiedReference(packageFqName);
204        }
205    
206        @Nullable
207        public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
208            return staticContext.getQualifierForDescriptor(descriptor);
209        }
210    
211        @NotNull
212        public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
213            return dynamicContext.declareTemporary(initExpression);
214        }
215    
216        @NotNull
217        public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
218            TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
219    
220            if (tempVar == null) {
221                TemporaryVariable tmpVar = declareTemporary(expression);
222    
223                tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
224    
225                expressionToTempConstVariableCache.put(expression, tempVar);
226                expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
227            }
228    
229            return tempVar;
230        }
231    
232        public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
233            assert expression == temporaryConstVariable.assignmentExpression();
234            expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
235        }
236    
237        @NotNull
238        public Namer namer() {
239            return staticContext.getNamer();
240        }
241    
242        @NotNull
243        public Intrinsics intrinsics() {
244            return staticContext.getIntrinsics();
245        }
246    
247        @NotNull
248        public ReflectionTypes getReflectionTypes() {
249            return staticContext.getReflectionTypes();
250        }
251    
252        @NotNull
253        public JsProgram program() {
254            return staticContext.getProgram();
255        }
256    
257        @NotNull
258        public Config getConfig() {
259            return staticContext.getConfig();
260        }
261    
262        @NotNull
263        public JsScope scope() {
264            return dynamicContext.getScope();
265        }
266    
267        @NotNull
268        public AliasingContext aliasingContext() {
269            return aliasingContext;
270        }
271    
272        @NotNull
273        public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
274            return staticContext.getFunctionWithScope(descriptor);
275        }
276    
277        public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
278            dynamicContext.jsBlock().getStatements().add(statement);
279        }
280    
281        public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
282            addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
283        }
284    
285        public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) {
286            dynamicContext.jsBlock().getStatements().addAll(block.getStatements());
287        }
288    
289        public boolean currentBlockIsEmpty() {
290            return dynamicContext.jsBlock().isEmpty();
291        }
292    
293        public void moveVarsFrom(@NotNull TranslationContext context) {
294            dynamicContext.moveVarsFrom(context.dynamicContext());
295        }
296    
297        @NotNull
298        public JsBlock getCurrentBlock() {
299            return dynamicContext.jsBlock();
300        }
301    
302        @NotNull
303        public JsEmpty getEmptyStatement() {
304            return program().getEmptyStatement();
305        }
306    
307        @NotNull
308        public JsExpression getEmptyExpression() {
309            return program().getEmptyExpression();
310        }
311    
312        @Nullable
313        public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
314            JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
315            if (nameRef != null) {
316                return nameRef;
317            }
318    
319            return aliasingContext.getAliasForDescriptor(descriptor);
320        }
321    
322        @NotNull
323        public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
324            JsExpression alias = getAliasForDescriptor(descriptor);
325            return alias == null ? JsLiteral.THIS : alias;
326        }
327    
328        @NotNull
329        private DefinitionPlace getDefinitionPlace() {
330            if (definitionPlace != null) return definitionPlace;
331            if (parent != null) return parent.getDefinitionPlace();
332    
333            throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
334        }
335    
336        @NotNull
337        public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
338            String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(this, descriptor);
339            return getDefinitionPlace().define(suggestedName, expression);
340        }
341    
342        @Nullable
343        private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
344            if (usageTracker != null && descriptor instanceof CallableDescriptor) {
345                CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
346    
347                usageTracker.used(callableDescriptor);
348    
349                JsName name = getNameForCapturedDescriptor(usageTracker, callableDescriptor);
350                if (name != null) return name.makeRef();
351            }
352    
353            return null;
354        }
355    }