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 }