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 import org.jetbrains.kotlin.resolve.calls.tasks.TasksPackage; 039 040 import java.util.Map; 041 042 import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.*; 043 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*; 044 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName; 045 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName; 046 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension; 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 (TasksPackage.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 String name = getNameForAnnotatedObjectWithOverrides(descriptor); 331 if (name != null) return getEnclosingScope(descriptor).declareName(name); 332 return null; 333 } 334 }; 335 336 Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() { 337 @Override 338 public JsName apply(@NotNull DeclarationDescriptor descriptor) { 339 //TODO: refactor 340 if (!(descriptor instanceof FunctionDescriptor)) { 341 return null; 342 } 343 FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor); 344 if (overriddenDescriptor == null) { 345 return null; 346 } 347 348 JsScope scope = getEnclosingScope(descriptor); 349 JsName result = getNameForDescriptor(overriddenDescriptor); 350 scope.declareName(result.getIdent()); 351 return result; 352 } 353 }; 354 355 addRule(namesForDynamic); 356 addRule(namesForStandardClasses); 357 addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass); 358 addRule(propertyOrPropertyAccessor); 359 addRule(predefinedObjectsHasUnobfuscatableNames); 360 addRule(overridingDescriptorsReferToOriginalName); 361 addRule(memberDeclarationsInsideParentsScope); 362 } 363 } 364 365 @NotNull 366 public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) { 367 JsScope scope = getEnclosingScope(descriptor); 368 return fresh ? scope.declareFreshName(name) : scope.declareName(name); 369 } 370 371 @NotNull 372 private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) { 373 DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); 374 return getScopeForDescriptor(containingDeclaration.getOriginal()); 375 } 376 377 private final class ScopeGenerator extends Generator<JsScope> { 378 379 public ScopeGenerator() { 380 Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() { 381 @Override 382 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 383 if (!(descriptor instanceof ClassDescriptor)) { 384 return null; 385 } 386 if (getSuperclass((ClassDescriptor) descriptor) == null) { 387 return getRootScope().innerObjectScope("Scope for class " + descriptor.getName()); 388 } 389 return null; 390 } 391 }; 392 Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() { 393 @Override 394 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 395 if (!(descriptor instanceof ClassDescriptor)) { 396 return null; 397 } 398 ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor); 399 if (superclass == null) { 400 return null; 401 } 402 return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName()); 403 } 404 }; 405 Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() { 406 @Override 407 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 408 if (!(descriptor instanceof PackageFragmentDescriptor)) { 409 return null; 410 } 411 return getRootScope().innerObjectScope("Package " + descriptor.getName()); 412 } 413 }; 414 //TODO: never get there 415 Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() { 416 @Override 417 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 418 JsScope enclosingScope = getEnclosingScope(descriptor); 419 return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName()); 420 } 421 }; 422 Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() { 423 @Override 424 public JsScope apply(@NotNull DeclarationDescriptor descriptor) { 425 if (!(descriptor instanceof CallableDescriptor)) { 426 return null; 427 } 428 JsScope enclosingScope = getEnclosingScope(descriptor); 429 430 JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope); 431 assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor; 432 scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); 433 return correspondingFunction.getScope(); 434 } 435 }; 436 addRule(createFunctionObjectsForCallableDescriptors); 437 addRule(generateNewScopesForClassesWithNoAncestors); 438 addRule(generateInnerScopesForDerivedClasses); 439 addRule(generateNewScopesForPackageDescriptors); 440 addRule(generateInnerScopesForMembers); 441 } 442 } 443 444 @Nullable 445 public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { 446 if (qualifierIsNull.get(descriptor.getOriginal()) != null) { 447 return null; 448 } 449 return qualifiers.get(descriptor.getOriginal()); 450 } 451 452 private final class QualifierGenerator extends Generator<JsExpression> { 453 public QualifierGenerator() { 454 Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 455 @Override 456 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 457 if (!standardClasses.isStandardObject(descriptor)) { 458 return null; 459 } 460 return namer.kotlinObject(); 461 } 462 }; 463 //TODO: review and refactor 464 Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() { 465 @Override 466 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 467 if (isNativeObject(descriptor)) return null; 468 469 DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor); 470 if (!(containingDescriptor instanceof PackageFragmentDescriptor)) { 471 return null; 472 } 473 474 JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName()); 475 476 String moduleName = getExternalModuleName(descriptor); 477 if (moduleName == null) { 478 return result; 479 } 480 481 if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) { 482 return null; 483 } 484 485 return JsAstUtils.replaceRootReference( 486 result, namer.getModuleReference(program.getStringLiteral(moduleName))); 487 } 488 }; 489 Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() { 490 @Override 491 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 492 if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) { 493 //noinspection ConstantConditions 494 return getQualifierForDescriptor(descriptor.getContainingDeclaration()); 495 } 496 return null; 497 } 498 }; 499 Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() { 500 @Override 501 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 502 if (isLibraryObject(descriptor)) { 503 return namer.kotlinObject(); 504 } 505 return null; 506 } 507 }; 508 Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() { 509 @Override 510 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 511 if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null; 512 513 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 514 if (containingDeclaration != null && isNativeObject(containingDeclaration)) { 515 return getQualifiedReference(containingDeclaration); 516 } 517 518 return null; 519 } 520 }; 521 Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() { 522 @Override 523 public JsExpression apply(@NotNull DeclarationDescriptor descriptor) { 524 if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) { 525 CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor; 526 if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) { 527 return getQualifiedReference(callableDescriptor.getContainingDeclaration()); 528 } 529 } 530 531 return null; 532 } 533 }; 534 535 addRule(libraryObjectsHaveKotlinQualifier); 536 addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass); 537 addRule(standardObjectsHaveKotlinQualifier); 538 addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier); 539 addRule(nativeObjectsHaveNativePartOfFullQualifier); 540 addRule(staticMembersHaveContainerQualifier); 541 } 542 } 543 544 private static class QualifierIsNullGenerator extends Generator<Boolean> { 545 546 private QualifierIsNullGenerator() { 547 Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() { 548 @Override 549 public Boolean apply(@NotNull DeclarationDescriptor descriptor) { 550 if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) { 551 return true; 552 } 553 return null; 554 } 555 }; 556 addRule(propertiesInClassHaveNoQualifiers); 557 } 558 } 559 }