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