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