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 }