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