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        @NotNull
085        public DynamicContext dynamicContext() {
086            return dynamicContext;
087        }
088    
089        @NotNull
090        public TranslationContext contextWithScope(@NotNull JsFunction fun) {
091            return this.newFunctionBody(fun, aliasingContext);
092        }
093    
094        @NotNull
095        public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
096            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
097            if (aliasingContext == null) {
098                aliasingContext = this.aliasingContext.inner();
099            }
100    
101            return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null);
102        }
103    
104        @NotNull
105        public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
106            DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
107            UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
108            return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace);
109        }
110    
111        @NotNull
112        public TranslationContext innerBlock(@NotNull JsBlock block) {
113            return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
114        }
115    
116        @NotNull
117        public TranslationContext innerBlock() {
118            return innerBlock(new JsBlock());
119        }
120    
121        @NotNull
122        public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
123            DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
124            return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
125        }
126    
127        @NotNull
128        private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
129            return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
130        }
131    
132        @NotNull
133        public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
134            return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
135        }
136    
137        @NotNull
138        public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<JetExpression, JsExpression> aliases) {
139            return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
140        }
141    
142        @NotNull
143        public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
144            return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
145        }
146    
147        @NotNull
148        public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
149            if (descriptor instanceof CallableDescriptor) {
150                return getFunctionObject((CallableDescriptor) descriptor).getBody();
151            }
152            else {
153                return new JsBlock();
154            }
155        }
156    
157        @NotNull
158        public BindingContext bindingContext() {
159            return staticContext.getBindingContext();
160        }
161    
162        @NotNull
163        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
164            return staticContext.getScopeForDescriptor(descriptor);
165        }
166    
167        @NotNull
168        public JsName getNameForElement(@NotNull PsiElement element) {
169            DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
170            return getNameForDescriptor(descriptor);
171        }
172    
173        @NotNull
174        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
175            return staticContext.getNameForDescriptor(descriptor);
176        }
177    
178        @NotNull
179        public JsName getNameForPackage(@NotNull FqName fqName) {
180            return staticContext.getNameForPackage(fqName);
181        }
182    
183        @NotNull
184        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
185            return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
186        }
187    
188        @NotNull
189        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
190            return staticContext.getQualifiedReference(descriptor);
191        }
192    
193        @NotNull
194        public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
195            return staticContext.getQualifiedReference(packageFqName);
196        }
197    
198        @Nullable
199        public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
200            return staticContext.getQualifierForDescriptor(descriptor);
201        }
202    
203        @NotNull
204        public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
205            return dynamicContext.declareTemporary(initExpression);
206        }
207    
208        @NotNull
209        public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
210            TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
211    
212            if (tempVar == null) {
213                TemporaryVariable tmpVar = declareTemporary(expression);
214    
215                tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
216    
217                expressionToTempConstVariableCache.put(expression, tempVar);
218                expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
219            }
220    
221            return tempVar;
222        }
223    
224        public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
225            assert expression == temporaryConstVariable.assignmentExpression();
226            expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
227        }
228    
229        @NotNull
230        public Namer namer() {
231            return staticContext.getNamer();
232        }
233    
234        @NotNull
235        public Intrinsics intrinsics() {
236            return staticContext.getIntrinsics();
237        }
238    
239        @NotNull
240        public ReflectionTypes getReflectionTypes() {
241            return staticContext.getReflectionTypes();
242        }
243    
244        @NotNull
245        public JsProgram program() {
246            return staticContext.getProgram();
247        }
248    
249        @NotNull
250        public JsScope scope() {
251            return dynamicContext.getScope();
252        }
253    
254        @NotNull
255        public AliasingContext aliasingContext() {
256            return aliasingContext;
257        }
258    
259        @NotNull
260        public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
261            return staticContext.getFunctionWithScope(descriptor);
262        }
263    
264        public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
265            dynamicContext.jsBlock().getStatements().add(statement);
266        }
267    
268        public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
269            addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
270        }
271    
272        public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) {
273            dynamicContext.jsBlock().getStatements().addAll(block.getStatements());
274        }
275    
276        public boolean currentBlockIsEmpty() {
277            return dynamicContext.jsBlock().isEmpty();
278        }
279    
280        public void moveVarsFrom(@NotNull TranslationContext context) {
281            dynamicContext.moveVarsFrom(context.dynamicContext());
282        }
283    
284        @NotNull
285        public JsBlock getCurrentBlock() {
286            return dynamicContext.jsBlock();
287        }
288    
289        @NotNull
290        public JsEmpty getEmptyStatement() {
291            return program().getEmptyStatement();
292        }
293    
294        @NotNull
295        public JsExpression getEmptyExpression() {
296            return program().getEmptyExpression();
297        }
298    
299        @Nullable
300        public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
301            JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
302            if (nameRef != null) {
303                return nameRef;
304            }
305    
306            return aliasingContext.getAliasForDescriptor(descriptor);
307        }
308    
309        @NotNull
310        public JsExpression getThisObject(@NotNull ReceiverParameterDescriptor descriptor) {
311            JsExpression alias = getAliasForDescriptor(descriptor);
312            return alias == null ? JsLiteral.THIS : alias;
313        }
314    
315        @NotNull
316        private DefinitionPlace getDefinitionPlace() {
317            if (definitionPlace != null) return definitionPlace;
318            if (parent != null) return parent.getDefinitionPlace();
319    
320            throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
321        }
322    
323        @NotNull
324        public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
325            String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(this, descriptor);
326            return getDefinitionPlace().define(suggestedName, expression);
327        }
328    
329        @Nullable
330        private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
331            if (usageTracker != null && descriptor instanceof CallableDescriptor) {
332                CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
333    
334                usageTracker.used(callableDescriptor);
335    
336                JsName name = getNameForCapturedDescriptor(usageTracker, callableDescriptor);
337                if (name != null) return name.makeRef();
338            }
339    
340            return null;
341        }
342    }