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