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