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