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.*;
024    import org.jetbrains.jet.lang.psi.JetExpression;
025    import org.jetbrains.jet.lang.reflect.ReflectionTypes;
026    import org.jetbrains.jet.lang.resolve.BindingContext;
027    import org.jetbrains.jet.lang.resolve.name.FqName;
028    import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
029    import org.jetbrains.k2js.translate.utils.TranslationUtils;
030    
031    import java.util.HashMap;
032    import java.util.Map;
033    
034    import static org.jetbrains.k2js.translate.context.ContextPackage.getNameForCapturedDescriptor;
035    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
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        @Nullable
050        private final TranslationContext parent;
051        @Nullable
052        private final DefinitionPlace definitionPlace;
053    
054        @NotNull
055        public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
056            DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
057            AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
058            return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
059        }
060    
061        private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
062    
063        private TranslationContext(
064                @Nullable TranslationContext parent,
065                @NotNull StaticContext staticContext,
066                @NotNull DynamicContext dynamicContext,
067                @NotNull AliasingContext aliasingContext,
068                @Nullable UsageTracker usageTracker,
069                @Nullable DefinitionPlace definitionPlace
070        ) {
071            this.parent = parent;
072            this.dynamicContext = dynamicContext;
073            this.staticContext = staticContext;
074            this.aliasingContext = aliasingContext;
075            this.usageTracker = usageTracker;
076            this.definitionPlace = definitionPlace;
077        }
078    
079        @Nullable
080        public UsageTracker usageTracker() {
081            return usageTracker;
082        }
083    
084        public DynamicContext dynamicContext() {
085            return dynamicContext;
086        }
087    
088        @NotNull
089        public TranslationContext contextWithScope(@NotNull JsFunction fun) {
090            return this.newFunctionBody(fun, aliasingContext);
091        }
092    
093        @NotNull
094        public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
095            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
096            if (aliasingContext == null) {
097                aliasingContext = this.aliasingContext.inner();
098            }
099    
100            return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null);
101        }
102    
103        @NotNull
104        public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
105            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
106            UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
107            return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace);
108        }
109    
110        @NotNull
111        public TranslationContext innerBlock(@NotNull JsBlock block) {
112            return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
113        }
114    
115        @NotNull
116        public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
117            DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
118            return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
119        }
120    
121        @NotNull
122        private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
123            return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
124        }
125    
126        @NotNull
127        public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
128            return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
129        }
130    
131        @NotNull
132        public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsExpression> aliases) {
133            return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
134        }
135    
136        @NotNull
137        public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
138            return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
139        }
140    
141        @NotNull
142        public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
143            if (descriptor instanceof CallableDescriptor) {
144                return getFunctionObject((CallableDescriptor) descriptor).getBody();
145            }
146            else {
147                return new JsBlock();
148            }
149        }
150    
151        @NotNull
152        public BindingContext bindingContext() {
153            return staticContext.getBindingContext();
154        }
155    
156        @NotNull
157        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
158            return staticContext.getScopeForDescriptor(descriptor);
159        }
160    
161        @NotNull
162        public JsName getNameForElement(@NotNull PsiElement element) {
163            DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
164            return getNameForDescriptor(descriptor);
165        }
166    
167        @NotNull
168        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
169            return staticContext.getNameForDescriptor(descriptor);
170        }
171    
172        @NotNull
173        public JsName getNameForPackage(@NotNull FqName fqName) {
174            return staticContext.getNameForPackage(fqName);
175        }
176    
177        @NotNull
178        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
179            return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
180        }
181    
182        @NotNull
183        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
184            return staticContext.getQualifiedReference(descriptor);
185        }
186    
187        @NotNull
188        public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
189            return staticContext.getQualifiedReference(packageFqName);
190        }
191    
192        @Nullable
193        public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
194            return staticContext.getQualifierForDescriptor(descriptor);
195        }
196    
197        @NotNull
198        public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
199            return dynamicContext.declareTemporary(initExpression);
200        }
201    
202        @NotNull
203        public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
204            TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
205    
206            if (tempVar == null) {
207                TemporaryVariable tmpVar = declareTemporary(expression);
208    
209                tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
210    
211                expressionToTempConstVariableCache.put(expression, tempVar);
212                expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
213            }
214    
215            return tempVar;
216        }
217    
218        public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
219            assert expression == temporaryConstVariable.assignmentExpression();
220            expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
221        }
222    
223        @NotNull
224        public Namer namer() {
225            return staticContext.getNamer();
226        }
227    
228        @NotNull
229        public Intrinsics intrinsics() {
230            return staticContext.getIntrinsics();
231        }
232    
233        @NotNull
234        public ReflectionTypes getReflectionTypes() {
235            return staticContext.getReflectionTypes();
236        }
237    
238        @NotNull
239        public JsProgram program() {
240            return staticContext.getProgram();
241        }
242    
243        @NotNull
244        public JsScope scope() {
245            return dynamicContext.getScope();
246        }
247    
248        @NotNull
249        public AliasingContext aliasingContext() {
250            return aliasingContext;
251        }
252    
253        @NotNull
254        public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
255            return staticContext.getFunctionWithScope(descriptor);
256        }
257    
258        public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
259            dynamicContext.jsBlock().getStatements().add(statement);
260        }
261    
262        @Nullable
263        public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
264            JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
265            if (nameRef != null) {
266                return nameRef;
267            }
268    
269            return aliasingContext.getAliasForDescriptor(descriptor);
270        }
271    
272        @NotNull
273        public JsExpression getThisObject(@NotNull ReceiverParameterDescriptor descriptor) {
274            JsExpression alias = getAliasForDescriptor(descriptor);
275            return alias == null ? JsLiteral.THIS : alias;
276        }
277    
278        @NotNull
279        private DefinitionPlace getDefinitionPlace() {
280            if (definitionPlace != null) return definitionPlace;
281            if (parent != null) return parent.getDefinitionPlace();
282    
283            throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
284        }
285    
286        @NotNull
287        public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
288            String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(this, descriptor);
289            return getDefinitionPlace().define(suggestedName, expression);
290        }
291    
292        @Nullable
293        private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
294            if (usageTracker != null && descriptor instanceof CallableDescriptor) {
295                CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
296    
297                usageTracker.used(callableDescriptor);
298    
299                JsName name = getNameForCapturedDescriptor(usageTracker, callableDescriptor);
300                if (name != null) return name.makeRef();
301            }
302    
303            return null;
304        }
305    }