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