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