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