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 }