001 /*
002 * Copyright 2010-2016 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.kotlin.js.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.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.js.config.JsConfig;
025 import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
026 import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
027 import org.jetbrains.kotlin.name.FqName;
028 import org.jetbrains.kotlin.psi.KtExpression;
029 import org.jetbrains.kotlin.resolve.BindingContext;
030 import org.jetbrains.kotlin.resolve.BindingTrace;
031 import org.jetbrains.kotlin.resolve.DescriptorUtils;
032 import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
033
034 import java.util.*;
035
036 import static org.jetbrains.kotlin.js.translate.context.UsageTrackerKt.getNameForCapturedDescriptor;
037 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForElement;
038
039 /**
040 * All the info about the state of the translation process.
041 */
042 public class TranslationContext {
043 @NotNull
044 private final DynamicContext dynamicContext;
045 @NotNull
046 private final StaticContext staticContext;
047 @NotNull
048 private final AliasingContext aliasingContext;
049 @Nullable
050 private final UsageTracker usageTracker;
051 @Nullable
052 private final TranslationContext parent;
053 @Nullable
054 private final DefinitionPlace definitionPlace;
055 @Nullable
056 private final DeclarationDescriptor declarationDescriptor;
057 @Nullable
058 private final ClassDescriptor classDescriptor;
059
060 @NotNull
061 public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
062 DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), rootFunction.getBody());
063 AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
064 return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null, null);
065 }
066
067 private final Map<JsExpression, TemporaryConstVariable> expressionToTempConstVariableCache = new HashMap<JsExpression, TemporaryConstVariable>();
068
069 private TranslationContext(
070 @Nullable TranslationContext parent,
071 @NotNull StaticContext staticContext,
072 @NotNull DynamicContext dynamicContext,
073 @NotNull AliasingContext aliasingContext,
074 @Nullable UsageTracker usageTracker,
075 @Nullable DefinitionPlace definitionPlace,
076 @Nullable DeclarationDescriptor declarationDescriptor
077 ) {
078 this.parent = parent;
079 this.dynamicContext = dynamicContext;
080 this.staticContext = staticContext;
081 this.aliasingContext = aliasingContext;
082 this.usageTracker = usageTracker;
083 this.definitionPlace = definitionPlace;
084 this.declarationDescriptor = declarationDescriptor;
085 if (declarationDescriptor instanceof ClassDescriptor) {
086 this.classDescriptor = (ClassDescriptor) declarationDescriptor;
087 }
088 else {
089 this.classDescriptor = parent != null ? parent.classDescriptor : null;
090 }
091 }
092
093 @NotNull
094 public Map<String, JsName> getImportedModules() {
095 return staticContext.getImportedModules();
096 }
097
098 @Nullable
099 public UsageTracker usageTracker() {
100 return usageTracker;
101 }
102
103 @NotNull
104 public DynamicContext dynamicContext() {
105 return dynamicContext;
106 }
107
108 @NotNull
109 public TranslationContext contextWithScope(@NotNull JsFunction fun) {
110 return this.newFunctionBody(fun, aliasingContext, declarationDescriptor);
111 }
112
113 @NotNull
114 public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext,
115 DeclarationDescriptor descriptor) {
116 DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
117 if (aliasingContext == null) {
118 aliasingContext = this.aliasingContext.inner();
119 }
120
121 return new TranslationContext(this, this.staticContext, dynamicContext, aliasingContext, this.usageTracker, null, descriptor);
122 }
123
124 @NotNull
125 public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
126 DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
127 UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope());
128 return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker,
129 this.definitionPlace, descriptor);
130 }
131
132 @NotNull
133 public TranslationContext innerWithUsageTracker(@NotNull JsScope scope, @NotNull MemberDescriptor descriptor) {
134 UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, scope);
135 return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, definitionPlace,
136 descriptor);
137 }
138
139 @NotNull
140 public TranslationContext innerBlock(@NotNull JsBlock block) {
141 return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null,
142 this.declarationDescriptor);
143 }
144
145 @NotNull
146 public TranslationContext innerBlock() {
147 return innerBlock(new JsBlock());
148 }
149
150 @NotNull
151 public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
152 DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
153 return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place, descriptor);
154 }
155
156 @NotNull
157 private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
158 return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, null, declarationDescriptor);
159 }
160
161 @NotNull
162 public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
163 return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
164 }
165
166 @NotNull
167 public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map<KtExpression, JsExpression> aliases) {
168 return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
169 }
170
171 @NotNull
172 public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
173 return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
174 }
175
176 @NotNull
177 private JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
178 if (descriptor instanceof CallableDescriptor) {
179 return getFunctionObject((CallableDescriptor) descriptor).getBody();
180 }
181 else {
182 return new JsBlock();
183 }
184 }
185
186 @NotNull
187 public BindingContext bindingContext() {
188 return staticContext.getBindingContext();
189 }
190
191 @NotNull
192 public BindingTrace bindingTrace() {
193 return staticContext.getBindingTrace();
194 }
195
196 @NotNull
197 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
198 return staticContext.getScopeForDescriptor(descriptor);
199 }
200
201 @NotNull
202 public JsName getNameForElement(@NotNull PsiElement element) {
203 DeclarationDescriptor descriptor = getDescriptorForElement(bindingContext(), element);
204 return getNameForDescriptor(descriptor);
205 }
206
207 @NotNull
208 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
209 return staticContext.getNameForDescriptor(descriptor);
210 }
211
212 @NotNull
213 public JsName getNameForPackage(@NotNull FqName fqName) {
214 return staticContext.getNameForPackage(fqName);
215 }
216
217 @NotNull
218 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
219 return staticContext.declarePropertyOrPropertyAccessorName(descriptor, name, fresh);
220 }
221
222 @NotNull
223 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
224 return staticContext.getQualifiedReference(descriptor);
225 }
226
227 @NotNull
228 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
229 return staticContext.getQualifiedReference(packageFqName);
230 }
231
232 @Nullable
233 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
234 return staticContext.getQualifierForDescriptor(descriptor);
235 }
236
237 @NotNull
238 public TemporaryVariable declareTemporary(@Nullable JsExpression initExpression) {
239 return dynamicContext.declareTemporary(initExpression);
240 }
241
242 @NotNull
243 public JsExpression defineTemporary(@NotNull JsExpression initExpression) {
244 TemporaryVariable var = dynamicContext.declareTemporary(initExpression);
245 addStatementToCurrentBlock(var.assignmentStatement());
246 return var.reference();
247 }
248
249 @NotNull
250 public JsExpression cacheExpressionIfNeeded(@NotNull JsExpression expression) {
251 return TranslationUtils.isCacheNeeded(expression) ? defineTemporary(expression) : expression;
252 }
253
254 @NotNull
255 public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) {
256 TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression);
257
258 if (tempVar == null) {
259 TemporaryVariable tmpVar = declareTemporary(expression);
260
261 tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression());
262
263 expressionToTempConstVariableCache.put(expression, tempVar);
264 expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar);
265 }
266
267 return tempVar;
268 }
269
270 public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
271 assert expression == temporaryConstVariable.assignmentExpression();
272 expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
273 }
274
275 @NotNull
276 public Namer namer() {
277 return staticContext.getNamer();
278 }
279
280 @NotNull
281 public Intrinsics intrinsics() {
282 return staticContext.getIntrinsics();
283 }
284
285 @NotNull
286 public JsProgram program() {
287 return staticContext.getProgram();
288 }
289
290 @NotNull
291 public JsConfig getConfig() {
292 return staticContext.getConfig();
293 }
294
295 @NotNull
296 public JsScope scope() {
297 return dynamicContext.getScope();
298 }
299
300 @NotNull
301 public AliasingContext aliasingContext() {
302 return aliasingContext;
303 }
304
305 @NotNull
306 public JsFunction getFunctionObject(@NotNull CallableDescriptor descriptor) {
307 return staticContext.getFunctionWithScope(descriptor);
308 }
309
310 public void addStatementToCurrentBlock(@NotNull JsStatement statement) {
311 dynamicContext.jsBlock().getStatements().add(statement);
312 }
313
314 public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
315 addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
316 }
317
318 public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) {
319 dynamicContext.jsBlock().getStatements().addAll(block.getStatements());
320 }
321
322 public boolean currentBlockIsEmpty() {
323 return dynamicContext.jsBlock().isEmpty();
324 }
325
326 public void moveVarsFrom(@NotNull TranslationContext context) {
327 dynamicContext.moveVarsFrom(context.dynamicContext());
328 }
329
330 @NotNull
331 public JsBlock getCurrentBlock() {
332 return dynamicContext.jsBlock();
333 }
334
335 @Nullable
336 public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
337 JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
338 if (nameRef != null) {
339 return nameRef;
340 }
341
342 return aliasingContext.getAliasForDescriptor(descriptor);
343 }
344
345 @NotNull
346 public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
347 JsExpression alias = getAliasForDescriptor(descriptor);
348 if (alias != null) {
349 return alias;
350 }
351 if (DescriptorUtils.isObject(descriptor.getContainingDeclaration())) {
352 if (isConstructorOrDirectScope(descriptor.getContainingDeclaration())) {
353 return JsLiteral.THIS;
354 }
355 else {
356 return getQualifiedReference(descriptor.getContainingDeclaration());
357 }
358 }
359
360 if (descriptor.getValue() instanceof ExtensionReceiver) return JsLiteral.THIS;
361
362 ClassifierDescriptor classifier = descriptor.getValue().getType().getConstructor().getDeclarationDescriptor();
363
364 // TODO: can't tell why this assertion is valid, revisit this code later
365 assert classifier instanceof ClassDescriptor;
366
367 ClassDescriptor cls = (ClassDescriptor) classifier;
368
369 assert classDescriptor != null : "Can't get ReceiverParameterDescriptor in top level";
370 JsExpression receiver = getAliasForDescriptor(classDescriptor.getThisAsReceiverParameter());
371 if (receiver == null) {
372 receiver = JsLiteral.THIS;
373 }
374
375 return getDispatchReceiverPath(cls, receiver);
376 }
377
378 private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
379 if (declarationDescriptor instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(declarationDescriptor)) {
380 return descriptor == declarationDescriptor;
381 }
382 else {
383 return declarationDescriptor != null && descriptor == DescriptorUtils.getContainingClass(declarationDescriptor);
384 }
385 }
386
387 @NotNull
388 private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) {
389 if (cls != null) {
390 JsExpression alias = getAliasForDescriptor(cls);
391 if (alias != null) {
392 return alias;
393 }
394 }
395
396 if (classDescriptor == cls || parent == null) {
397 return thisExpression;
398 }
399
400 ClassDescriptor parentDescriptor = parent.classDescriptor;
401 if (classDescriptor != parentDescriptor) {
402 return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression));
403 }
404 else {
405 return parent.getDispatchReceiverPath(cls, thisExpression);
406 }
407 }
408
409 @NotNull
410 public DefinitionPlace getDefinitionPlace() {
411 if (definitionPlace != null) return definitionPlace;
412 if (parent != null) return parent.getDefinitionPlace();
413
414 throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
415 }
416
417 @NotNull
418 public JsNameRef define(DeclarationDescriptor descriptor, JsExpression expression) {
419 String suggestedName = TranslationUtils.getSuggestedNameForInnerDeclaration(staticContext, descriptor);
420 return getDefinitionPlace().define(suggestedName, expression);
421 }
422
423 @Nullable
424 private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
425 if (usageTracker != null) {
426 usageTracker.used(descriptor);
427
428 JsName name = getNameForCapturedDescriptor(usageTracker, descriptor);
429 if (name != null) {
430 JsNameRef result = name.makeRef();
431 if (shouldCaptureViaThis()) {
432 result.setQualifier(JsLiteral.THIS);
433 }
434 return result;
435 }
436 }
437
438 return null;
439 }
440
441 private boolean shouldCaptureViaThis() {
442 if (declarationDescriptor == null) return false;
443
444 if (DescriptorUtils.isDescriptorWithLocalVisibility(declarationDescriptor)) return false;
445 if (declarationDescriptor instanceof ConstructorDescriptor &&
446 DescriptorUtils.isDescriptorWithLocalVisibility(declarationDescriptor.getContainingDeclaration())) return false;
447
448 return true;
449 }
450
451 @Nullable
452 public DeclarationDescriptor getDeclarationDescriptor() {
453 return declarationDescriptor;
454 }
455
456 public void putClassOrConstructorClosure(@NotNull MemberDescriptor descriptor, @NotNull List<DeclarationDescriptor> closure) {
457 staticContext.putClassOrConstructorClosure(descriptor, closure);
458 }
459
460 @Nullable
461 public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor classOrConstructor) {
462 List<DeclarationDescriptor> result = staticContext.getClassOrConstructorClosure(classOrConstructor);
463 if (result == null &&
464 classOrConstructor instanceof ConstructorDescriptor &&
465 ((ConstructorDescriptor) classOrConstructor).isPrimary()
466 ) {
467 result = staticContext.getClassOrConstructorClosure((ClassDescriptor) classOrConstructor.getContainingDeclaration());
468 }
469 return result;
470 }
471
472 /**
473 * Gets an expression to pass to a constructor of a closure function. I.e. consider the case:
474 *
475 * ```
476 * fun a(x) {
477 * fun b(y) = x + y
478 * return b
479 * }
480 * ```
481 *
482 * Here, `x` is a free variable of `b`. Transform `a` into the following form:
483 *
484 * ```
485 * fun a(x) {
486 * fun b0(x0) = { y -> x0 * y }
487 * return b0(x)
488 * }
489 * ```
490 *
491 * This function generates arguments passed to newly generated `b0` closure, as well as for the similar case of local class and
492 * object expression.
493 *
494 * @param descriptor represents a free variable or, more generally, free declaration.
495 * @return expression to pass to a closure constructor.
496 */
497 @NotNull
498 public JsExpression getArgumentForClosureConstructor(@NotNull DeclarationDescriptor descriptor) {
499 JsExpression alias = getAliasForDescriptor(descriptor);
500 if (alias != null) return alias;
501 if (descriptor instanceof ReceiverParameterDescriptor) {
502 return getDispatchReceiver((ReceiverParameterDescriptor) descriptor);
503 }
504 return getNameForDescriptor(descriptor).makeRef();
505 }
506
507 @Nullable
508 public JsName getOuterClassReference(ClassDescriptor descriptor) {
509 DeclarationDescriptor container = descriptor.getContainingDeclaration();
510 if (!(container instanceof ClassDescriptor) || !descriptor.isInner()) {
511 return null;
512 }
513
514 return staticContext.getScopeForDescriptor(descriptor).declareName(Namer.OUTER_FIELD_NAME);
515 }
516
517 public void startDeclaration() {
518 ClassDescriptor classDescriptor = this.classDescriptor;
519 if (classDescriptor != null && !(classDescriptor.getContainingDeclaration() instanceof ClassOrPackageFragmentDescriptor)) {
520 staticContext.getDeferredCallSites().put(classDescriptor, new ArrayList<DeferredCallSite>());
521 }
522 }
523
524 @NotNull
525 public List<DeferredCallSite> endDeclaration() {
526 List<DeferredCallSite> result = null;
527 if (classDescriptor != null) {
528 result = staticContext.getDeferredCallSites().remove(classDescriptor);
529 }
530 if (result == null) {
531 result = Collections.emptyList();
532 }
533 return result;
534 }
535
536 public boolean shouldBeDeferred(@NotNull ConstructorDescriptor constructor) {
537 ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
538 return staticContext.getDeferredCallSites().containsKey(classDescriptor);
539 }
540
541 public void deferConstructorCall(@NotNull ConstructorDescriptor constructor, @NotNull List<JsExpression> invocationArgs) {
542 ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
543 List<DeferredCallSite> callSites = staticContext.getDeferredCallSites().get(classDescriptor);
544 if (callSites == null) throw new IllegalStateException("This method should be call only when `shouldBeDeferred` method " +
545 "reports true for given constructor: " + constructor);
546 callSites.add(new DeferredCallSite(constructor, invocationArgs, this));
547 }
548
549 @Nullable
550 public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
551 return staticContext.getModuleExpressionFor(descriptor);
552 }
553 }