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 for (FqName pathElement : ContainerUtil.reverse(packageFqName.path())) { 212 JsNameRef ref = getNameForPackage(pathElement).makeRef(); 213 214 if (qualifier == null) { 215 result = ref; 216 } 217 else { 218 qualifier.setQualifier(ref); 219 } 220 221 qualifier = ref; 222 } 223 224 assert result != null : "didn't iterate: " + packageFqName; 225 return result; 226 } 227 228 @NotNull 229 public Config getConfig() { 230 return config; 231 } 232 233 private final class NameGenerator extends Generator<JsName> { 234 235 public NameGenerator() { 236 Rule<JsName> namesForDynamic = new Rule<JsName>() { 237 @Override 238 @Nullable 239 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 240 if (isDynamic(descriptor)) { 241 String name = descriptor.getName().asString(); 242 return JsDynamicScope.INSTANCE$.declareName(name); 243 } 244 245 return null; 246 } 247 }; 248 249 Rule<JsName> namesForStandardClasses = new Rule<JsName>() { 250 @Override 251 @Nullable 252 public JsName apply(@NotNull DeclarationDescriptor data) { 253 if (!standardClasses.isStandardObject(data)) { 254 return null; 255 } 256 return standardClasses.getStandardObjectName(data); 257 } 258 }; 259 Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() { 260 @Override 261 @Nullable 262 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 263 JsScope scope = getEnclosingScope(descriptor); 264 return scope.declareFreshName(getSuggestedName(descriptor)); 265 } 266 }; 267 Rule<JsName> constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() { 268 @Override 269 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 270 if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() || 271 DescriptorUtils.isCompanionObject(descriptor) 272 ) { 273 //noinspection ConstantConditions 274 return getNameForDescriptor(descriptor.getContainingDeclaration()); 275 } 276 return null; 277 } 278 }; 279 280 // ecma 5 property name never declares as obfuscatable: 281 // 1) property cannot be overloaded, so, name collision is not possible 282 // 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 283 // 284 // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2 285 Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() { 286 @Override 287 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 288 PropertyDescriptor propertyDescriptor; 289 if (descriptor instanceof PropertyAccessorDescriptor) { 290 propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty(); 291 } 292 else if (descriptor instanceof PropertyDescriptor) { 293 propertyDescriptor = (PropertyDescriptor) descriptor; 294 } 295 else { 296 return null; 297 } 298 299 String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor); 300 if (nameFromAnnotation != null) { 301 return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false); 302 } 303 304 String propertyName = getSuggestedName(propertyDescriptor); 305 306 if (!isExtension(propertyDescriptor)) { 307 if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) { 308 propertyName = getMangledName(propertyDescriptor, propertyName); 309 } 310 return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false); 311 } else { 312 assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor; 313 314 boolean isGetter = descriptor instanceof PropertyGetterDescriptor; 315 String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false); 316 return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false); 317 } 318 } 319 }; 320 321 Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() { 322 @Override 323 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 324 // The mixing of override and rename by annotation(e.g. native) is forbidden. 325 if (descriptor instanceof CallableMemberDescriptor && 326 !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) { 327 return null; 328 } 329 330 if (descriptor instanceof ConstructorDescriptor) { 331 DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration(); 332 assert classDescriptor != null; 333 descriptor = classDescriptor; 334 } 335 336 String name = getNameForAnnotatedObjectWithOverrides(descriptor); 337 if (name != null) return getEnclosingScope(descriptor).declareName(name); 338 return null; 339 } 340 }; 341 342 Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() { 343 @Override 344 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 345 //TODO: refactor 346 if (!(descriptor instanceof FunctionDescriptor)) { 347 return null; 348 } 349 FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor); 350 if (overriddenDescriptor == null) { 351 return null; 352 } 353 354 JsScope scope = getEnclosingScope(descriptor); 355 JsName result = getNameForDescriptor(overriddenDescriptor); 356 scope.declareName(result.getIdent()); 357 return result; 358 } 359 }; 360 361 addRule(namesForDynamic); 362 addRule(namesForStandardClasses); 363 addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass); 364 addRule(propertyOrPropertyAccessor); 365 addRule(predefinedObjectsHasUnobfuscatableNames); 366 addRule(overridingDescriptorsReferToOriginalName); 367 addRule(memberDeclarationsInsideParentsScope); 368 } 369 } 370 371 @NotNull 372 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { 373 JsScope scope = getEnclosingScope(descriptor); 374 return fresh ? scope.declareFreshName(name) : scope.declareName(name); 375 } 376 377 @NotNull 378 private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) { 379 DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); 380 return getScopeForDescriptor(containingDeclaration.getOriginal()); 381 } 382 383 private final class ScopeGenerator extends Generator<JsScope> { 384 385 public ScopeGenerator() { 386 Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() { 387 @Override 388 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 389 if (!(descriptor instanceof ClassDescriptor)) { 390 return null; 391 } 392 if (getSuperclass((ClassDescriptor) descriptor) == null) { 393 return getRootScope().innerObjectScope("Scope for class " + descriptor.getName()); 394 } 395 return null; 396 } 397 }; 398 Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() { 399 @Override 400 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 401 if (!(descriptor instanceof ClassDescriptor)) { 402 return null; 403 } 404 ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor); 405 if (superclass == null) { 406 return null; 407 } 408 return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName()); 409 } 410 }; 411 Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() { 412 @Override 413 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 414 if (!(descriptor instanceof PackageFragmentDescriptor)) { 415 return null; 416 } 417 return getRootScope().innerObjectScope("Package " + descriptor.getName()); 418 } 419 }; 420 //TODO: never get there 421 Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() { 422 @Override 423 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 424 JsScope enclosingScope = getEnclosingScope(descriptor); 425 return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName()); 426 } 427 }; 428 Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() { 429 @Override 430 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 431 if (!(descriptor instanceof CallableDescriptor)) { 432 return null; 433 } 434 JsScope enclosingScope = getEnclosingScope(descriptor); 435 436 JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope); 437 assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor; 438 scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); 439 return correspondingFunction.getScope(); 440 } 441 }; 442 addRule(createFunctionObjectsForCallableDescriptors); 443 addRule(generateNewScopesForClassesWithNoAncestors); 444 addRule(generateInnerScopesForDerivedClasses); 445 addRule(generateNewScopesForPackageDescriptors); 446 addRule(generateInnerScopesForMembers); 447 } 448 } 449 450 @Nullable 451 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { 452 if (qualifierIsNull.get(descriptor.getOriginal()) != null) { 453 return null; 454 } 455 return qualifiers.get(descriptor.getOriginal()); 456 } 457 458 private final class QualifierGenerator extends Generator<JsExpression> { 459 public QualifierGenerator() { 460 Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 461 @Override 462 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 463 if (!standardClasses.isStandardObject(descriptor)) { 464 return null; 465 } 466 return namer.kotlinObject(); 467 } 468 }; 469 //TODO: review and refactor 470 Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() { 471 @Override 472 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 473 if (isNativeObject(descriptor)) return null; 474 475 DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor); 476 if (!(containingDescriptor instanceof PackageFragmentDescriptor)) { 477 return null; 478 } 479 480 JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName()); 481 482 String moduleName = getExternalModuleName(descriptor); 483 if (moduleName == null) { 484 return result; 485 } 486 487 if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) { 488 return null; 489 } 490 491 return JsAstUtils.replaceRootReference( 492 result, namer.getModuleReference(program.getStringLiteral(moduleName))); 493 } 494 }; 495 Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() { 496 @Override 497 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 498 if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) { 499 //noinspection ConstantConditions 500 return getQualifierForDescriptor(descriptor.getContainingDeclaration()); 501 } 502 return null; 503 } 504 }; 505 Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 506 @Override 507 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 508 if (isLibraryObject(descriptor)) { 509 return namer.kotlinObject(); 510 } 511 return null; 512 } 513 }; 514 Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() { 515 @Override 516 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 517 if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null; 518 519 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 520 if (containingDeclaration != null && isNativeObject(containingDeclaration)) { 521 return getQualifiedReference(containingDeclaration); 522 } 523 524 return null; 525 } 526 }; 527 Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() { 528 @Override 529 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 530 if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) { 531 CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor; 532 if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) { 533 return getQualifiedReference(callableDescriptor.getContainingDeclaration()); 534 } 535 } 536 537 return null; 538 } 539 }; 540 541 addRule(libraryObjectsHaveKotlinQualifier); 542 addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass); 543 addRule(standardObjectsHaveKotlinQualifier); 544 addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier); 545 addRule(nativeObjectsHaveNativePartOfFullQualifier); 546 addRule(staticMembersHaveContainerQualifier); 547 } 548 } 549 550 private static class QualifierIsNullGenerator extends Generator<Boolean> { 551 552 private QualifierIsNullGenerator() { 553 Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() { 554 @Override 555 public Boolean apply(@NotNull DeclarationDescriptor descriptor) { 556 if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) { 557 return true; 558 } 559 return null; 560 } 561 }; 562 addRule(propertiesInClassHaveNoQualifiers); 563 } 564 } 565 }