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