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