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    }