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.google.dart.compiler.backend.js.ast.*;
022    import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata;
023    import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
024    import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind;
025    import com.intellij.openapi.util.Factory;
026    import com.intellij.util.containers.ContainerUtil;
027    import com.intellij.util.containers.hash.LinkedHashMap;
028    import org.jetbrains.annotations.NotNull;
029    import org.jetbrains.annotations.Nullable;
030    import org.jetbrains.kotlin.builtins.ReflectionTypes;
031    import org.jetbrains.kotlin.descriptors.*;
032    import org.jetbrains.kotlin.js.config.JsConfig;
033    import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
034    import org.jetbrains.kotlin.js.translate.context.generator.Generator;
035    import org.jetbrains.kotlin.js.translate.context.generator.Rule;
036    import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
037    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
038    import org.jetbrains.kotlin.name.FqName;
039    import org.jetbrains.kotlin.resolve.BindingContext;
040    import org.jetbrains.kotlin.resolve.BindingTrace;
041    import org.jetbrains.kotlin.resolve.DescriptorUtils;
042    import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
043    
044    import java.util.Collections;
045    import java.util.HashMap;
046    import java.util.List;
047    import java.util.Map;
048    
049    import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.*;
050    import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
051    import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*;
052    import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName;
053    import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName;
054    import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
055    import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic;
056    
057    /**
058     * Aggregates all the static parts of the context.
059     */
060    public final class StaticContext {
061    
062        public static StaticContext generateStaticContext(@NotNull BindingTrace bindingTrace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor) {
063            JsProgram program = new JsProgram("main");
064            Namer namer = Namer.newInstance(program.getRootScope());
065            Intrinsics intrinsics = new Intrinsics();
066            StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
067            return new StaticContext(program, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config, moduleDescriptor);
068        }
069    
070        @NotNull
071        private final JsProgram program;
072    
073        @NotNull
074        private final BindingTrace bindingTrace;
075        @NotNull
076        private final Namer namer;
077    
078        @NotNull
079        private final Intrinsics intrinsics;
080    
081        @NotNull
082        private final StandardClasses standardClasses;
083    
084        @NotNull
085        private final ReflectionTypes reflectionTypes;
086    
087        @NotNull
088        private final JsScope rootScope;
089    
090        @NotNull
091        private final Generator<JsName> names = new NameGenerator();
092        @NotNull
093        private final Map<FqName, JsName> packageNames = Maps.newHashMap();
094        @NotNull
095        private final Generator<JsScope> scopes = new ScopeGenerator();
096        @NotNull
097        private final Generator<JsExpression> qualifiers = new QualifierGenerator();
098        @NotNull
099        private final Generator<Boolean> qualifierIsNull = new QualifierIsNullGenerator();
100    
101        @NotNull
102        private final Map<JsScope, JsFunction> scopeToFunction = Maps.newHashMap();
103    
104        @NotNull
105        private final Map<MemberDescriptor, List<DeclarationDescriptor>> classOrConstructorClosure = Maps.newHashMap();
106    
107        @NotNull
108        private final Map<ClassDescriptor, List<DeferredCallSite>> deferredCallSites = new HashMap<ClassDescriptor, List<DeferredCallSite>>();
109    
110        @NotNull
111        private final JsConfig config;
112    
113        @NotNull
114        private final Map<String, JsName> importedModules = new LinkedHashMap<String, JsName>();
115    
116        private Map<String, JsName> readOnlyImportedModules;
117    
118        //TODO: too many parameters in constructor
119        private StaticContext(
120                @NotNull JsProgram program,
121                @NotNull BindingTrace bindingTrace,
122                @NotNull Namer namer,
123                @NotNull Intrinsics intrinsics,
124                @NotNull StandardClasses standardClasses,
125                @NotNull JsScope rootScope,
126                @NotNull JsConfig config,
127                @NotNull ModuleDescriptor moduleDescriptor
128        ) {
129            this.program = program;
130            this.bindingTrace = bindingTrace;
131            this.namer = namer;
132            this.intrinsics = intrinsics;
133            this.rootScope = rootScope;
134            this.standardClasses = standardClasses;
135            this.config = config;
136            this.reflectionTypes = new ReflectionTypes(moduleDescriptor);
137        }
138    
139        @NotNull
140        public JsProgram getProgram() {
141            return program;
142        }
143    
144        @NotNull
145        public BindingTrace getBindingTrace() {
146            return bindingTrace;
147        }
148    
149        @NotNull
150        public BindingContext getBindingContext() {
151            return bindingTrace.getBindingContext();
152        }
153    
154        @NotNull
155        public Intrinsics getIntrinsics() {
156            return intrinsics;
157        }
158    
159        @NotNull
160        public Namer getNamer() {
161            return namer;
162        }
163    
164        @NotNull
165        public ReflectionTypes getReflectionTypes() {
166            return reflectionTypes;
167        }
168    
169        @NotNull
170        private JsScope getRootScope() {
171            return rootScope;
172        }
173    
174        @NotNull
175        public Map<String, JsName> getImportedModules() {
176            if (readOnlyImportedModules == null) {
177                readOnlyImportedModules = Collections.unmodifiableMap(importedModules);
178            }
179            return readOnlyImportedModules;
180        }
181    
182        @NotNull
183        public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
184            JsScope scope = scopes.get(descriptor.getOriginal());
185            assert scope != null : "Must have a scope for descriptor";
186            return scope;
187        }
188    
189        @NotNull
190        public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
191            JsScope scope = getScopeForDescriptor(descriptor);
192            JsFunction function = scopeToFunction.get(scope);
193            assert scope.equals(function.getScope()) : "Inconsistency.";
194            return function;
195        }
196    
197        @NotNull
198        public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
199            if (descriptor instanceof PackageViewDescriptor) {
200                return getQualifiedReference(((PackageViewDescriptor) descriptor).getFqName());
201            }
202            if (descriptor instanceof PackageFragmentDescriptor) {
203                return getQualifiedReference(((PackageFragmentDescriptor) descriptor).getFqName());
204            }
205    
206            JsNameRef result = new JsNameRef(getNameForDescriptor(descriptor), getQualifierForDescriptor(descriptor));
207            applySideEffects(result, descriptor);
208            return result;
209        }
210    
211        @NotNull
212        public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
213            JsName packageName = getNameForPackage(packageFqName);
214            return pureFqn(packageName, packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
215        }
216    
217        @NotNull
218        public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
219            JsName name = names.get(descriptor.getOriginal());
220            assert name != null : "Must have name for descriptor";
221            return name;
222        }
223    
224        @NotNull
225        public JsName getNameForPackage(@NotNull final FqName packageFqName) {
226            return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory<JsName>() {
227                @Override
228                public JsName create() {
229                    String name = Namer.generatePackageName(packageFqName);
230                    return getRootScope().declareName(name);
231                }
232            });
233        }
234    
235        @NotNull
236        private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
237            JsNameRef result = null;
238            JsNameRef qualifier = null;
239    
240            FqName fqName = packageFqName;
241    
242            while (true) {
243                JsNameRef ref = pureFqn(getNameForPackage(fqName), null);
244    
245                if (qualifier == null) {
246                    result = ref;
247                }
248                else {
249                    qualifier.setQualifier(ref);
250                }
251    
252                qualifier = ref;
253    
254                if (fqName.isRoot()) break;
255                fqName = fqName.parent();
256            }
257    
258            return result;
259        }
260    
261        @NotNull
262        public JsConfig getConfig() {
263            return config;
264        }
265    
266        private final class NameGenerator extends Generator<JsName> {
267    
268            public NameGenerator() {
269                Rule<JsName> namesForDynamic = new Rule<JsName>() {
270                    @Override
271                    @Nullable
272                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
273                        if (isDynamic(descriptor)) {
274                            String name = descriptor.getName().asString();
275                            return JsDynamicScope.INSTANCE.declareName(name);
276                        }
277    
278                        return null;
279                    }
280                };
281    
282                Rule<JsName> localClasses = new Rule<JsName>() {
283                    @Nullable
284                    @Override
285                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
286                        if (!DescriptorUtils.isDescriptorWithLocalVisibility(descriptor) ||
287                            !DescriptorUtils.isClass(descriptor)) {
288                            return null;
289                        }
290    
291                        String suggested = getSuggestedName(descriptor);
292    
293                        descriptor = getParentOfType(descriptor, ClassOrPackageFragmentDescriptor.class, true);
294                        assert descriptor != null;
295    
296                        JsScope scope = getScopeForDescriptor(descriptor);
297                        return scope.declareFreshName(suggested);
298                    }
299                };
300    
301                Rule<JsName> namesForStandardClasses = new Rule<JsName>() {
302                    @Override
303                    @Nullable
304                    public JsName apply(@NotNull DeclarationDescriptor data) {
305                        if (!standardClasses.isStandardObject(data)) {
306                            return null;
307                        }
308                        return standardClasses.getStandardObjectName(data);
309                    }
310                };
311                Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() {
312                    @Override
313                    @Nullable
314                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
315                        JsScope scope = getEnclosingScope(descriptor);
316                        return scope.declareFreshName(getSuggestedName(descriptor));
317                    }
318                };
319                Rule<JsName> constructorOrNativeCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() {
320                    @Override
321                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
322                        if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() ||
323                            DescriptorUtils.isCompanionObject(descriptor) && isNativeObject(descriptor)) {
324                            //noinspection ConstantConditions
325                            return getNameForDescriptor(descriptor.getContainingDeclaration());
326                        }
327                        return null;
328                    }
329                };
330    
331                // ecma 5 property name never declares as obfuscatable:
332                // 1) property cannot be overloaded, so, name collision is not possible
333                // 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
334                //
335                // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
336                Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() {
337                    @Override
338                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
339                        PropertyDescriptor propertyDescriptor;
340                        if (descriptor instanceof PropertyAccessorDescriptor) {
341                            propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
342                        }
343                        else if (descriptor instanceof PropertyDescriptor) {
344                            propertyDescriptor = (PropertyDescriptor) descriptor;
345                        }
346                        else {
347                            return null;
348                        }
349    
350                        String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor);
351                        if (nameFromAnnotation != null) {
352                            return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
353                        }
354    
355                        String propertyName = getSuggestedName(propertyDescriptor);
356    
357                        if (!isExtension(propertyDescriptor)) {
358                            if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) {
359                                propertyName = getMangledName(propertyDescriptor, propertyName);
360                            }
361                            return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
362                        } else {
363                            assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor;
364    
365                            boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
366                            String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false);
367                            return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
368                        }
369                    }
370                };
371    
372                Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() {
373                    @Override
374                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
375                        // The mixing of override and rename by annotation(e.g. native) is forbidden.
376                        if (descriptor instanceof CallableMemberDescriptor &&
377                            !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) {
378                            return null;
379                        }
380    
381                        if (descriptor instanceof ConstructorDescriptor) {
382                            DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration();
383                            assert classDescriptor != null;
384                            descriptor = classDescriptor;
385                        }
386    
387                        String name = getNameForAnnotatedObjectWithOverrides(descriptor);
388                        if (name != null) return getEnclosingScope(descriptor).declareName(name);
389                        return null;
390                    }
391                };
392    
393                Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() {
394                    @Override
395                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
396                        //TODO: refactor
397                        if (!(descriptor instanceof FunctionDescriptor)) {
398                            return null;
399                        }
400                        FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
401                        if (overriddenDescriptor == null) {
402                            return null;
403                        }
404    
405                        JsScope scope = getEnclosingScope(descriptor);
406                        JsName result = getNameForDescriptor(overriddenDescriptor);
407                        scope.declareName(result.getIdent());
408                        return result;
409                    }
410                };
411    
412                Rule<JsName> fakeCallableDescriptor = new Rule<JsName>() {
413                    @Nullable
414                    @Override
415                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
416                        if (!(descriptor instanceof FakeCallableDescriptorForObject)) {
417                            return null;
418                        }
419    
420                        FakeCallableDescriptorForObject fakeCallableDescriptor = (FakeCallableDescriptorForObject) descriptor;
421                        return getNameForDescriptor(fakeCallableDescriptor.getReferencedDescriptor());
422                    }
423                };
424    
425                addRule(namesForDynamic);
426                addRule(localClasses);
427                addRule(namesForStandardClasses);
428                addRule(constructorOrNativeCompanionObjectHasTheSameNameAsTheClass);
429                addRule(propertyOrPropertyAccessor);
430                addRule(predefinedObjectsHasUnobfuscatableNames);
431                addRule(overridingDescriptorsReferToOriginalName);
432                addRule(fakeCallableDescriptor);
433                addRule(memberDeclarationsInsideParentsScope);
434            }
435        }
436    
437        @NotNull
438        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
439            JsScope scope = getEnclosingScope(descriptor);
440            return fresh ? scope.declareFreshName(name) : scope.declareName(name);
441        }
442    
443        @NotNull
444        private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
445            DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
446            return getScopeForDescriptor(containingDeclaration.getOriginal());
447        }
448    
449        private final class ScopeGenerator extends Generator<JsScope> {
450    
451            public ScopeGenerator() {
452                Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
453                    @Override
454                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
455                        if (!(descriptor instanceof ClassDescriptor)) {
456                            return null;
457                        }
458                        if (getSuperclass((ClassDescriptor) descriptor) == null) {
459                            return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
460                        }
461                        return null;
462                    }
463                };
464                Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
465                    @Override
466                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
467                        if (!(descriptor instanceof ClassDescriptor)) {
468                            return null;
469                        }
470                        ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
471                        if (superclass == null) {
472                            return null;
473                        }
474                        return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
475                    }
476                };
477                Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
478                    @Override
479                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
480                        if (!(descriptor instanceof PackageFragmentDescriptor)) {
481                            return null;
482                        }
483                        return getRootScope().innerObjectScope("Package " + descriptor.getName());
484                    }
485                };
486                //TODO: never get there
487                Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
488                    @Override
489                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
490                        JsScope enclosingScope = getEnclosingScope(descriptor);
491                        return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
492                    }
493                };
494                Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
495                    @Override
496                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
497                        if (!(descriptor instanceof CallableDescriptor)) {
498                            return null;
499                        }
500                        JsScope enclosingScope = getEnclosingScope(descriptor);
501    
502                        JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
503                        assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
504                        scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
505                        return correspondingFunction.getScope();
506                    }
507                };
508                addRule(createFunctionObjectsForCallableDescriptors);
509                addRule(generateNewScopesForClassesWithNoAncestors);
510                addRule(generateInnerScopesForDerivedClasses);
511                addRule(generateNewScopesForPackageDescriptors);
512                addRule(generateInnerScopesForMembers);
513            }
514        }
515    
516        @Nullable
517        public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
518            if (qualifierIsNull.get(descriptor.getOriginal()) != null) {
519                return null;
520            }
521            return qualifiers.get(descriptor.getOriginal());
522        }
523    
524        private final class QualifierGenerator extends Generator<JsExpression> {
525            public QualifierGenerator() {
526                Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
527                    @Override
528                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
529                        if (!standardClasses.isStandardObject(descriptor)) {
530                            return null;
531                        }
532                        return Namer.kotlinObject();
533                    }
534                };
535                //TODO: review and refactor
536                Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() {
537                    @Override
538                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
539                        if (isNativeObject(descriptor)) return null;
540    
541                        DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
542                        if (!(containingDescriptor instanceof PackageFragmentDescriptor)) {
543                            return null;
544                        }
545    
546                        JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName());
547    
548                        JsExpression moduleExpression = getModuleExpressionFor(descriptor);
549                        return moduleExpression != null ? JsAstUtils.replaceRootReference(result, moduleExpression) : result;
550                    }
551                };
552                Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() {
553                    @Override
554                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
555                        if (descriptor instanceof ConstructorDescriptor ||
556                            isNativeObject(descriptor) && DescriptorUtils.isCompanionObject(descriptor)) {
557                            //noinspection ConstantConditions
558                            return getQualifierForDescriptor(descriptor.getContainingDeclaration());
559                        }
560                        return null;
561                    }
562                };
563                Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
564                    @Override
565                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
566                        if (isLibraryObject(descriptor)) {
567                            return Namer.kotlinObject();
568                        }
569                        return null;
570                    }
571                };
572                Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() {
573                    @Override
574                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
575                        if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null;
576    
577                        DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
578                        if (containingDeclaration != null && isNativeObject(containingDeclaration)) {
579                            return isCompanionObject(descriptor) ? getQualifierForDescriptor(containingDeclaration) :
580                                getQualifiedReference(containingDeclaration);
581                        }
582    
583                        return null;
584                    }
585                };
586                Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() {
587                    @Override
588                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
589                        if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
590                            CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
591                            if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
592                                return getQualifiedReference(callableDescriptor.getContainingDeclaration());
593                            }
594                        }
595    
596                        return null;
597                    }
598                };
599                Rule<JsExpression> nestedClassesHaveContainerQualifier = new Rule<JsExpression>() {
600                    @Nullable
601                    @Override
602                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
603                        if (!(descriptor instanceof ClassDescriptor)) {
604                            return null;
605                        }
606                        DeclarationDescriptor container = getParentOfType(descriptor, ClassDescriptor.class);
607                        if (container == null) {
608                            return null;
609                        }
610    
611                        if (isNativeObject(descriptor)) {
612                            return null;
613                        }
614                        return getQualifiedReference(container);
615                    }
616                };
617    
618                Rule<JsExpression> localClassesHavePackageQualifier = new Rule<JsExpression>() {
619                    @Nullable
620                    @Override
621                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
622                        if (!DescriptorUtils.isDescriptorWithLocalVisibility(descriptor) || !(descriptor instanceof ClassDescriptor)) {
623                            return null;
624                        }
625    
626                        descriptor = getParentOfType(descriptor, PackageFragmentDescriptor.class, true);
627                        assert descriptor != null;
628                        return getQualifiedReference(descriptor);
629                    }
630                };
631    
632                addRule(libraryObjectsHaveKotlinQualifier);
633                addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass);
634                addRule(standardObjectsHaveKotlinQualifier);
635                addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
636                addRule(nativeObjectsHaveNativePartOfFullQualifier);
637                addRule(staticMembersHaveContainerQualifier);
638                addRule(nestedClassesHaveContainerQualifier);
639                addRule(localClassesHavePackageQualifier);
640            }
641        }
642    
643        @Nullable
644        public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
645            String moduleName = getExternalModuleName(descriptor);
646            if (moduleName == null) return null;
647    
648            if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null;
649    
650            JsName moduleId = moduleName.equals(Namer.KOTLIN_LOWER_NAME) ? rootScope.declareName(Namer.KOTLIN_NAME) :
651                              importedModules.get(moduleName);
652            if (moduleId == null) {
653                moduleId = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(moduleName));
654                importedModules.put(moduleName, moduleId);
655            }
656    
657            return JsAstUtils.pureFqn(moduleId, null);
658        }
659    
660        private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
661            if (expression instanceof HasMetadata) {
662                if (descriptor instanceof FunctionDescriptor ||
663                    descriptor instanceof PackageFragmentDescriptor ||
664                    descriptor instanceof ClassDescriptor
665                ) {
666                    MetadataProperties.setSideEffects((HasMetadata) expression, SideEffectKind.PURE);
667                }
668            }
669            return expression;
670        }
671    
672        private static class QualifierIsNullGenerator extends Generator<Boolean> {
673    
674            private QualifierIsNullGenerator() {
675                Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() {
676                    @Override
677                    public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
678                        if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
679                            return true;
680                        }
681                        return null;
682                    }
683                };
684                addRule(propertiesInClassHaveNoQualifiers);
685            }
686        }
687    
688        public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List<DeclarationDescriptor> closure) {
689            classOrConstructorClosure.put(localClass, Lists.newArrayList(closure));
690        }
691    
692        @Nullable
693        public List<DeclarationDescriptor> getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) {
694            List<DeclarationDescriptor> result = classOrConstructorClosure.get(descriptor);
695            return result != null ? Lists.newArrayList(result) : null;
696        }
697    
698        @NotNull
699        public Map<ClassDescriptor, List<DeferredCallSite>> getDeferredCallSites() {
700            return deferredCallSites;
701        }
702    }