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