001 /*
002 * Copyright 2010-2016 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.Lists;
020 import com.google.common.collect.Maps;
021 import com.intellij.openapi.util.Factory;
022 import com.intellij.openapi.util.text.StringUtil;
023 import com.intellij.util.containers.ContainerUtil;
024 import com.intellij.util.containers.hash.LinkedHashMap;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
028 import org.jetbrains.kotlin.descriptors.*;
029 import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
030 import org.jetbrains.kotlin.js.backend.ast.*;
031 import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
032 import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
033 import org.jetbrains.kotlin.js.config.JsConfig;
034 import org.jetbrains.kotlin.js.naming.NameSuggestion;
035 import org.jetbrains.kotlin.js.naming.SuggestedName;
036 import org.jetbrains.kotlin.js.translate.context.generator.Generator;
037 import org.jetbrains.kotlin.js.translate.context.generator.Rule;
038 import org.jetbrains.kotlin.js.translate.declaration.InterfaceFunctionCopier;
039 import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
040 import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
041 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
042 import org.jetbrains.kotlin.name.FqName;
043 import org.jetbrains.kotlin.name.FqNameUnsafe;
044 import org.jetbrains.kotlin.resolve.BindingContext;
045 import org.jetbrains.kotlin.resolve.BindingTrace;
046 import org.jetbrains.kotlin.resolve.DescriptorUtils;
047 import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
048 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
049 import org.jetbrains.kotlin.serialization.js.ModuleKind;
050 import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
051
052 import java.util.*;
053
054 import static org.jetbrains.kotlin.js.config.LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME;
055 import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject;
056 import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject;
057 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
058 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSuperclass;
059
060 /**
061 * Aggregates all the static parts of the context.
062 */
063 public final class StaticContext {
064
065 public static StaticContext generateStaticContext(
066 @NotNull BindingTrace bindingTrace,
067 @NotNull JsConfig config,
068 @NotNull ModuleDescriptor moduleDescriptor) {
069 JsProgram program = new JsProgram();
070 Namer namer = Namer.newInstance(program.getRootScope());
071 JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope());
072 return new StaticContext(program, rootFunction, bindingTrace, namer, program.getRootScope(), config, moduleDescriptor);
073 }
074
075 @NotNull
076 private final JsProgram program;
077
078 @NotNull
079 private final BindingTrace bindingTrace;
080 @NotNull
081 private final Namer namer;
082
083 @NotNull
084 private final Intrinsics intrinsics;
085
086 @NotNull
087 private final JsScope rootScope;
088
089 @NotNull
090 private final Generator<JsName> innerNames = new InnerNameGenerator();
091 @NotNull
092 private final Map<FqName, JsName> packageNames = Maps.newHashMap();
093 @NotNull
094 private final Generator<JsScope> scopes = new ScopeGenerator();
095 @NotNull
096 private final Generator<JsName> objectInstanceNames = new ObjectInstanceNameGenerator();
097
098 @NotNull
099 private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
100
101 @NotNull
102 private final Map<MemberDescriptor, List<DeclarationDescriptor>> classOrConstructorClosure = Maps.newHashMap();
103
104 @NotNull
105 private final Map<ClassDescriptor, List<DeferredCallSite>> deferredCallSites = new HashMap<ClassDescriptor, List<DeferredCallSite>>();
106
107 @NotNull
108 private final JsConfig config;
109
110 @NotNull
111 private final ModuleDescriptor currentModule;
112
113 @NotNull
114 private final NameSuggestion nameSuggestion = new NameSuggestion();
115
116 @NotNull
117 private final Map<DeclarationDescriptor, JsName> nameCache = new HashMap<DeclarationDescriptor, JsName>();
118
119 @NotNull
120 private final Map<PropertyDescriptor, JsName> backingFieldNameCache = new HashMap<PropertyDescriptor, JsName>();
121
122 @NotNull
123 private final Map<DeclarationDescriptor, JsExpression> fqnCache = new HashMap<DeclarationDescriptor, JsExpression>();
124
125 @NotNull
126 private final Map<ImportedModuleKey, ImportedModule> importedModules = new LinkedHashMap<ImportedModuleKey, ImportedModule>();
127
128 private Collection<ImportedModule> readOnlyImportedModules;
129
130 @NotNull
131 private final JsScope rootPackageScope;
132
133 @NotNull
134 private JsFunction rootFunction;
135
136 @NotNull
137 private final List<JsStatement> declarationStatements = new ArrayList<JsStatement>();
138
139 @NotNull
140 private final List<JsStatement> topLevelStatements = new ArrayList<JsStatement>();
141
142 @NotNull
143 private final List<JsStatement> importStatements = new ArrayList<JsStatement>();
144
145 @NotNull
146 private final DeclarationExporter exporter = new DeclarationExporter(this);
147
148 @NotNull
149 private final Set<ClassDescriptor> classes = new LinkedHashSet<ClassDescriptor>();
150
151 @NotNull
152 private final Map<FqName, JsScope> packageScopes = new HashMap<FqName, JsScope>();
153
154 //TODO: too many parameters in constructor
155 private StaticContext(
156 @NotNull JsProgram program,
157 @NotNull JsFunction rootFunction,
158 @NotNull BindingTrace bindingTrace,
159 @NotNull Namer namer,
160 @NotNull JsScope rootScope,
161 @NotNull JsConfig config,
162 @NotNull ModuleDescriptor moduleDescriptor
163 ) {
164 this.program = program;
165 this.rootFunction = rootFunction;
166 this.bindingTrace = bindingTrace;
167 this.namer = namer;
168 this.intrinsics = new Intrinsics(this);
169 this.rootScope = rootScope;
170 this.config = config;
171 this.currentModule = moduleDescriptor;
172 this.rootFunction = rootFunction;
173 rootPackageScope = new JsObjectScope(rootScope, "<root package>");
174
175 JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
176 importedModules.put(new ImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null),
177 new ImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName, null));
178 }
179
180 @NotNull
181 public JsProgram getProgram() {
182 return program;
183 }
184
185 @NotNull
186 public BindingTrace getBindingTrace() {
187 return bindingTrace;
188 }
189
190 @NotNull
191 public BindingContext getBindingContext() {
192 return bindingTrace.getBindingContext();
193 }
194
195 @NotNull
196 public Intrinsics getIntrinsics() {
197 return intrinsics;
198 }
199
200 @NotNull
201 public Namer getNamer() {
202 return namer;
203 }
204
205 @NotNull
206 public Collection<ImportedModule> getImportedModules() {
207 if (readOnlyImportedModules == null) {
208 readOnlyImportedModules = Collections.unmodifiableCollection(importedModules.values());
209 }
210 return readOnlyImportedModules;
211 }
212
213 @NotNull
214 public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
215 if (descriptor instanceof ModuleDescriptor) {
216 return rootScope;
217 }
218 JsScope scope = scopes.get(descriptor.getOriginal());
219 assert scope != null : "Must have a scope for descriptor";
220 return scope;
221 }
222
223 @NotNull
224 public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
225 JsScope scope = getScopeForDescriptor(descriptor);
226 JsFunction function = scopeToFunction.get(scope);
227 assert scope.equals(function.getScope()) : "Inconsistency.";
228 return function;
229 }
230
231 @NotNull
232 public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
233 return (JsNameRef) getQualifiedExpression(descriptor);
234 }
235
236 @NotNull
237 private JsExpression getQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
238 JsExpression fqn = fqnCache.get(descriptor);
239 if (fqn == null) {
240 fqn = buildQualifiedExpression(descriptor);
241 fqnCache.put(descriptor, fqn);
242 }
243 return fqn.deepCopy();
244 }
245
246 @NotNull
247 private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
248 if (descriptor instanceof ClassDescriptor) {
249 ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
250 if (KotlinBuiltIns.isAny(classDescriptor)) {
251 return pureFqn("Object", null);
252 }
253 else if (TypeUtilsKt.isThrowable(classDescriptor.getDefaultType())) {
254 return pureFqn("Error", null);
255 }
256 }
257
258 SuggestedName suggested = nameSuggestion.suggest(descriptor);
259 if (suggested == null) {
260 ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
261 JsExpression result = getModuleExpressionFor(module);
262 return result != null ? result : pureFqn(Namer.getRootPackageName(), null);
263 }
264
265 if (config.getModuleKind() != ModuleKind.PLAIN) {
266 String moduleName = AnnotationsUtils.getModuleName(suggested.getDescriptor());
267 if (moduleName != null) {
268 return JsAstUtils.pureFqn(getImportedModule(moduleName, suggested.getDescriptor()).internalName, null);
269 }
270 }
271
272 JsExpression expression;
273 List<JsName> partNames = getActualNameFromSuggested(suggested);
274 if (isLibraryObject(suggested.getDescriptor())) {
275 expression = Namer.kotlinObject();
276 }
277 // Don't generate qualifier for top-level native declarations
278 // Don't generate qualifier for local declarations
279 else if (isNativeObject(suggested.getDescriptor()) && !isNativeObject(suggested.getScope()) ||
280 suggested.getDescriptor() instanceof CallableDescriptor && suggested.getScope() instanceof FunctionDescriptor) {
281 expression = null;
282 }
283 else {
284 expression = getQualifiedExpression(suggested.getScope());
285 }
286
287 if (isNativeObject(suggested.getDescriptor()) && DescriptorUtils.isTopLevelDeclaration(suggested.getDescriptor())) {
288 String fileModuleName = AnnotationsUtils.getFileModuleName(getBindingContext(), suggested.getDescriptor());
289 if (fileModuleName != null) {
290 JsName moduleJsName = getImportedModule(fileModuleName, null).internalName;
291 expression = pureFqn(moduleJsName, expression);
292 }
293
294 String qualifier = AnnotationsUtils.getFileQualifier(getBindingContext(), suggested.getDescriptor());
295 if (qualifier != null) {
296 for (String qualifierPart : StringUtil.split(qualifier, ".")) {
297 expression = pureFqn(qualifierPart, expression);
298 }
299 }
300 }
301
302 for (JsName partName : partNames) {
303 expression = new JsNameRef(partName, expression);
304 applySideEffects(expression, suggested.getDescriptor());
305 }
306 assert expression != null : "Since partNames is not empty, expression must be non-null";
307 return expression;
308 }
309
310 @NotNull
311 public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
312 JsName packageName = getNameForPackage(packageFqName);
313 return pureFqn(packageName, packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
314 }
315
316 @NotNull
317 public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
318 if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
319 JsName result = rootScope.declareName("Object");
320 MetadataProperties.setDescriptor(result, descriptor);
321 return result;
322 }
323 SuggestedName suggested = nameSuggestion.suggest(descriptor);
324 if (suggested == null) {
325 throw new IllegalArgumentException("Can't generate name for root declarations: " + descriptor);
326 }
327 return getActualNameFromSuggested(suggested).get(0);
328 }
329
330 @NotNull
331 public JsName getNameForBackingField(@NotNull PropertyDescriptor property) {
332 JsName name = backingFieldNameCache.get(property);
333
334 if (name == null) {
335 SuggestedName fqn = nameSuggestion.suggest(property);
336 assert fqn != null : "Properties are non-root declarations: " + property;
337 assert fqn.getNames().size() == 1 : "Private names must always consist of exactly one name";
338
339 JsScope scope = getScopeForDescriptor(fqn.getScope());
340 String baseName = NameSuggestion.getPrivateMangledName(fqn.getNames().get(0), property) + "_0";
341 name = scope.declareFreshName(baseName);
342 backingFieldNameCache.put(property, name);
343 }
344
345 return name;
346 }
347
348 @NotNull
349 public JsName getInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
350 JsName name = innerNames.get(descriptor.getOriginal());
351 assert name != null : "Must have inner name for descriptor";
352 return name;
353 }
354
355 @NotNull
356 public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
357 JsName name = objectInstanceNames.get(descriptor.getOriginal());
358 assert name != null : "Must have inner name for object instance";
359 return name;
360 }
361
362 @NotNull
363 private List<JsName> getActualNameFromSuggested(@NotNull SuggestedName suggested) {
364 JsScope scope = getScopeForDescriptor(suggested.getScope());
365
366 if (DynamicCallsKt.isDynamic(suggested.getDescriptor())) {
367 scope = JsDynamicScope.INSTANCE;
368 }
369 else if (AnnotationsUtils.isPredefinedObject(suggested.getDescriptor()) &&
370 DescriptorUtils.isTopLevelDeclaration(suggested.getDescriptor())) {
371 scope = rootScope;
372 }
373
374 List<JsName> names = new ArrayList<JsName>();
375 if (suggested.getStable()) {
376 for (String namePart : suggested.getNames()) {
377 JsName name = scope.declareName(namePart);
378 MetadataProperties.setDescriptor(name, suggested.getDescriptor());
379 names.add(name);
380 }
381 }
382 else {
383 // TODO: consider using sealed class to represent FQNs
384 assert suggested.getNames().size() == 1 : "Private names must always consist of exactly one name";
385 JsName name = nameCache.get(suggested.getDescriptor());
386 if (name == null) {
387 String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0));
388 if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
389 suggested.getDescriptor() instanceof ValueParameterDescriptor
390 ) {
391 name = scope.declareTemporaryName(baseName);
392 }
393 else {
394 if (!DescriptorUtils.isDescriptorWithLocalVisibility(suggested.getDescriptor())) {
395 baseName += "_0";
396 }
397 name = scope.declareFreshName(baseName);
398 }
399 }
400 nameCache.put(suggested.getDescriptor(), name);
401 MetadataProperties.setDescriptor(name, suggested.getDescriptor());
402 names.add(name);
403 }
404
405 return names;
406 }
407
408 @NotNull
409 private JsName getNameForPackage(@NotNull final FqName packageFqName) {
410 return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory<JsName>() {
411 @Override
412 public JsName create() {
413 String name = Namer.generatePackageName(packageFqName);
414 return rootPackageScope.declareName(name);
415 }
416 });
417 }
418
419 @NotNull
420 private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
421 JsNameRef result = null;
422 JsNameRef qualifier = null;
423
424 FqName fqName = packageFqName;
425
426 while (true) {
427 JsNameRef ref = pureFqn(getNameForPackage(fqName), null);
428
429 if (qualifier == null) {
430 result = ref;
431 }
432 else {
433 qualifier.setQualifier(ref);
434 }
435
436 qualifier = ref;
437
438 if (fqName.isRoot()) break;
439 fqName = fqName.parent();
440 }
441
442 return result;
443 }
444
445 @NotNull
446 public JsConfig getConfig() {
447 return config;
448 }
449
450 @NotNull
451 public JsName importDeclaration(@NotNull String suggestedName, @NotNull JsExpression declaration) {
452 // Adding prefix is a workaround for a problem with scopes.
453 // Consider we declare name `foo` in functions's local scope, then call top-level function `foo`
454 // from another module. It's imported into global scope under name `foo`. If we could somehow
455 // declare all names in global scope before running translator, we would have got `foo_1` for local variable,
456 // since local scope inherited from global scope.
457 // TODO: remove prefix when problem with scopes is solved
458
459 JsName result = rootFunction.getScope().declareTemporaryName(suggestedName);
460 MetadataProperties.setImported(result, true);
461 importStatements.add(JsAstUtils.newVar(result, declaration));
462 return result;
463 }
464
465 @NotNull
466 private JsName localOrImportedName(@NotNull DeclarationDescriptor descriptor, @NotNull String suggestedName) {
467 ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
468 JsName name = module != currentModule ?
469 importDeclaration(suggestedName, getQualifiedReference(descriptor)) :
470 rootFunction.getScope().declareTemporaryName(suggestedName);
471 MetadataProperties.setDescriptor(name, descriptor);
472 return name;
473 }
474
475 private final class InnerNameGenerator extends Generator<JsName> {
476 public InnerNameGenerator() {
477 addRule(new Rule<JsName>() {
478 @Nullable
479 @Override
480 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
481 if (descriptor instanceof FunctionDescriptor) {
482 FunctionDescriptor initialDescriptor = ((FunctionDescriptor) descriptor).getInitialSignatureDescriptor();
483 if (initialDescriptor != null) {
484 return getInnerNameForDescriptor(initialDescriptor);
485 }
486 }
487 if (descriptor instanceof ModuleDescriptor) {
488 return getModuleInnerName(descriptor);
489 }
490 if (descriptor instanceof LocalVariableDescriptor || descriptor instanceof ParameterDescriptor) {
491 return getNameForDescriptor(descriptor);
492 }
493 if (descriptor instanceof ConstructorDescriptor) {
494 if (((ConstructorDescriptor) descriptor).isPrimary()) {
495 return getInnerNameForDescriptor(((ConstructorDescriptor) descriptor).getConstructedClass());
496 }
497 }
498 return localOrImportedName(descriptor, getSuggestedName(descriptor));
499 }
500 });
501 }
502 }
503
504 private final class ObjectInstanceNameGenerator extends Generator<JsName> {
505 public ObjectInstanceNameGenerator() {
506 addRule(new Rule<JsName>() {
507 @Nullable
508 @Override
509 public JsName apply(@NotNull DeclarationDescriptor descriptor) {
510 String suggested = getSuggestedName(descriptor) + Namer.OBJECT_INSTANCE_FUNCTION_SUFFIX;
511 return localOrImportedName(descriptor, suggested);
512 }
513 });
514 }
515 }
516
517 @NotNull
518 public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
519 String suggestedName;
520 if (descriptor instanceof PropertyGetterDescriptor) {
521 PropertyGetterDescriptor getter = (PropertyGetterDescriptor) descriptor;
522 suggestedName = "get_" + getSuggestedName(getter.getCorrespondingProperty());
523 }
524 else if (descriptor instanceof PropertySetterDescriptor) {
525 PropertySetterDescriptor setter = (PropertySetterDescriptor) descriptor;
526 suggestedName = "set_" + getSuggestedName(setter.getCorrespondingProperty());
527 }
528 else if (descriptor instanceof ConstructorDescriptor) {
529 ConstructorDescriptor constructor = (ConstructorDescriptor) descriptor;
530 suggestedName = getSuggestedName(constructor.getContainingDeclaration()) + "_init";
531 descriptor = descriptor.getContainingDeclaration();
532 assert descriptor != null : "ConstructorDescriptor should have containing declaration: " + constructor;
533 }
534 else {
535 if (descriptor.getName().isSpecial()) {
536 if (descriptor instanceof ClassDescriptor) {
537 if (DescriptorUtils.isAnonymousObject(descriptor)) {
538 suggestedName = "ObjectLiteral";
539 }
540 else {
541 suggestedName = "Anonymous";
542 }
543 }
544 else if (descriptor instanceof FunctionDescriptor) {
545 suggestedName = "lambda";
546 }
547 else {
548 suggestedName = "anonymous";
549 }
550 }
551 else {
552 suggestedName = NameSuggestion.sanitizeName(descriptor.getName().asString());
553 }
554 }
555
556 if (!(descriptor instanceof PackageFragmentDescriptor) && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
557 DeclarationDescriptor container = descriptor.getContainingDeclaration();
558 assert container != null : "We just figured out that descriptor is not for a top-level declaration: " + descriptor;
559 suggestedName = getSuggestedName(container) + "$" + NameSuggestion.sanitizeName(suggestedName);
560 }
561
562 return suggestedName;
563 }
564
565 private JsScope getScopeForPackage(FqName fqName) {
566 JsScope scope = packageScopes.get(fqName);
567 if (scope == null) {
568 if (fqName.isRoot()) {
569 scope = new JsRootScope(program);
570 }
571 else {
572 JsScope parentScope = getScopeForPackage(fqName.parent());
573 scope = parentScope.innerObjectScope(fqName.shortName().asString());
574 }
575 packageScopes.put(fqName, scope);
576 }
577 return scope;
578 }
579
580 private final class ScopeGenerator extends Generator<JsScope> {
581
582 public ScopeGenerator() {
583 Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
584 @Override
585 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
586 if (!(descriptor instanceof ClassDescriptor)) {
587 return null;
588 }
589 if (getSuperclass((ClassDescriptor) descriptor) == null) {
590 JsFunction function = new JsFunction(new JsRootScope(program), new JsBlock(), descriptor.toString());
591 scopeToFunction.put(function.getScope(), function);
592 return function.getScope();
593 }
594 return null;
595 }
596 };
597 Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
598 @Override
599 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
600 if (!(descriptor instanceof ClassDescriptor)) {
601 return null;
602 }
603 ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
604 if (superclass == null) {
605 return null;
606 }
607 return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
608 }
609 };
610 Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
611 @Override
612 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
613 return rootFunction.getScope();
614 }
615 };
616 //TODO: never get there
617 Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
618 @Override
619 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
620 return rootFunction.getScope().innerObjectScope("Scope for member " + descriptor.getName());
621 }
622 };
623 Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
624 @Override
625 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
626 if (!(descriptor instanceof CallableDescriptor)) {
627 return null;
628 }
629
630 JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(rootFunction.getScope());
631 assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
632 scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
633 return correspondingFunction.getScope();
634 }
635 };
636 Rule<JsScope> scopeForPackage = new Rule<JsScope>() {
637 @Nullable
638 @Override
639 public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
640 if (!(descriptor instanceof PackageFragmentDescriptor)) return null;
641
642 PackageFragmentDescriptor packageDescriptor = (PackageFragmentDescriptor) descriptor;
643 return getScopeForPackage(packageDescriptor.getFqName());
644 }
645 };
646 addRule(scopeForPackage);
647 addRule(createFunctionObjectsForCallableDescriptors);
648 addRule(generateNewScopesForClassesWithNoAncestors);
649 addRule(generateInnerScopesForDerivedClasses);
650 addRule(generateNewScopesForPackageDescriptors);
651 addRule(generateInnerScopesForMembers);
652 }
653 }
654
655 @Nullable
656 public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
657 JsName name = getModuleInnerName(descriptor);
658 return name != null ? JsAstUtils.pureFqn(name, null) : null;
659 }
660
661 @Nullable
662 private JsName getModuleInnerName(@NotNull DeclarationDescriptor descriptor) {
663 ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
664 if (currentModule == module) {
665 return rootScope.declareName(Namer.getRootPackageName());
666 }
667 String moduleName;
668 if (module == module.getBuiltIns().getBuiltInsModule()) {
669 moduleName = Namer.KOTLIN_LOWER_NAME;
670 }
671 else {
672 moduleName = module.getName().asString();
673 moduleName = moduleName.substring(1, moduleName.length() - 1);
674 }
675
676 if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null;
677
678 return getImportedModule(moduleName, null).getInternalName();
679 }
680
681 @NotNull
682 private ImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) {
683 JsName plainName = descriptor != null && config.getModuleKind() == ModuleKind.UMD ?
684 rootScope.declareName(getPlainId(descriptor)) : null;
685 ImportedModuleKey key = new ImportedModuleKey(baseName, plainName);
686
687 ImportedModule module = importedModules.get(key);
688 if (module == null) {
689 JsName internalName = rootScope.declareTemporaryName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName));
690 module = new ImportedModule(baseName, internalName, plainName != null ? pureFqn(plainName, null) : null);
691 importedModules.put(key, module);
692 }
693 return module;
694 }
695
696 @NotNull
697 private String getPlainId(@NotNull DeclarationDescriptor declaration) {
698 SuggestedName suggestedName = nameSuggestion.suggest(declaration);
699 assert suggestedName != null : "Declaration should not be ModuleDescriptor, therefore suggestedName should be non-null";
700 return suggestedName.getNames().get(0);
701 }
702
703 private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
704 if (descriptor instanceof FunctionDescriptor ||
705 descriptor instanceof PackageFragmentDescriptor ||
706 descriptor instanceof ClassDescriptor
707 ) {
708 MetadataProperties.setSideEffects(expression, SideEffectKind.PURE);
709 }
710 return expression;
711 }
712
713 public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
714 classOrConstructorClosure.put(localClass, Lists.newArrayList(closure));
715 }
716
717 @Nullable
718 public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) {
719 List<DeclarationDescriptor> result = classOrConstructorClosure.get(descriptor);
720 return result != null ? Lists.newArrayList(result) : null;
721 }
722
723 @NotNull
724 public Map<ClassDescriptor, List<DeferredCallSite>> getDeferredCallSites() {
725 return deferredCallSites;
726 }
727
728 @NotNull
729 public JsFunction getRootFunction() {
730 return rootFunction;
731 }
732
733 @NotNull
734 public List<JsStatement> getTopLevelStatements() {
735 return topLevelStatements;
736 }
737
738 @NotNull
739 public List<JsStatement> getDeclarationStatements() {
740 return declarationStatements;
741 }
742
743 public void addClass(@NotNull ClassDescriptor classDescriptor) {
744 classes.add(classDescriptor);
745 }
746
747 public void export(@NotNull MemberDescriptor descriptor, boolean force) {
748 exporter.export(descriptor, force);
749 }
750
751 @NotNull
752 public NameSuggestion getNameSuggestion() {
753 return nameSuggestion;
754 }
755
756 @NotNull
757 public ModuleDescriptor getCurrentModule() {
758 return currentModule;
759 }
760
761 public void postProcess() {
762 addInterfaceDefaultMethods();
763 rootFunction.getBody().getStatements().addAll(importStatements);
764 addClassPrototypes();
765 rootFunction.getBody().getStatements().addAll(declarationStatements);
766 rootFunction.getBody().getStatements().addAll(exporter.getStatements());
767 rootFunction.getBody().getStatements().addAll(topLevelStatements);
768 }
769
770 private void addClassPrototypes() {
771 Set<ClassDescriptor> visited = new HashSet<ClassDescriptor>();
772 for (ClassDescriptor cls : classes) {
773 addClassPrototypes(cls, visited);
774 }
775 }
776
777 private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set<ClassDescriptor> visited) {
778 if (!visited.add(cls)) return;
779 if (DescriptorUtilsKt.getModule(cls) != currentModule) return;
780 if (isNativeObject(cls) || isLibraryObject(cls)) return;
781
782 ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassNotAny(cls);
783 if (superclass != null) {
784 addClassPrototypes(superclass, visited);
785
786 List<JsStatement> statements = rootFunction.getBody().getStatements();
787
788 JsNameRef superclassRef;
789 if (isNativeObject(superclass) || isLibraryObject(superclass)) {
790 superclassRef = getQualifiedReference(superclass);
791 }
792 else {
793 superclassRef = getInnerNameForDescriptor(superclass).makeRef();
794 }
795
796 JsExpression superPrototype = JsAstUtils.prototypeOf(superclassRef);
797 JsExpression superPrototypeInstance = new JsInvocation(new JsNameRef("create", "Object"), superPrototype);
798 JsExpression classRef = new JsNameRef(getInnerNameForDescriptor(cls));
799 JsExpression prototype = JsAstUtils.prototypeOf(classRef);
800 statements.add(JsAstUtils.assignment(prototype, superPrototypeInstance).makeStmt());
801
802 JsExpression constructorRef = new JsNameRef("constructor", prototype.deepCopy());
803 statements.add(JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt());
804 }
805 }
806
807 private void addInterfaceDefaultMethods() {
808 new InterfaceFunctionCopier(this).copyInterfaceFunctions(classes);
809 }
810
811 public boolean isBuiltinModule() {
812 for (ClassDescriptor cls : classes) {
813 FqNameUnsafe fqn = DescriptorUtils.getFqName(cls);
814 if ("kotlin.Enum".equals(fqn.asString())) {
815 return true;
816 }
817 }
818 return false;
819 }
820
821 public static class ImportedModule {
822 @NotNull
823 private final String externalName;
824
825 @NotNull
826 private final JsName internalName;
827
828 @Nullable
829 private final JsExpression plainReference;
830
831 ImportedModule(@NotNull String externalName, @NotNull JsName internalName, @Nullable JsExpression plainReference) {
832 this.externalName = externalName;
833 this.internalName = internalName;
834 this.plainReference = plainReference;
835 }
836
837 @NotNull
838 public String getExternalName() {
839 return externalName;
840 }
841
842 @NotNull
843 public JsName getInternalName() {
844 return internalName;
845 }
846
847 @Nullable
848 public JsExpression getPlainReference() {
849 return plainReference;
850 }
851 }
852
853 private static class ImportedModuleKey {
854 @NotNull
855 private final String baseName;
856
857 @Nullable
858 private final JsName plainName;
859
860 public ImportedModuleKey(@NotNull String baseName, @Nullable JsName plainName) {
861 this.baseName = baseName;
862 this.plainName = plainName;
863 }
864
865 @Override
866 public boolean equals(Object o) {
867 if (this == o) return true;
868 if (o == null || getClass() != o.getClass()) return false;
869
870 ImportedModuleKey key = (ImportedModuleKey) o;
871
872 if (!baseName.equals(key.baseName)) return false;
873 if (plainName != null ? !plainName.equals(key.plainName) : key.plainName != null) return false;
874
875 return true;
876 }
877
878 @Override
879 public int hashCode() {
880 int result = baseName.hashCode();
881 result = 31 * result + (plainName != null ? plainName.hashCode() : 0);
882 return result;
883 }
884 }
885 }