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.descriptors.*;
026    import org.jetbrains.kotlin.js.config.Config;
027    import org.jetbrains.kotlin.js.config.EcmaVersion;
028    import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
029    import org.jetbrains.kotlin.js.translate.context.generator.Generator;
030    import org.jetbrains.kotlin.js.translate.context.generator.Rule;
031    import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
032    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
033    import org.jetbrains.kotlin.name.FqName;
034    import org.jetbrains.kotlin.resolve.BindingContext;
035    import org.jetbrains.kotlin.resolve.BindingTrace;
036    import org.jetbrains.kotlin.resolve.DescriptorUtils;
037    import org.jetbrains.kotlin.resolve.calls.tasks.TasksPackage;
038    import org.jetbrains.kotlin.types.reflect.ReflectionTypes;
039    
040    import java.util.Map;
041    
042    import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.*;
043    import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*;
044    import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName;
045    import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName;
046    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension;
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 (TasksPackage.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 || (DescriptorUtils.isCompanionObject(descriptor))) {
271                            //noinspection ConstantConditions
272                            return getNameForDescriptor(descriptor.getContainingDeclaration());
273                        }
274                        return null;
275                    }
276                };
277    
278                // ecma 5 property name never declares as obfuscatable:
279                // 1) property cannot be overloaded, so, name collision is not possible
280                // 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
281                //
282                // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
283                Rule<JsName> propertyOrPropertyAccessor = new Rule<JsName>() {
284                    @Override
285                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
286                        PropertyDescriptor propertyDescriptor;
287                        if (descriptor instanceof PropertyAccessorDescriptor) {
288                            propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
289                        }
290                        else if (descriptor instanceof PropertyDescriptor) {
291                            propertyDescriptor = (PropertyDescriptor) descriptor;
292                        }
293                        else {
294                            return null;
295                        }
296    
297                        String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor);
298                        if (nameFromAnnotation != null) {
299                            return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
300                        }
301    
302                        String propertyName = getSuggestedName(propertyDescriptor);
303    
304                        if (!isExtension(propertyDescriptor)) {
305                            if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) {
306                                propertyName = getMangledName(propertyDescriptor, propertyName);
307                            }
308                            return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
309                        } else {
310                            assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor;
311    
312                            boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
313                            String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false);
314                            return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
315                        }
316                    }
317                };
318    
319                Rule<JsName> predefinedObjectsHasUnobfuscatableNames = new Rule<JsName>() {
320                    @Override
321                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
322                        // The mixing of override and rename by annotation(e.g. native) is forbidden.
323                        if (descriptor instanceof CallableMemberDescriptor &&
324                            !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) {
325                            return null;
326                        }
327    
328                        String name = getNameForAnnotatedObjectWithOverrides(descriptor);
329                        if (name != null) return getEnclosingScope(descriptor).declareName(name);
330                        return null;
331                    }
332                };
333    
334                Rule<JsName> overridingDescriptorsReferToOriginalName = new Rule<JsName>() {
335                    @Override
336                    public JsName apply(@NotNull DeclarationDescriptor descriptor) {
337                        //TODO: refactor
338                        if (!(descriptor instanceof FunctionDescriptor)) {
339                            return null;
340                        }
341                        FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
342                        if (overriddenDescriptor == null) {
343                            return null;
344                        }
345    
346                        JsScope scope = getEnclosingScope(descriptor);
347                        JsName result = getNameForDescriptor(overriddenDescriptor);
348                        scope.declareName(result.getIdent());
349                        return result;
350                    }
351                };
352    
353                addRule(namesForDynamic);
354                addRule(namesForStandardClasses);
355                addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass);
356                addRule(propertyOrPropertyAccessor);
357                addRule(predefinedObjectsHasUnobfuscatableNames);
358                addRule(overridingDescriptorsReferToOriginalName);
359                addRule(memberDeclarationsInsideParentsScope);
360            }
361        }
362    
363        @NotNull
364        public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
365            JsScope scope = getEnclosingScope(descriptor);
366            return fresh ? scope.declareFreshName(name) : scope.declareName(name);
367        }
368    
369        @NotNull
370        private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
371            DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
372            return getScopeForDescriptor(containingDeclaration.getOriginal());
373        }
374    
375        private final class ScopeGenerator extends Generator<JsScope> {
376    
377            public ScopeGenerator() {
378                Rule<JsScope> generateNewScopesForClassesWithNoAncestors = new Rule<JsScope>() {
379                    @Override
380                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
381                        if (!(descriptor instanceof ClassDescriptor)) {
382                            return null;
383                        }
384                        if (getSuperclass((ClassDescriptor) descriptor) == null) {
385                            return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
386                        }
387                        return null;
388                    }
389                };
390                Rule<JsScope> generateInnerScopesForDerivedClasses = new Rule<JsScope>() {
391                    @Override
392                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
393                        if (!(descriptor instanceof ClassDescriptor)) {
394                            return null;
395                        }
396                        ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
397                        if (superclass == null) {
398                            return null;
399                        }
400                        return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
401                    }
402                };
403                Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
404                    @Override
405                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
406                        if (!(descriptor instanceof PackageFragmentDescriptor)) {
407                            return null;
408                        }
409                        return getRootScope().innerObjectScope("Package " + descriptor.getName());
410                    }
411                };
412                //TODO: never get there
413                Rule<JsScope> generateInnerScopesForMembers = new Rule<JsScope>() {
414                    @Override
415                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
416                        JsScope enclosingScope = getEnclosingScope(descriptor);
417                        return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
418                    }
419                };
420                Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
421                    @Override
422                    public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
423                        if (!(descriptor instanceof CallableDescriptor)) {
424                            return null;
425                        }
426                        JsScope enclosingScope = getEnclosingScope(descriptor);
427    
428                        JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
429                        assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
430                        scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
431                        return correspondingFunction.getScope();
432                    }
433                };
434                addRule(createFunctionObjectsForCallableDescriptors);
435                addRule(generateNewScopesForClassesWithNoAncestors);
436                addRule(generateInnerScopesForDerivedClasses);
437                addRule(generateNewScopesForPackageDescriptors);
438                addRule(generateInnerScopesForMembers);
439            }
440        }
441    
442        @Nullable
443        public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
444            if (qualifierIsNull.get(descriptor.getOriginal()) != null) {
445                return null;
446            }
447            return qualifiers.get(descriptor.getOriginal());
448        }
449    
450        private final class QualifierGenerator extends Generator<JsExpression> {
451            public QualifierGenerator() {
452                Rule<JsExpression> standardObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
453                    @Override
454                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
455                        if (!standardClasses.isStandardObject(descriptor)) {
456                            return null;
457                        }
458                        return namer.kotlinObject();
459                    }
460                };
461                //TODO: review and refactor
462                Rule<JsExpression> packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule<JsExpression>() {
463                    @Override
464                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
465                        if (isNativeObject(descriptor)) return null;
466    
467                        DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
468                        if (!(containingDescriptor instanceof PackageFragmentDescriptor)) {
469                            return null;
470                        }
471    
472                        JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName());
473    
474                        String moduleName = getExternalModuleName(descriptor);
475                        if (moduleName == null) {
476                            return result;
477                        }
478    
479                        if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) {
480                            return null;
481                        }
482    
483                        return JsAstUtils.replaceRootReference(
484                                result, namer.getModuleReference(program.getStringLiteral(moduleName)));
485                    }
486                };
487                Rule<JsExpression> constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule<JsExpression>() {
488                    @Override
489                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
490                        if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) {
491                            //noinspection ConstantConditions
492                            return getQualifierForDescriptor(descriptor.getContainingDeclaration());
493                        }
494                        return null;
495                    }
496                };
497                Rule<JsExpression> libraryObjectsHaveKotlinQualifier = new Rule<JsExpression>() {
498                    @Override
499                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
500                        if (isLibraryObject(descriptor)) {
501                            return namer.kotlinObject();
502                        }
503                        return null;
504                    }
505                };
506                Rule<JsExpression> nativeObjectsHaveNativePartOfFullQualifier = new Rule<JsExpression>() {
507                    @Override
508                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
509                        if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null;
510    
511                        DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
512                        if (containingDeclaration != null && isNativeObject(containingDeclaration)) {
513                            return getQualifiedReference(containingDeclaration);
514                        }
515    
516                        return null;
517                    }
518                };
519                Rule<JsExpression> staticMembersHaveContainerQualifier = new Rule<JsExpression>() {
520                    @Override
521                    public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
522                        if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
523                            CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
524                            if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
525                                return getQualifiedReference(callableDescriptor.getContainingDeclaration());
526                            }
527                        }
528    
529                        return null;
530                    }
531                };
532    
533                addRule(libraryObjectsHaveKotlinQualifier);
534                addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass);
535                addRule(standardObjectsHaveKotlinQualifier);
536                addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
537                addRule(nativeObjectsHaveNativePartOfFullQualifier);
538                addRule(staticMembersHaveContainerQualifier);
539            }
540        }
541    
542        private static class QualifierIsNullGenerator extends Generator<Boolean> {
543    
544            private QualifierIsNullGenerator() {
545                Rule<Boolean> propertiesInClassHaveNoQualifiers = new Rule<Boolean>() {
546                    @Override
547                    public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
548                        if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
549                            return true;
550                        }
551                        return null;
552                    }
553                };
554                addRule(propertiesInClassHaveNoQualifiers);
555            }
556        }
557    }