All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.js.translate.context.StaticContext Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.js.translate.context;

import com.google.common.collect.Maps;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Factory;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.config.Config;
import org.jetbrains.kotlin.js.config.EcmaVersion;
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
import org.jetbrains.kotlin.js.translate.context.generator.Rule;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;

import java.util.Map;

import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName;
import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isExtension;
import static org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt.isDynamic;

/**
 * Aggregates all the static parts of the context.
 */
public final class StaticContext {

    public static StaticContext generateStaticContext(@NotNull BindingTrace bindingTrace, @NotNull Config config, @NotNull ModuleDescriptor moduleDescriptor) {
        JsProgram program = new JsProgram("main");
        Namer namer = Namer.newInstance(program.getRootScope());
        Intrinsics intrinsics = new Intrinsics();
        StandardClasses standardClasses = StandardClasses.bindImplementations(namer.getKotlinScope());
        return new StaticContext(program, bindingTrace, namer, intrinsics, standardClasses, program.getRootScope(), config, moduleDescriptor);
    }

    @NotNull
    private final JsProgram program;

    @NotNull
    private final BindingTrace bindingTrace;
    @NotNull
    private final Namer namer;

    @NotNull
    private final Intrinsics intrinsics;

    @NotNull
    private final StandardClasses standardClasses;

    @NotNull
    private final ReflectionTypes reflectionTypes;

    @NotNull
    private final JsScope rootScope;

    @NotNull
    private final Generator names = new NameGenerator();
    @NotNull
    private final Map packageNames = Maps.newHashMap();
    @NotNull
    private final Generator scopes = new ScopeGenerator();
    @NotNull
    private final Generator qualifiers = new QualifierGenerator();
    @NotNull
    private final Generator qualifierIsNull = new QualifierIsNullGenerator();

    @NotNull
    private final Map scopeToFunction = Maps.newHashMap();

    @NotNull
    private final Config config;

    @NotNull
    private final EcmaVersion ecmaVersion;

    //TODO: too many parameters in constructor
    private StaticContext(@NotNull JsProgram program, @NotNull BindingTrace bindingTrace,
            @NotNull Namer namer, @NotNull Intrinsics intrinsics,
            @NotNull StandardClasses standardClasses, @NotNull JsScope rootScope, @NotNull Config config, @NotNull ModuleDescriptor moduleDescriptor) {
        this.program = program;
        this.bindingTrace = bindingTrace;
        this.namer = namer;
        this.intrinsics = intrinsics;
        this.rootScope = rootScope;
        this.standardClasses = standardClasses;
        this.config = config;
        this.ecmaVersion = config.getTarget();
        this.reflectionTypes = new ReflectionTypes(moduleDescriptor);
    }

    public boolean isEcma5() {
        return ecmaVersion == EcmaVersion.v5;
    }

    @NotNull
    public JsProgram getProgram() {
        return program;
    }

    @NotNull
    public BindingTrace getBindingTrace() {
        return bindingTrace;
    }

    @NotNull
    public BindingContext getBindingContext() {
        return bindingTrace.getBindingContext();
    }

    @NotNull
    public Intrinsics getIntrinsics() {
        return intrinsics;
    }

    @NotNull
    public Namer getNamer() {
        return namer;
    }

    @NotNull
    public ReflectionTypes getReflectionTypes() {
        return reflectionTypes;
    }

    @NotNull
    public JsScope getRootScope() {
        return rootScope;
    }

    @NotNull
    public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
        JsScope scope = scopes.get(descriptor.getOriginal());
        assert scope != null : "Must have a scope for descriptor";
        return scope;
    }

    @NotNull
    public JsFunction getFunctionWithScope(@NotNull CallableDescriptor descriptor) {
        JsScope scope = getScopeForDescriptor(descriptor);
        JsFunction function = scopeToFunction.get(scope);
        assert scope.equals(function.getScope()) : "Inconsistency.";
        return function;
    }

    @NotNull
    public JsNameRef getQualifiedReference(@NotNull DeclarationDescriptor descriptor) {
        if (descriptor instanceof PackageViewDescriptor) {
            return getQualifiedReference(((PackageViewDescriptor) descriptor).getFqName());
        }
        if (descriptor instanceof PackageFragmentDescriptor) {
            return getQualifiedReference(((PackageFragmentDescriptor) descriptor).getFqName());
        }

        return new JsNameRef(getNameForDescriptor(descriptor), getQualifierForDescriptor(descriptor));
    }

    @NotNull
    public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
        return new JsNameRef(getNameForPackage(packageFqName),
                             packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
    }

    @NotNull
    public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
        JsName name = names.get(descriptor.getOriginal());
        assert name != null : "Must have name for descriptor";
        return name;
    }

    @NotNull
    public JsName getNameForPackage(@NotNull final FqName packageFqName) {
        return ContainerUtil.getOrCreate(packageNames, packageFqName, new Factory() {
            @Override
            public JsName create() {
                String name = Namer.generatePackageName(packageFqName);
                return getRootScope().declareName(name);
            }
        });
    }

    @NotNull
    private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
        JsNameRef result = null;
        JsNameRef qualifier = null;

        for (FqName pathElement : ContainerUtil.reverse(packageFqName.path())) {
            JsNameRef ref = getNameForPackage(pathElement).makeRef();

            if (qualifier == null) {
                result = ref;
            }
            else {
                qualifier.setQualifier(ref);
            }

            qualifier = ref;
        }

        assert result != null : "didn't iterate: " + packageFqName;
        return result;
    }

    @NotNull
    public Config getConfig() {
        return config;
    }

    private final class NameGenerator extends Generator {

        public NameGenerator() {
            Rule namesForDynamic = new Rule() {
                @Override
                @Nullable
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    if (isDynamic(descriptor)) {
                        String name = descriptor.getName().asString();
                        return JsDynamicScope.INSTANCE$.declareName(name);
                    }

                    return null;
                }
            };

            Rule namesForStandardClasses = new Rule() {
                @Override
                @Nullable
                public JsName apply(@NotNull DeclarationDescriptor data) {
                    if (!standardClasses.isStandardObject(data)) {
                        return null;
                    }
                    return standardClasses.getStandardObjectName(data);
                }
            };
            Rule memberDeclarationsInsideParentsScope = new Rule() {
                @Override
                @Nullable
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    JsScope scope = getEnclosingScope(descriptor);
                    return scope.declareFreshName(getSuggestedName(descriptor));
                }
            };
            Rule constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule() {
                @Override
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() ||
                        DescriptorUtils.isCompanionObject(descriptor)
                    ) {
                        //noinspection ConstantConditions
                        return getNameForDescriptor(descriptor.getContainingDeclaration());
                    }
                    return null;
                }
            };

            // ecma 5 property name never declares as obfuscatable:
            // 1) property cannot be overloaded, so, name collision is not possible
            // 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
            //
            // But extension property may obfuscatable, because transform into function. Example: String.foo = 1, Int.foo = 2
            Rule propertyOrPropertyAccessor = new Rule() {
                @Override
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    PropertyDescriptor propertyDescriptor;
                    if (descriptor instanceof PropertyAccessorDescriptor) {
                        propertyDescriptor = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty();
                    }
                    else if (descriptor instanceof PropertyDescriptor) {
                        propertyDescriptor = (PropertyDescriptor) descriptor;
                    }
                    else {
                        return null;
                    }

                    String nameFromAnnotation = getNameForAnnotatedObjectWithOverrides(propertyDescriptor);
                    if (nameFromAnnotation != null) {
                        return declarePropertyOrPropertyAccessorName(descriptor, nameFromAnnotation, false);
                    }

                    String propertyName = getSuggestedName(propertyDescriptor);

                    if (!isExtension(propertyDescriptor)) {
                        if (Visibilities.isPrivate(propertyDescriptor.getVisibility())) {
                            propertyName = getMangledName(propertyDescriptor, propertyName);
                        }
                        return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
                    } else {
                        assert !(descriptor instanceof PropertyDescriptor) : "descriptor should not be instance of PropertyDescriptor: " + descriptor;

                        boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
                        String accessorName = Namer.getNameForAccessor(propertyName, isGetter, false);
                        return declarePropertyOrPropertyAccessorName(descriptor, accessorName, false);
                    }
                }
            };

            Rule predefinedObjectsHasUnobfuscatableNames = new Rule() {
                @Override
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    // The mixing of override and rename by annotation(e.g. native) is forbidden.
                    if (descriptor instanceof CallableMemberDescriptor &&
                        !((CallableMemberDescriptor) descriptor).getOverriddenDescriptors().isEmpty()) {
                        return null;
                    }

                    if (descriptor instanceof ConstructorDescriptor) {
                        DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration();
                        assert classDescriptor != null;
                        descriptor = classDescriptor;
                    }

                    String name = getNameForAnnotatedObjectWithOverrides(descriptor);
                    if (name != null) return getEnclosingScope(descriptor).declareName(name);
                    return null;
                }
            };

            Rule overridingDescriptorsReferToOriginalName = new Rule() {
                @Override
                public JsName apply(@NotNull DeclarationDescriptor descriptor) {
                    //TODO: refactor
                    if (!(descriptor instanceof FunctionDescriptor)) {
                        return null;
                    }
                    FunctionDescriptor overriddenDescriptor = getOverriddenDescriptor((FunctionDescriptor) descriptor);
                    if (overriddenDescriptor == null) {
                        return null;
                    }

                    JsScope scope = getEnclosingScope(descriptor);
                    JsName result = getNameForDescriptor(overriddenDescriptor);
                    scope.declareName(result.getIdent());
                    return result;
                }
            };

            addRule(namesForDynamic);
            addRule(namesForStandardClasses);
            addRule(constructorOrCompanionObjectHasTheSameNameAsTheClass);
            addRule(propertyOrPropertyAccessor);
            addRule(predefinedObjectsHasUnobfuscatableNames);
            addRule(overridingDescriptorsReferToOriginalName);
            addRule(memberDeclarationsInsideParentsScope);
        }
    }

    @NotNull
    public JsName declarePropertyOrPropertyAccessorName(@NotNull DeclarationDescriptor descriptor, @NotNull String name, boolean fresh) {
        JsScope scope = getEnclosingScope(descriptor);
        return fresh ? scope.declareFreshName(name) : scope.declareName(name);
    }

    @NotNull
    private JsScope getEnclosingScope(@NotNull DeclarationDescriptor descriptor) {
        DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor);
        return getScopeForDescriptor(containingDeclaration.getOriginal());
    }

    private final class ScopeGenerator extends Generator {

        public ScopeGenerator() {
            Rule generateNewScopesForClassesWithNoAncestors = new Rule() {
                @Override
                public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
                    if (!(descriptor instanceof ClassDescriptor)) {
                        return null;
                    }
                    if (getSuperclass((ClassDescriptor) descriptor) == null) {
                        return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
                    }
                    return null;
                }
            };
            Rule generateInnerScopesForDerivedClasses = new Rule() {
                @Override
                public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
                    if (!(descriptor instanceof ClassDescriptor)) {
                        return null;
                    }
                    ClassDescriptor superclass = getSuperclass((ClassDescriptor) descriptor);
                    if (superclass == null) {
                        return null;
                    }
                    return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
                }
            };
            Rule generateNewScopesForPackageDescriptors = new Rule() {
                @Override
                public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
                    if (!(descriptor instanceof PackageFragmentDescriptor)) {
                        return null;
                    }
                    return getRootScope().innerObjectScope("Package " + descriptor.getName());
                }
            };
            //TODO: never get there
            Rule generateInnerScopesForMembers = new Rule() {
                @Override
                public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
                    JsScope enclosingScope = getEnclosingScope(descriptor);
                    return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
                }
            };
            Rule createFunctionObjectsForCallableDescriptors = new Rule() {
                @Override
                public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
                    if (!(descriptor instanceof CallableDescriptor)) {
                        return null;
                    }
                    JsScope enclosingScope = getEnclosingScope(descriptor);

                    JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
                    assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
                    scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
                    return correspondingFunction.getScope();
                }
            };
            addRule(createFunctionObjectsForCallableDescriptors);
            addRule(generateNewScopesForClassesWithNoAncestors);
            addRule(generateInnerScopesForDerivedClasses);
            addRule(generateNewScopesForPackageDescriptors);
            addRule(generateInnerScopesForMembers);
        }
    }

    @Nullable
    public JsExpression getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
        if (qualifierIsNull.get(descriptor.getOriginal()) != null) {
            return null;
        }
        return qualifiers.get(descriptor.getOriginal());
    }

    private final class QualifierGenerator extends Generator {
        public QualifierGenerator() {
            Rule standardObjectsHaveKotlinQualifier = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (!standardClasses.isStandardObject(descriptor)) {
                        return null;
                    }
                    return namer.kotlinObject();
                }
            };
            //TODO: review and refactor
            Rule packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (isNativeObject(descriptor)) return null;

                    DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor);
                    if (!(containingDescriptor instanceof PackageFragmentDescriptor)) {
                        return null;
                    }

                    JsNameRef result = getQualifierForParentPackage(((PackageFragmentDescriptor) containingDescriptor).getFqName());

                    String moduleName = getExternalModuleName(descriptor);
                    if (moduleName == null) {
                        return result;
                    }

                    if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) {
                        return null;
                    }

                    return JsAstUtils.replaceRootReference(
                            result, namer.getModuleReference(program.getStringLiteral(moduleName)));
                }
            };
            Rule constructorOrCompanionObjectHasTheSameQualifierAsTheClass = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (descriptor instanceof ConstructorDescriptor || DescriptorUtils.isCompanionObject(descriptor)) {
                        //noinspection ConstantConditions
                        return getQualifierForDescriptor(descriptor.getContainingDeclaration());
                    }
                    return null;
                }
            };
            Rule libraryObjectsHaveKotlinQualifier = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (isLibraryObject(descriptor)) {
                        return namer.kotlinObject();
                    }
                    return null;
                }
            };
            Rule nativeObjectsHaveNativePartOfFullQualifier = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (descriptor instanceof ConstructorDescriptor || !isNativeObject(descriptor)) return null;

                    DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
                    if (containingDeclaration != null && isNativeObject(containingDeclaration)) {
                        return getQualifiedReference(containingDeclaration);
                    }

                    return null;
                }
            };
            Rule staticMembersHaveContainerQualifier = new Rule() {
                @Override
                public JsExpression apply(@NotNull DeclarationDescriptor descriptor) {
                    if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
                        CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
                        if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
                            return getQualifiedReference(callableDescriptor.getContainingDeclaration());
                        }
                    }

                    return null;
                }
            };

            addRule(libraryObjectsHaveKotlinQualifier);
            addRule(constructorOrCompanionObjectHasTheSameQualifierAsTheClass);
            addRule(standardObjectsHaveKotlinQualifier);
            addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
            addRule(nativeObjectsHaveNativePartOfFullQualifier);
            addRule(staticMembersHaveContainerQualifier);
        }
    }

    private static class QualifierIsNullGenerator extends Generator {

        private QualifierIsNullGenerator() {
            Rule propertiesInClassHaveNoQualifiers = new Rule() {
                @Override
                public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
                    if ((descriptor instanceof PropertyDescriptor) && descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
                        return true;
                    }
                    return null;
                }
            };
            addRule(propertiesInClassHaveNoQualifiers);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy