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