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