All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.k2js.translate.context.StaticContext Maven / Gradle / Ivy
/*
* Copyright 2010-2013 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.k2js.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.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.reflect.ReflectionTypes;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.k2js.config.EcmaVersion;
import org.jetbrains.k2js.config.LibrarySourcesConfig;
import org.jetbrains.k2js.translate.context.generator.Generator;
import org.jetbrains.k2js.translate.context.generator.Rule;
import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.Map;
import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.getNameForAnnotatedObjectWithOverrides;
import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isLibraryObject;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMangledName;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName;
/**
* Aggregates all the static parts of the context.
*/
public final class StaticContext {
public static StaticContext generateStaticContext(@NotNull BindingContext bindingContext, @NotNull EcmaVersion ecmaVersion, @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, bindingContext, namer, intrinsics, standardClasses, program.getRootScope(), ecmaVersion, moduleDescriptor);
}
@NotNull
private final JsProgram program;
@NotNull
private final BindingContext bindingContext;
@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 EcmaVersion ecmaVersion;
//TODO: too many parameters in constructor
private StaticContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext,
@NotNull Namer namer, @NotNull Intrinsics intrinsics,
@NotNull StandardClasses standardClasses, @NotNull JsScope rootScope, @NotNull EcmaVersion ecmaVersion, @NotNull ModuleDescriptor moduleDescriptor) {
this.program = program;
this.bindingContext = bindingContext;
this.namer = namer;
this.intrinsics = intrinsics;
this.rootScope = rootScope;
this.standardClasses = standardClasses;
this.ecmaVersion = ecmaVersion;
this.reflectionTypes = new ReflectionTypes(moduleDescriptor);
}
public boolean isEcma5() {
return ecmaVersion == EcmaVersion.v5;
}
@NotNull
public JsProgram getProgram() {
return program;
}
@NotNull
public BindingContext getBindingContext() {
return bindingContext;
}
@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;
}
private final class NameGenerator extends Generator {
public NameGenerator() {
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 constructorHasTheSameNameAsTheClass = new Rule() {
@Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) {
if (!(descriptor instanceof ConstructorDescriptor)) {
return null;
}
ClassDescriptor containingClass = getContainingClass(descriptor);
assert containingClass != null : "Can't have constructor without a class";
return getNameForDescriptor(containingClass);
}
};
// 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 = propertyDescriptor.getName().asString();
if (!isExtension(propertyDescriptor)) {
if (propertyDescriptor.getVisibility() == Visibilities.PRIVATE) {
propertyName = getMangledName(propertyDescriptor, propertyName);
}
return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
} else {
if (descriptor instanceof PropertyDescriptor) {
return declarePropertyOrPropertyAccessorName(descriptor, propertyName, true);
} else {
String propertyJsName = getNameForDescriptor(propertyDescriptor).getIdent();
boolean isGetter = descriptor instanceof PropertyGetterDescriptor;
String accessorName = Namer.getNameForAccessor(propertyJsName, 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;
}
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(namesForStandardClasses);
addRule(constructorHasTheSameNameAsTheClass);
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().innerScope("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).innerScope("Scope for class " + descriptor.getName());
}
};
Rule generateNewScopesForPackageDescriptors = new Rule() {
@Override
public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
if (!(descriptor instanceof PackageFragmentDescriptor)) {
return null;
}
return getRootScope().innerScope("Package " + descriptor.getName());
}
};
//TODO: never get there
Rule generateInnerScopesForMembers = new Rule() {
@Override
public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
JsScope enclosingScope = getEnclosingScope(descriptor);
return enclosingScope.innerScope("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 JsNameRef 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 JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
if (!standardClasses.isStandardObject(descriptor)) {
return null;
}
return namer.kotlinObject();
}
};
//TODO: review and refactor
Rule packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier = new Rule() {
@Override
public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
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;
}
JsAstUtils.replaceRootReference(
result, new JsArrayAccess(namer.kotlin("modules"), program.getStringLiteral(moduleName)));
return result;
}
private String getExternalModuleName(DeclarationDescriptor descriptor) {
PsiElement element = descriptorToDeclaration(descriptor);
if (element == null && descriptor instanceof PropertyAccessorDescriptor) {
element = descriptorToDeclaration(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
}
if (element == null) {
return null;
}
return element.getContainingFile().getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME);
}
};
Rule constructorHaveTheSameQualifierAsTheClass = new Rule() {
@Override
public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
if (!(descriptor instanceof ConstructorDescriptor)) {
return null;
}
ClassDescriptor containingClass = getContainingClass(descriptor);
assert containingClass != null : "Can't have constructor without a class";
return getQualifierForDescriptor(containingClass);
}
};
Rule libraryObjectsHaveKotlinQualifier = new Rule() {
@Override
public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
if (isLibraryObject(descriptor)) {
return namer.kotlinObject();
}
return null;
}
};
addRule(libraryObjectsHaveKotlinQualifier);
addRule(constructorHaveTheSameQualifierAsTheClass);
addRule(standardObjectsHaveKotlinQualifier);
addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
}
}
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;
}
};
//TODO: hack! it seems like needed, only for Inheritance from native class
Rule nativeObjectsHaveNoQualifiers = new Rule() {
@Override
public Boolean apply(@NotNull DeclarationDescriptor descriptor) {
if (!AnnotationsUtils.isNativeObject(descriptor)) {
return null;
}
return true;
}
};
addRule(propertiesInClassHaveNoQualifiers);
addRule(nativeObjectsHaveNoQualifiers);
}
}
}