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