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            for (FqName pathElement : ContainerUtil.reverse(packageFqName.path())) {
212                JsNameRef ref = getNameForPackage(pathElement).makeRef();
213    
214                if (qualifier == null) {
215                    result = ref;
216                }
217                else {
218                    qualifier.setQualifier(ref);
219                }
220    
221                qualifier = ref;
222            }
223    
224            assert result != null : "didn't iterate: " + packageFqName;
225            return result;
226        }
227    
228        @NotNull
229        public Config getConfig() {
230            return config;
231        }
232    
233        private final class NameGenerator extends Generator<JsName> {
234    
235            public NameGenerator() {
236                Rule<JsName> namesForDynamic = new Rule<JsName>() {
237                    @Override
238                    @Nullable
239                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
240                        if (isDynamic(descriptor)) {
241                            String name = descriptor.getName().asString();
242                            return JsDynamicScope.INSTANCE$.declareName(name);
243                        }
244    
245                        return null;
246                    }
247                };
248    
249                Rule<JsName> namesForStandardClasses = new Rule<JsName>() {
250                    @Override
251                    @Nullable
252                    public JsName apply(@NotNull DeclarationDescriptor data) {
253                        if (!standardClasses.isStandardObject(data)) {
254                            return null;
255                        }
256                        return standardClasses.getStandardObjectName(data);
257                    }
258                };
259                Rule<JsName> memberDeclarationsInsideParentsScope = new Rule<JsName>() {
260                    @Override
261                    @Nullable
262                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
263                        JsScope scope = getEnclosingScope(descriptor);
264                        return scope.declareFreshName(getSuggestedName(descriptor));
265                    }
266                };
267                Rule<JsName> constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() {
268                    @Override
269                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
270                        if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() ||
271                            DescriptorUtils.isCompanionObject(descriptor)
272                        ) {
273                            //noinspection ConstantConditions
274                            return getNameForDescriptor(descriptor.getContainingDeclaration());
275                        }
276                        return null;
277                    }
278                };
279    
280                // ecma 5 property name never declares as obfuscatable:
281                // 1) property cannot be overloaded, so, name collision is not possible
282                // 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
283                //
284                // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
285                Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() {
286                    @Override
287                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
288                        PropertyDescriptor propertyDescriptor;
289                        if (descriptor instanceof PropertyAccessorDescriptor) {
290                            propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
291                        }
292                        else if (descriptor instanceof PropertyDescriptor) {
293                            propertyDescriptor = (PropertyDescriptor) descriptor;
294                        }
295                        else {
296                            return null;
297                        }
298    
299                        String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor);
300                        if (nameFromAnnotation != null) {
301                            return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
302                        }
303    
304                        String propertyName = getSuggestedName(propertyDescriptor);
305    
306                        if (!isExtension(propertyDescriptor)) {
307                            if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) {
308                                propertyName = getMangledName(propertyDescriptor, propertyName);
309                            }
310                            return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
311                        } else {
312                            assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor;
313    
314                            boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
315                            String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false);
316                            return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
317                        }
318                    }
319                };
320    
321                Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() {
322                    @Override
323                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
324                        // The mixing of override and rename by annotation(e.g. native) is forbidden.
325                        if (descriptor instanceof CallableMemberDescriptor &&
326                            !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) {
327                            return null;
328                        }
329    
330                        if (descriptor instanceof ConstructorDescriptor) {
331                            DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration();
332                            assert classDescriptor != null;
333                            descriptor = classDescriptor;
334                        }
335    
336                        String name = getNameForAnnotatedObjectWithOverrides(descriptor);
337                        if (name != null) return getEnclosingScope(descriptor).declareName(name);
338                        return null;
339                    }
340                };
341    
342                Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() {
343                    @Override
344                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
345                        //TODO: refactor
346                        if (!(descriptor instanceof FunctionDescriptor)) {
347                            return null;
348                        }
349                        FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
350                        if (overriddenDescriptor == null) {
351                            return null;
352                        }
353    
354                        JsScope scope = getEnclosingScope(descriptor);
355                        JsName result = getNameForDescriptor(overriddenDescriptor);
356                        scope.declareName(result.getIdent());
357                        return result;
358                    }
359                };
360    
361                addRule(namesForDynamic);
362                addRule(namesForStandardClasses);
363                addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass);
364                addRule(propertyOrPropertyAccessor);
365                addRule(predefinedObjectsHasUnobfuscatableNames);
366                addRule(overridingDescriptorsReferToOriginalName);
367                addRule(memberDeclarationsInsideParentsScope);
368            }
369        }
370    
371        @NotNull
372        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
373            JsScope scope = getEnclosingScope(descriptor);
374            return fresh ? scope.declareFreshName(name) : scope.declareName(name);
375        }
376    
377        @NotNull
378        private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
379            DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
380            return getScopeForDescriptor(containingDeclaration.getOriginal());
381        }
382    
383        private final class ScopeGenerator extends Generator<JsScope> {
384    
385            public ScopeGenerator() {
386                Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
387                    @Override
388                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
389                        if (!(descriptor instanceof ClassDescriptor)) {
390                            return null;
391                        }
392                        if (getSuperclass((ClassDescriptor) descriptor) == null) {
393                            return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
394                        }
395                        return null;
396                    }
397                };
398                Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
399                    @Override
400                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
401                        if (!(descriptor instanceof ClassDescriptor)) {
402                            return null;
403                        }
404                        ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
405                        if (superclass == null) {
406                            return null;
407                        }
408                        return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
409                    }
410                };
411                Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
412                    @Override
413                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
414                        if (!(descriptor instanceof PackageFragmentDescriptor)) {
415                            return null;
416                        }
417                        return getRootScope().innerObjectScope("Package " + descriptor.getName());
418                    }
419                };
420                //TODO: never get there
421                Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
422                    @Override
423                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
424                        JsScope enclosingScope = getEnclosingScope(descriptor);
425                        return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
426                    }
427                };
428                Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
429                    @Override
430                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
431                        if (!(descriptor instanceof CallableDescriptor)) {
432                            return null;
433                        }
434                        JsScope enclosingScope = getEnclosingScope(descriptor);
435    
436                        JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
437                        assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
438                        scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
439                        return correspondingFunction.getScope();
440                    }
441                };
442                addRule(createFunctionObjectsForCallableDescriptors);
443                addRule(generateNewScopesForClassesWithNoAncestors);
444                addRule(generateInnerScopesForDerivedClasses);
445                addRule(generateNewScopesForPackageDescriptors);
446                addRule(generateInnerScopesForMembers);
447            }
448        }
449    
450        @Nullable
451        public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
452            if (qualifierIsNull.get(descriptor.getOriginal()) != null) {
453                return null;
454            }
455            return qualifiers.get(descriptor.getOriginal());
456        }
457    
458        private final class QualifierGenerator extends Generator<JsExpression> {
459            public QualifierGenerator() {
460                Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
461                    @Override
462                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
463                        if (!standardClasses.isStandardObject(descriptor)) {
464                            return null;
465                        }
466                        return namer.kotlinObject();
467                    }
468                };
469                //TODO: review and refactor
470                Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() {
471                    @Override
472                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
473                        if (isNativeObject(descriptor)) return null;
474    
475                        DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
476                        if (!(containingDescriptor instanceof PackageFragmentDescriptor)) {
477                            return null;
478                        }
479    
480                        JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName());
481    
482                        String moduleName = getExternalModuleName(descriptor);
483                        if (moduleName == null) {
484                            return result;
485                        }
486    
487                        if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) {
488                            return null;
489                        }
490    
491                        return JsAstUtils.replaceRootReference(
492                                result, namer.getModuleReference(program.getStringLiteral(moduleName)));
493                    }
494                };
495                Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() {
496                    @Override
497                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
498                        if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) {
499                            //noinspection ConstantConditions
500                            return getQualifierForDescriptor(descriptor.getContainingDeclaration());
501                        }
502                        return null;
503                    }
504                };
505                Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
506                    @Override
507                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
508                        if (isLibraryObject(descriptor)) {
509                            return namer.kotlinObject();
510                        }
511                        return null;
512                    }
513                };
514                Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() {
515                    @Override
516                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
517                        if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null;
518    
519                        DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
520                        if (containingDeclaration != null && isNativeObject(containingDeclaration)) {
521                            return getQualifiedReference(containingDeclaration);
522                        }
523    
524                        return null;
525                    }
526                };
527                Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() {
528                    @Override
529                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
530                        if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
531                            CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
532                            if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
533                                return getQualifiedReference(callableDescriptor.getContainingDeclaration());
534                            }
535                        }
536    
537                        return null;
538                    }
539                };
540    
541                addRule(libraryObjectsHaveKotlinQualifier);
542                addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass);
543                addRule(standardObjectsHaveKotlinQualifier);
544                addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
545                addRule(nativeObjectsHaveNativePartOfFullQualifier);
546                addRule(staticMembersHaveContainerQualifier);
547            }
548        }
549    
550        private static class QualifierIsNullGenerator extends Generator<Boolean> {
551    
552            private QualifierIsNullGenerator() {
553                Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() {
554                    @Override
555                    public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
556                        if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
557                            return true;
558                        }
559                        return null;
560                    }
561                };
562                addRule(propertiesInClassHaveNoQualifiers);
563            }
564        }
565    }