001    /*
002     * Copyright 2010-2013 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.k2js.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.jet.lang.descriptors.CallableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025    import org.jetbrains.jet.lang.descriptors.Named;
026    import org.jetbrains.jet.lang.psi.JetExpression;
027    import org.jetbrains.jet.lang.resolve.BindingContext;
028    import org.jetbrains.k2js.translate.declaration.ClassDeclarationTranslator;
029    import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator;
030    import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
031    
032    import java.util.HashMap;
033    import java.util.Map;
034    
035    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
036    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
037    
038    /**
039     * All the info about the state of the translation process.
040     */
041    public class TranslationContext {
042        @NotNull
043        private final DynamicContext dynamicContext;
044        @NotNull
045        private final StaticContext staticContext;
046        @NotNull
047        private final AliasingContext aliasingContext;
048        @Nullable
049        private final UsageTracker usageTracker;
050    
051        @NotNull
052        public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
053            DynamicContext rootDynamicContext =
054                    DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
055            AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
056            return new TranslationContext(staticContext, rootDynamicContext, rootAliasingContext, null);
057        }
058    
059        private final HashMap<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
060    
061        public boolean isEcma5() {
062            return staticContext.isEcma5();
063        }
064    
065        private TranslationContext(
066                @NotNull StaticContext staticContext,
067                @NotNull DynamicContext dynamicContext,
068                @NotNull AliasingContext aliasingContext,
069                @Nullable UsageTracker usageTracker
070        ) {
071            this.dynamicContext = dynamicContext;
072            this.staticContext = staticContext;
073            this.aliasingContext = aliasingContext;
074            this.usageTracker = usageTracker;
075        }
076    
077        private TranslationContext(@NotNull TranslationContext parent, @NotNull AliasingContext aliasingContext) {
078            this(parent.staticContext, parent.dynamicContext, aliasingContext, parent.usageTracker);
079        }
080    
081        private TranslationContext(
082                @NotNull TranslationContext parent,
083                @NotNull JsFunction fun,
084                @NotNull AliasingContext aliasingContext,
085                @Nullable UsageTracker usageTracker
086        ) {
087            this(parent.staticContext, DynamicContext.newContext(fun.getScope(), fun.getBody()), aliasingContext,
088                 usageTracker == null ? parent.usageTracker : usageTracker);
089        }
090    
091        @Nullable
092        public UsageTracker usageTracker() {
093            return usageTracker;
094        }
095    
096        public DynamicContext dynamicContext() {
097            return dynamicContext;
098        }
099    
100        @NotNull
101        public TranslationContext contextWithScope(@NotNull JsFunction fun) {
102            return new TranslationContext(this, fun, aliasingContext, null);
103        }
104    
105        @NotNull
106        private TranslationContext contextWithScope(@NotNull JsScope newScope,
107                @NotNull JsBlock block,
108                @NotNull AliasingContext aliasingContext,
109                @Nullable UsageTracker usageTracker) {
110            return new TranslationContext(staticContext, DynamicContext.newContext(newScope, block), aliasingContext, usageTracker);
111        }
112    
113        @NotNull
114        public TranslationContext newFunctionBody(
115                @NotNull JsFunction fun,
116                @Nullable AliasingContext aliasingContext,
117                @Nullable UsageTracker usageTracker
118        ) {
119            return new TranslationContext(this, fun, aliasingContext == null ? new AliasingContext(this.aliasingContext) : aliasingContext,
120                                          usageTracker);
121        }
122    
123        @NotNull
124        public TranslationContext innerBlock(@NotNull JsBlock block) {
125            return new TranslationContext(staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker);
126        }
127    
128        @NotNull
129        public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) {
130            return contextWithScope(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor), aliasingContext, usageTracker);
131        }
132    
133        @NotNull
134        public TranslationContext innerContextWithThisAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsNameRef alias) {
135            return new TranslationContext(this, aliasingContext.inner(correspondingDescriptor, alias));
136        }
137    
138        @NotNull
139        public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsName> aliases) {
140            return new TranslationContext(this, aliasingContext.withExpressionsAliased(aliases));
141        }
142    
143        @NotNull
144        public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
145            return new TranslationContext(this, aliasingContext.withDescriptorsAliased(aliases));
146        }
147    
148        @NotNull
149        public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
150            if (descriptor instanceof CallableDescriptor) {
151                return getFunctionObject((CallableDescriptor)descriptor).getBody();
152            }
153            else {
154                return new JsBlock();
155            }
156        }
157    
158        @NotNull
159        public BindingContext bindingContext() {
160            return staticContext.getBindingContext();
161        }
162    
163        @NotNull
164        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
165            return staticContext.getScopeForDescriptor(descriptor);
166        }
167    
168        @NotNull
169        public JsName getNameForElement(@NotNull PsiElement element) {
170            DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
171            return getNameForDescriptor(descriptor);
172        }
173    
174        @NotNull
175        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
176            return staticContext.getNameForDescriptor(descriptor);
177        }
178    
179        @NotNull
180        public JsStringLiteral nameToLiteral(@NotNull Named named) {
181            return program().getStringLiteral(named.getName().asString());
182        }
183    
184        @NotNull
185        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
186            return staticContext.getQualifiedReference(descriptor);
187        }
188    
189        @Nullable
190        public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
191            return staticContext.getQualifierForDescriptor(descriptor);
192        }
193    
194        @NotNull
195        public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
196            return dynamicContext.declareTemporary(initExpression);
197        }
198    
199        @NotNull
200        public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
201            TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
202    
203            if (tempVar == null) {
204                TemporaryVariable tmpVar = declareTemporary(expression);
205    
206                tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
207    
208                expressionToTempConstVariableCache.put(expression, tempVar);
209                expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
210            }
211    
212            return tempVar;
213        }
214    
215        public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
216            assert expression == temporaryConstVariable.assignmentExpression();
217            expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
218        }
219    
220        @NotNull
221        public Namer namer() {
222            return staticContext.getNamer();
223        }
224    
225        @NotNull
226        public Intrinsics intrinsics() {
227            return staticContext.getIntrinsics();
228        }
229    
230        @NotNull
231        public JsProgram program() {
232            return staticContext.getProgram();
233        }
234    
235        @NotNull
236        public JsScope scope() {
237            return dynamicContext.getScope();
238        }
239    
240        @NotNull
241        public AliasingContext aliasingContext() {
242            return aliasingContext;
243        }
244    
245        @NotNull
246        public LiteralFunctionTranslator literalFunctionTranslator() {
247            return staticContext.getLiteralFunctionTranslator();
248        }
249    
250        @NotNull
251        public ClassDeclarationTranslator classDeclarationTranslator() {
252            return staticContext.getClassDeclarationTranslator();
253        }
254    
255        @NotNull
256        public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
257            return staticContext.getFunctionWithScope(descriptor);
258        }
259    
260        public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
261            dynamicContext.jsBlock().getStatements().add(statement);
262        }
263    
264        @Nullable
265        public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
266            if (usageTracker != null) {
267                usageTracker.triggerUsed(descriptor);
268            }
269            return aliasingContext.getAliasForDescriptor(descriptor);
270        }
271    
272        @NotNull
273        public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) {
274            DeclarationDescriptor effectiveDescriptor;
275            if (descriptor instanceof CallableDescriptor) {
276                effectiveDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
277                assert effectiveDescriptor != null;
278            }
279            else {
280                effectiveDescriptor = descriptor;
281            }
282    
283            if (usageTracker != null) {
284                usageTracker.triggerUsed(effectiveDescriptor);
285            }
286    
287            JsExpression alias = aliasingContext.getAliasForDescriptor(effectiveDescriptor);
288            return alias == null ? JsLiteral.THIS : alias;
289        }
290    }