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 }