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
017package org.jetbrains.k2js.translate.context;
018
019import com.google.dart.compiler.backend.js.ast.*;
020import com.intellij.psi.PsiElement;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
024import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025import org.jetbrains.jet.lang.descriptors.Named;
026import org.jetbrains.jet.lang.psi.JetExpression;
027import org.jetbrains.jet.lang.resolve.BindingContext;
028import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator;
029import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
030
031import java.util.HashMap;
032import java.util.Map;
033
034import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
035
036/**
037 * All the info about the state of the translation process.
038 */
039public 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}