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