001 /* 002 * Copyright 2010-2015 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.common.collect.Maps; 020 import com.google.dart.compiler.backend.js.ast.*; 021 import com.intellij.openapi.util.Factory; 022 import com.intellij.util.containers.ContainerUtil; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.annotations.Nullable; 025 import org.jetbrains.kotlin.builtins.ReflectionTypes; 026 import org.jetbrains.kotlin.descriptors.*; 027 import org.jetbrains.kotlin.js.config.Config; 028 import org.jetbrains.kotlin.js.config.EcmaVersion; 029 import org.jetbrains.kotlin.js.config.LibrarySourcesConfig; 030 import org.jetbrains.kotlin.js.translate.context.generator.Generator; 031 import org.jetbrains.kotlin.js.translate.context.generator.Rule; 032 import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; 033 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; 034 import org.jetbrains.kotlin.name.FqName; 035 import org.jetbrains.kotlin.resolve.BindingContext; 036 import org.jetbrains.kotlin.resolve.BindingTrace; 037 import org.jetbrains.kotlin.resolve.DescriptorUtils; 038 039 import java.util.Map; 040 041 import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.*; 042 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*; 043 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName; 044 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName; 045 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension; 046 import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic; 047 048 /** 049 * Aggregates all the static parts of the context. 050 */ 051 public final class StaticContext { 052 053 public static StaticContext generateStaticContext(@NotNull BindingTrace bindingTrace, @NotNull Config config, @NotNull ModuleDescriptor moduleDescriptor) { 054 JsProgram program = new JsProgram("main"); 055 Namer namer = Namer.newInstance(program.getRootScope()); 056 Intrinsics intrinsics = new Intrinsics(); 057 StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope()); 058 return new StaticContext(program, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config, moduleDescriptor); 059 } 060 061 @NotNull 062 private final JsProgram program; 063 064 @NotNull 065 private final BindingTrace bindingTrace; 066 @NotNull 067 private final Namer namer; 068 069 @NotNull 070 private final Intrinsics intrinsics; 071 072 @NotNull 073 private final StandardClasses standardClasses; 074 075 @NotNull 076 private final ReflectionTypes reflectionTypes; 077 078 @NotNull 079 private final JsScope rootScope; 080 081 @NotNull 082 private final Generator<JsName> names = new NameGenerator(); 083 @NotNull 084 private final Map<FqName, JsName> packageNames = Maps.newHashMap(); 085 @NotNull 086 private final Generator<JsScope> scopes = new ScopeGenerator(); 087 @NotNull 088 private final Generator<JsExpression> qualifiers = new QualifierGenerator(); 089 @NotNull 090 private final Generator<Boolean> qualifierIsNull = new QualifierIsNullGenerator(); 091 092 @NotNull 093 private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap(); 094 095 @NotNull 096 private final Config config; 097 098 @NotNull 099 private final EcmaVersion ecmaVersion; 100 101 //TODO: too many parameters in constructor 102 private StaticContext(@NotNull JsProgram program, @NotNull BindingTrace bindingTrace, 103 @NotNull Namer namer, @NotNull Intrinsics intrinsics, 104 @NotNull StandardClasses standardClasses, @NotNull JsScope rootScope, @NotNull Config config, @NotNull ModuleDescriptor moduleDescriptor) { 105 this.program = program; 106 this.bindingTrace = bindingTrace; 107 this.namer = namer; 108 this.intrinsics = intrinsics; 109 this.rootScope = rootScope; 110 this.standardClasses = standardClasses; 111 this.config = config; 112 this.ecmaVersion = config.getTarget(); 113 this.reflectionTypes = new ReflectionTypes(moduleDescriptor); 114 } 115 116 public boolean isEcma5() { 117 return ecmaVersion == EcmaVersion.v5; 118 } 119 120 @NotNull 121 public JsProgram getProgram() { 122 return program; 123 } 124 125 @NotNull 126 public BindingTrace getBindingTrace() { 127 return bindingTrace; 128 } 129 130 @NotNull 131 public BindingContext getBindingContext() { 132 return bindingTrace.getBindingContext(); 133 } 134 135 @NotNull 136 public Intrinsics getIntrinsics() { 137 return intrinsics; 138 } 139 140 @NotNull 141 public Namer getNamer() { 142 return namer; 143 } 144 145 @NotNull 146 public ReflectionTypes getReflectionTypes() { 147 return reflectionTypes; 148 } 149 150 @NotNull 151 public JsScope getRootScope() { 152 return rootScope; 153 } 154 155 @NotNull 156 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { 157 JsScope scope = scopes.get(descriptor.getOriginal()); 158 assert scope != null : "Must have a scope for descriptor"; 159 return scope; 160 } 161 162 @NotNull 163 public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) { 164 JsScope scope = getScopeForDescriptor(descriptor); 165 JsFunction function = scopeToFunction.get(scope); 166 assert scope.equals(function.getScope()) : "Inconsistency."; 167 return function; 168 } 169 170 @NotNull 171 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) { 172 if (descriptor instanceof PackageViewDescriptor) { 173 return getQualifiedReference(((PackageViewDescriptor) descriptor).getFqName()); 174 } 175 if (descriptor instanceof PackageFragmentDescriptor) { 176 return getQualifiedReference(((PackageFragmentDescriptor) descriptor).getFqName()); 177 } 178 179 return new JsNameRef(getNameForDescriptor(descriptor), getQualifierForDescriptor(descriptor)); 180 } 181 182 @NotNull 183 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) { 184 return new JsNameRef(getNameForPackage(packageFqName), 185 packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent())); 186 } 187 188 @NotNull 189 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { 190 JsName name = names.get(descriptor.getOriginal()); 191 assert name != null : "Must have name for descriptor"; 192 return name; 193 } 194 195 @NotNull 196 public JsName getNameForPackage(@NotNull final FqName packageFqName) { 197 return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory<JsName>() { 198 @Override 199 public JsName create() { 200 String name = Namer.generatePackageName(packageFqName); 201 return getRootScope().declareName(name); 202 } 203 }); 204 } 205 206 @NotNull 207 private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) { 208 JsNameRef result = null; 209 JsNameRef qualifier = null; 210 211 FqName fqName = packageFqName; 212 213 while (true) { 214 JsNameRef ref = getNameForPackage(fqName).makeRef(); 215 216 if (qualifier == null) { 217 result = ref; 218 } 219 else { 220 qualifier.setQualifier(ref); 221 } 222 223 qualifier = ref; 224 225 if (fqName.isRoot()) break; 226 fqName = fqName.parent(); 227 } 228 229 return result; 230 } 231 232 @NotNull 233 public Config getConfig() { 234 return config; 235 } 236 237 private final class NameGenerator extends Generator<JsName> { 238 239 public NameGenerator() { 240 Rule<JsName> namesForDynamic = new Rule<JsName>() { 241 @Override 242 @Nullable 243 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 244 if (isDynamic(descriptor)) { 245 String name = descriptor.getName().asString(); 246 return JsDynamicScope.INSTANCE.declareName(name); 247 } 248 249 return null; 250 } 251 }; 252 253 Rule<JsName> namesForStandardClasses = new Rule<JsName>() { 254 @Override 255 @Nullable 256 public JsName apply(@NotNull DeclarationDescriptor data) { 257 if (!standardClasses.isStandardObject(data)) { 258 return null; 259 } 260 return standardClasses.getStandardObjectName(data); 261 } 262 }; 263 Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() { 264 @Override 265 @Nullable 266 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 267 JsScope scope = getEnclosingScope(descriptor); 268 return scope.declareFreshName(getSuggestedName(descriptor)); 269 } 270 }; 271 Rule<JsName> constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() { 272 @Override 273 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 274 if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() || 275 DescriptorUtils.isCompanionObject(descriptor) 276 ) { 277 //noinspection ConstantConditions 278 return getNameForDescriptor(descriptor.getContainingDeclaration()); 279 } 280 return null; 281 } 282 }; 283 284 // ecma 5 property name never declares as obfuscatable: 285 // 1) property cannot be overloaded, so, name collision is not possible 286 // 2) main reason: if property doesn't have any custom accessor, value holder will have the same name as accessor, so, the same name will be declared more than once 287 // 288 // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2 289 Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() { 290 @Override 291 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 292 PropertyDescriptor propertyDescriptor; 293 if (descriptor instanceof PropertyAccessorDescriptor) { 294 propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty(); 295 } 296 else if (descriptor instanceof PropertyDescriptor) { 297 propertyDescriptor = (PropertyDescriptor) descriptor; 298 } 299 else { 300 return null; 301 } 302 303 String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor); 304 if (nameFromAnnotation != null) { 305 return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false); 306 } 307 308 String propertyName = getSuggestedName(propertyDescriptor); 309 310 if (!isExtension(propertyDescriptor)) { 311 if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) { 312 propertyName = getMangledName(propertyDescriptor, propertyName); 313 } 314 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false); 315 } else { 316 assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor; 317 318 boolean isGetter = descriptor instanceof PropertyGetterDescriptor; 319 String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false); 320 return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false); 321 } 322 } 323 }; 324 325 Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() { 326 @Override 327 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 328 // The mixing of override and rename by annotation(e.g. native) is forbidden. 329 if (descriptor instanceof CallableMemberDescriptor && 330 !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) { 331 return null; 332 } 333 334 if (descriptor instanceof ConstructorDescriptor) { 335 DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration(); 336 assert classDescriptor != null; 337 descriptor = classDescriptor; 338 } 339 340 String name = getNameForAnnotatedObjectWithOverrides(descriptor); 341 if (name != null) return getEnclosingScope(descriptor).declareName(name); 342 return null; 343 } 344 }; 345 346 Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() { 347 @Override 348 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 349 //TODO: refactor 350 if (!(descriptor instanceof FunctionDescriptor)) { 351 return null; 352 } 353 FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor); 354 if (overriddenDescriptor == null) { 355 return null; 356 } 357 358 JsScope scope = getEnclosingScope(descriptor); 359 JsName result = getNameForDescriptor(overriddenDescriptor); 360 scope.declareName(result.getIdent()); 361 return result; 362 } 363 }; 364 365 addRule(namesForDynamic); 366 addRule(namesForStandardClasses); 367 addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass); 368 addRule(propertyOrPropertyAccessor); 369 addRule(predefinedObjectsHasUnobfuscatableNames); 370 addRule(overridingDescriptorsReferToOriginalName); 371 addRule(memberDeclarationsInsideParentsScope); 372 } 373 } 374 375 @NotNull 376 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { 377 JsScope scope = getEnclosingScope(descriptor); 378 return fresh ? scope.declareFreshName(name) : scope.declareName(name); 379 } 380 381 @NotNull 382 private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) { 383 DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); 384 return getScopeForDescriptor(containingDeclaration.getOriginal()); 385 } 386 387 private final class ScopeGenerator extends Generator<JsScope> { 388 389 public ScopeGenerator() { 390 Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() { 391 @Override 392 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 393 if (!(descriptor instanceof ClassDescriptor)) { 394 return null; 395 } 396 if (getSuperclass((ClassDescriptor) descriptor) == null) { 397 return getRootScope().innerObjectScope("Scope for class " + descriptor.getName()); 398 } 399 return null; 400 } 401 }; 402 Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() { 403 @Override 404 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 405 if (!(descriptor instanceof ClassDescriptor)) { 406 return null; 407 } 408 ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor); 409 if (superclass == null) { 410 return null; 411 } 412 return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName()); 413 } 414 }; 415 Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() { 416 @Override 417 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 418 if (!(descriptor instanceof PackageFragmentDescriptor)) { 419 return null; 420 } 421 return getRootScope().innerObjectScope("Package " + descriptor.getName()); 422 } 423 }; 424 //TODO: never get there 425 Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() { 426 @Override 427 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 428 JsScope enclosingScope = getEnclosingScope(descriptor); 429 return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName()); 430 } 431 }; 432 Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() { 433 @Override 434 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 435 if (!(descriptor instanceof CallableDescriptor)) { 436 return null; 437 } 438 JsScope enclosingScope = getEnclosingScope(descriptor); 439 440 JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope); 441 assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor; 442 scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); 443 return correspondingFunction.getScope(); 444 } 445 }; 446 addRule(createFunctionObjectsForCallableDescriptors); 447 addRule(generateNewScopesForClassesWithNoAncestors); 448 addRule(generateInnerScopesForDerivedClasses); 449 addRule(generateNewScopesForPackageDescriptors); 450 addRule(generateInnerScopesForMembers); 451 } 452 } 453 454 @Nullable 455 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { 456 if (qualifierIsNull.get(descriptor.getOriginal()) != null) { 457 return null; 458 } 459 return qualifiers.get(descriptor.getOriginal()); 460 } 461 462 private final class QualifierGenerator extends Generator<JsExpression> { 463 public QualifierGenerator() { 464 Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 465 @Override 466 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 467 if (!standardClasses.isStandardObject(descriptor)) { 468 return null; 469 } 470 return namer.kotlinObject(); 471 } 472 }; 473 //TODO: review and refactor 474 Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() { 475 @Override 476 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 477 if (isNativeObject(descriptor)) return null; 478 479 DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor); 480 if (!(containingDescriptor instanceof PackageFragmentDescriptor)) { 481 return null; 482 } 483 484 JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName()); 485 486 String moduleName = getExternalModuleName(descriptor); 487 if (moduleName == null) { 488 return result; 489 } 490 491 if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) { 492 return null; 493 } 494 495 return JsAstUtils.replaceRootReference( 496 result, namer.getModuleReference(program.getStringLiteral(moduleName))); 497 } 498 }; 499 Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() { 500 @Override 501 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 502 if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) { 503 //noinspection ConstantConditions 504 return getQualifierForDescriptor(descriptor.getContainingDeclaration()); 505 } 506 return null; 507 } 508 }; 509 Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 510 @Override 511 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 512 if (isLibraryObject(descriptor)) { 513 return namer.kotlinObject(); 514 } 515 return null; 516 } 517 }; 518 Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() { 519 @Override 520 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 521 if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null; 522 523 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 524 if (containingDeclaration != null && isNativeObject(containingDeclaration)) { 525 return getQualifiedReference(containingDeclaration); 526 } 527 528 return null; 529 } 530 }; 531 Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() { 532 @Override 533 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 534 if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) { 535 CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor; 536 if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) { 537 return getQualifiedReference(callableDescriptor.getContainingDeclaration()); 538 } 539 } 540 541 return null; 542 } 543 }; 544 545 addRule(libraryObjectsHaveKotlinQualifier); 546 addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass); 547 addRule(standardObjectsHaveKotlinQualifier); 548 addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier); 549 addRule(nativeObjectsHaveNativePartOfFullQualifier); 550 addRule(staticMembersHaveContainerQualifier); 551 } 552 } 553 554 private static class QualifierIsNullGenerator extends Generator<Boolean> { 555 556 private QualifierIsNullGenerator() { 557 Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() { 558 @Override 559 public Boolean apply(@NotNull DeclarationDescriptor descriptor) { 560 if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) { 561 return true; 562 } 563 return null; 564 } 565 }; 566 addRule(propertiesInClassHaveNoQualifiers); 567 } 568 } 569 }