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