org.jetbrains.kotlin.js.translate.context.StaticContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2016 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.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.hash.LinkedHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.common.IdentifierPolicyKt;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.naming.SuggestedName;
import org.jetbrains.kotlin.js.resolve.diagnostics.JsBuiltinNameClashChecker;
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
import org.jetbrains.kotlin.js.translate.context.generator.Rule;
import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.*;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.source.PsiSourceElementKt;
import org.jetbrains.kotlin.serialization.js.ModuleKind;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
import java.util.*;
import static org.jetbrains.kotlin.descriptors.FindClassInModuleKt.findClassAcrossModuleDependencies;
import static org.jetbrains.kotlin.js.config.JsConfig.UNKNOWN_EXTERNAL_MODULE_NAME;
import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject;
import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSuperclass;
/**
* Aggregates all the static parts of the context.
*/
public final class StaticContext {
@NotNull
private final JsProgram program;
@NotNull
private final JsProgramFragment fragment;
@NotNull
private final BindingTrace bindingTrace;
@NotNull
private final Namer namer;
@NotNull
private final Intrinsics intrinsics;
@NotNull
private final JsScope rootScope;
@NotNull
private final Generator innerNames = new InnerNameGenerator();
@NotNull
private final Generator scopes = new ScopeGenerator();
@NotNull
private final Generator objectInstanceNames = new ObjectInstanceNameGenerator();
@NotNull
private final Map scopeToFunction = new HashMap<>();
@NotNull
private final Map> classOrConstructorClosure = new HashMap<>();
@NotNull
private final Map> deferredCallSites = new HashMap<>();
@NotNull
private final JsConfig config;
@NotNull
private final ModuleDescriptor currentModule;
@NotNull
private final JsImportedModule currentModuleAsImported;
@NotNull
private final NameSuggestion nameSuggestion;
@NotNull
private final Map nameCache = new HashMap<>();
@NotNull
private final Map backingFieldNameCache = new HashMap<>();
@NotNull
private final Map fqnCache = new HashMap<>();
private final Map tagCache = new HashMap<>();
@NotNull
private final Map importedModules = new LinkedHashMap<>();
@NotNull
private final DeclarationExporter exporter = new DeclarationExporter(this);
@NotNull
private final Map packageScopes = new HashMap<>();
@NotNull
private final ClassModelGenerator classModelGenerator;
@Nullable
private JsName nameForImportsForInline;
private final Map modulesImportedForInline = new HashMap<>();
private final Map specialFunctions = new EnumMap<>(SpecialFunction.class);
private final Map intrinsicNames = new HashMap<>();
@NotNull
private final SourceFilePathResolver sourceFilePathResolver;
private final Map propertyMetadataVariables = new HashMap<>();
private final boolean isStdlib;
private static final Set BUILTIN_JS_PROPERTIES = Sets.union(
JsBuiltinNameClashChecker.PROHIBITED_MEMBER_NAMES,
JsBuiltinNameClashChecker.PROHIBITED_STATIC_NAMES
);
public StaticContext(
@NotNull BindingTrace bindingTrace,
@NotNull JsConfig config,
@NotNull ModuleDescriptor moduleDescriptor,
@NotNull SourceFilePathResolver sourceFilePathResolver,
@NotNull String packageFqn
) {
program = new JsProgram();
JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope());
fragment = new JsProgramFragment(rootFunction.getScope(), packageFqn);
this.bindingTrace = bindingTrace;
this.nameSuggestion = new NameSuggestion();
this.namer = Namer.newInstance(program.getRootScope());
this.intrinsics = new Intrinsics();
this.rootScope = fragment.getScope();
this.config = config;
this.currentModule = moduleDescriptor;
this.currentModuleAsImported = new JsImportedModule(Namer.getRootPackageName(), rootScope.declareName(Namer.getRootPackageName()), null);
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
createImportedModule(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), Namer.KOTLIN_LOWER_NAME, kotlinName, null);
classModelGenerator = new ClassModelGenerator(TranslationContext.rootContext(this));
this.sourceFilePathResolver = sourceFilePathResolver;
ClassDescriptor exceptionClass = findClassAcrossModuleDependencies(
moduleDescriptor, ClassId.topLevel(new FqName("kotlin.Exception")));
isStdlib = exceptionClass != null && DescriptorUtils.getContainingModule(exceptionClass) == moduleDescriptor;
}
@NotNull
public JsProgram getProgram() {
return program;
}
@NotNull
public JsProgramFragment getFragment() {
return fragment;
}
@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 SourceFilePathResolver getSourceFilePathResolver() {
return sourceFilePathResolver;
}
@NotNull
public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ModuleDescriptor) {
return rootScope;
}
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) {
return (JsNameRef) getQualifiedExpression(descriptor);
}
@Nullable
public String getTag(@NotNull DeclarationDescriptor descriptor) {
String tag;
if (!tagCache.containsKey(descriptor)) {
tag = SignatureUtilsKt.generateSignature(descriptor);
tagCache.put(descriptor, tag);
}
else {
tag = tagCache.get(descriptor);
}
return tag;
}
@NotNull
private JsExpression getQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
JsExpression fqn = fqnCache.get(descriptor);
if (fqn == null) {
fqn = buildQualifiedExpression(descriptor);
fqnCache.put(descriptor, fqn);
}
return fqn.deepCopy();
}
@Nullable
public SuggestedName suggestName(@NotNull DeclarationDescriptor descriptor) {
return nameSuggestion.suggest(descriptor, getBindingContext());
}
@NotNull
private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
KotlinType type = classDescriptor.getDefaultType();
if (KotlinBuiltIns.isAny(classDescriptor)) {
return pureFqn("Object", null);
}
else if (KotlinBuiltIns.isInt(type) || KotlinBuiltIns.isShort(type) || KotlinBuiltIns.isByte(type) ||
KotlinBuiltIns.isFloat(type) || KotlinBuiltIns.isDouble(type)) {
return pureFqn("Number", null);
}
else if (KotlinBuiltIns.isLong(type)) {
return pureFqn("Long", Namer.kotlinObject());
}
else if (KotlinBuiltIns.isChar(type)) {
return pureFqn("BoxedChar", Namer.kotlinObject());
}
else if (KotlinBuiltIns.isString(type)) {
return pureFqn("String", null);
}
else if (KotlinBuiltIns.isBoolean(type)) {
return pureFqn("Boolean", null);
}
else if (KotlinBuiltIns.isArrayOrPrimitiveArray(classDescriptor)) {
return pureFqn("Array", null);
}
else if (FunctionTypesKt.isBuiltinFunctionalType(type)) {
return pureFqn("Function", null);
}
else if (TypeUtilsKt.isNotNullThrowable(classDescriptor.getDefaultType())) {
return pureFqn("Error", null);
}
}
SuggestedName suggested = suggestName(descriptor);
if (suggested == null) {
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
JsExpression result = getModuleExpressionFor(module);
return result != null ? result : pureFqn(Namer.getRootPackageName(), null);
}
if (config.getModuleKind() != ModuleKind.PLAIN) {
String moduleName = AnnotationsUtils.getModuleName(suggested.getDescriptor());
if (moduleName != null) {
return JsAstUtils.pureFqn(getImportedModule(moduleName, suggested.getDescriptor()).getInternalName(), null);
}
}
JsExpression expression;
List partNames = getActualNameFromSuggested(suggested);
if (isLibraryObject(suggested.getDescriptor())) {
expression = Namer.kotlinObject();
}
// Don't generate qualifier for top-level native declarations
// Don't generate qualifier for local declarations
else if (isNativeObject(suggested.getDescriptor()) && !isNativeObject(suggested.getScope()) ||
suggested.getDescriptor() instanceof CallableDescriptor && suggested.getScope() instanceof FunctionDescriptor) {
expression = null;
}
else {
expression = getQualifiedExpression(suggested.getScope());
}
if (isNativeObject(suggested.getDescriptor()) && DescriptorUtils.isTopLevelDeclaration(suggested.getDescriptor())) {
String fileModuleName = AnnotationsUtils.getFileModuleName(getBindingContext(), suggested.getDescriptor());
if (fileModuleName != null) {
JsName moduleJsName = getImportedModule(fileModuleName, null).getInternalName();
expression = pureFqn(moduleJsName, expression);
}
String qualifier = AnnotationsUtils.getFileQualifier(getBindingContext(), suggested.getDescriptor());
if (qualifier != null) {
for (String qualifierPart : StringUtil.split(qualifier, ".")) {
expression = pureFqn(qualifierPart, expression);
}
}
}
for (JsName partName : partNames) {
expression = new JsNameRef(partName, expression);
applySideEffects(expression, suggested.getDescriptor());
}
assert expression != null : "Since partNames is not empty, expression must be non-null";
return expression;
}
@NotNull
public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
JsName result = rootScope.declareName("Object");
MetadataProperties.setDescriptor(result, descriptor);
return result;
}
SuggestedName suggested = nameSuggestion.suggest(descriptor, getBindingContext());
if (suggested == null) {
throw new IllegalArgumentException("Can't generate name for root declarations: " + descriptor);
}
return getActualNameFromSuggested(suggested).get(0);
}
@NotNull
public JsName getNameForBackingField(@NotNull VariableDescriptorWithAccessors property) {
JsName name = backingFieldNameCache.get(property);
if (name == null) {
SuggestedName fqn = nameSuggestion.suggest(property, getBindingContext());
assert fqn != null : "Properties are non-root declarations: " + property;
assert fqn.getNames().size() == 1 : "Private names must always consist of exactly one name";
JsScope scope = getScopeForDescriptor(fqn.getScope());
String baseName = NameSuggestion.getPrivateMangledName(fqn.getNames().get(0), property) + "_0";
name = scope.declareFreshName(baseName);
backingFieldNameCache.put(property, name);
}
return name;
}
@NotNull
public JsName getInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsName name = innerNames.get(descriptor.getOriginal());
assert name != null : "Must have inner name for descriptor";
return name;
}
@NotNull
public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
JsName name = objectInstanceNames.get(descriptor.getOriginal());
assert name != null : "Must have inner name for object instance";
return name;
}
@NotNull
private List getActualNameFromSuggested(@NotNull SuggestedName suggested) {
JsScope scope = getScopeForDescriptor(suggested.getScope());
if (DynamicCallsKt.isDynamic(suggested.getDescriptor())) {
scope = JsDynamicScope.INSTANCE;
}
else if (AnnotationsUtils.isPredefinedObject(suggested.getDescriptor()) &&
DescriptorUtils.isTopLevelDeclaration(suggested.getDescriptor())) {
scope = rootScope;
}
List names = new ArrayList<>();
if (suggested.getStable()) {
String tag = getTag(suggested.getDescriptor());
int index = 0;
for (String namePart : suggested.getNames()) {
JsName name = scope.declareName(namePart);
MetadataProperties.setDescriptor(name, suggested.getDescriptor());
if (tag != null && !AnnotationsUtils.isNativeObject(suggested.getDescriptor()) &&
!AnnotationsUtils.isLibraryObject(suggested.getDescriptor())
) {
fragment.getNameBindings().add(new JsNameBinding(index++ + ":" + tag, name));
}
names.add(name);
}
}
else {
// TODO: consider using sealed class to represent FQNs
assert suggested.getNames().size() == 1 : "Private names must always consist of exactly one name";
JsName name = nameCache.get(suggested.getDescriptor());
if (name == null) {
String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0));
if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
suggested.getDescriptor() instanceof ValueParameterDescriptor
) {
name = JsScope.declareTemporaryName(baseName);
}
else {
if (!DescriptorUtils.isDescriptorWithLocalVisibility(suggested.getDescriptor())) {
baseName += "_0";
}
name = scope.declareFreshName(baseName);
}
}
nameCache.put(suggested.getDescriptor(), name);
MetadataProperties.setDescriptor(name, suggested.getDescriptor());
String tag = getTag(suggested.getDescriptor());
if (tag != null) {
fragment.getNameBindings().add(new JsNameBinding(tag, name));
}
names.add(name);
}
return names;
}
@NotNull
public JsConfig getConfig() {
return config;
}
@NotNull
JsName importDeclaration(@NotNull String suggestedName, @NotNull String tag, @NotNull JsExpression declaration) {
JsName result = importDeclarationImpl(suggestedName, tag, declaration);
fragment.getNameBindings().add(new JsNameBinding(tag, result));
return result;
}
@NotNull
private JsName importDeclarationImpl(@NotNull String suggestedName, @NotNull String tag, @NotNull JsExpression declaration) {
JsName result = JsScope.declareTemporaryName(suggestedName);
MetadataProperties.setImported(result, true);
fragment.getImports().put(tag, declaration);
return result;
}
@NotNull
private JsName localOrImportedName(@NotNull DeclarationDescriptor descriptor, @NotNull String suggestedName) {
ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
JsName name;
String tag = getTag(descriptor);
boolean isNative = AnnotationsUtils.isNativeObject(descriptor) || AnnotationsUtils.isLibraryObject(descriptor);
if (module != currentModule && !isLocallyRedeclaredBuiltin(descriptor) || isNative) {
assert tag != null : "Can't import declaration without tag: " + descriptor;
JsNameRef result = getQualifiedReference(descriptor);
if (isNative && result.getQualifier() == null && result.getName() != null) {
name = result.getName();
tag = null;
}
else {
name = importDeclarationImpl(suggestedName, tag, result);
}
}
else {
name = JsScope.declareTemporaryName(suggestedName);
}
if (tag != null) {
fragment.getNameBindings().add(new JsNameBinding(tag, name));
}
MetadataProperties.setDescriptor(name, descriptor);
return name;
}
// When compiling stdlib, we may have sources for built-in declaration. In this case we have two distinct descriptors
// for declaration with one signature. One descriptor is from current module, another descriptor is from built-in module.
// Different declarations refer different descriptors. This may cause single name to be both imported and declared locally,
// which in turn causes runtime error. We avoid this by detecting this case and turning off import.
private boolean isLocallyRedeclaredBuiltin(@NotNull DeclarationDescriptor descriptor) {
if (!(descriptor instanceof ClassDescriptor)) return false;
FqName fqName = DescriptorUtils.getFqNameSafe(descriptor);
ClassId classId = ClassId.topLevel(fqName);
ClassDescriptor localDescriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(currentModule, classId);
return localDescriptor != null && DescriptorUtils.getContainingModule(localDescriptor) == currentModule;
}
private final Set inlineFunctionTags = new HashSet<>();
@NotNull public Set getInlineFunctionTags() {
return inlineFunctionTags;
}
public void reportInlineFunctionTag(@NotNull String tag) {
inlineFunctionTags.add(tag);
}
private final class InnerNameGenerator extends Generator {
public InnerNameGenerator() {
addRule(descriptor -> {
if (descriptor instanceof PackageFragmentDescriptor && DescriptorUtils.getContainingModule(descriptor) == currentModule) {
return exporter.getLocalPackageName(((PackageFragmentDescriptor) descriptor).getFqName());
}
if (descriptor instanceof FunctionDescriptor) {
FunctionDescriptor initialDescriptor = ((FunctionDescriptor) descriptor).getInitialSignatureDescriptor();
if (initialDescriptor != null) {
return getInnerNameForDescriptor(initialDescriptor);
}
}
if (descriptor instanceof ModuleDescriptor) {
return getModuleInnerName(descriptor);
}
if (descriptor instanceof LocalVariableDescriptor || descriptor instanceof ParameterDescriptor) {
return getNameForDescriptor(descriptor);
}
if (descriptor instanceof ConstructorDescriptor) {
if (((ConstructorDescriptor) descriptor).isPrimary()) {
return getInnerNameForDescriptor(((ConstructorDescriptor) descriptor).getConstructedClass());
}
}
return localOrImportedName(descriptor, getSuggestedName(descriptor));
});
}
}
private final class ObjectInstanceNameGenerator extends Generator {
public ObjectInstanceNameGenerator() {
addRule(descriptor -> {
String suggested = getSuggestedName(descriptor) + Namer.OBJECT_INSTANCE_FUNCTION_SUFFIX;
JsName result = JsScope.declareTemporaryName(suggested);
String tag = SignatureUtilsKt.generateSignature(descriptor);
if (tag != null) {
fragment.getNameBindings().add(new JsNameBinding("object:" + tag, result));
}
return result;
});
}
}
@NotNull
public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
String suggestedName;
if (descriptor instanceof PropertyGetterDescriptor) {
PropertyGetterDescriptor getter = (PropertyGetterDescriptor) descriptor;
suggestedName = "get_" + getSuggestedName(getter.getCorrespondingProperty());
}
else if (descriptor instanceof PropertySetterDescriptor) {
PropertySetterDescriptor setter = (PropertySetterDescriptor) descriptor;
suggestedName = "set_" + getSuggestedName(setter.getCorrespondingProperty());
}
else if (descriptor instanceof ConstructorDescriptor) {
ConstructorDescriptor constructor = (ConstructorDescriptor) descriptor;
suggestedName = getSuggestedName(constructor.getContainingDeclaration()) + "_init";
descriptor = descriptor.getContainingDeclaration();
assert descriptor != null : "ConstructorDescriptor should have containing declaration: " + constructor;
}
else {
if (descriptor.getName().isSpecial()) {
if (descriptor instanceof ClassDescriptor) {
if (DescriptorUtils.isAnonymousObject(descriptor)) {
suggestedName = "ObjectLiteral";
}
else {
suggestedName = "Anonymous";
}
}
else if (descriptor instanceof FunctionDescriptor) {
suggestedName = "lambda";
}
else {
suggestedName = "anonymous";
}
}
else {
suggestedName = NameSuggestion.sanitizeName(descriptor.getName().asString());
}
}
if (!(descriptor instanceof PackageFragmentDescriptor) && !DescriptorUtils.isTopLevelDeclaration(descriptor)) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
assert container != null : "We just figured out that descriptor is not for a top-level declaration: " + descriptor;
suggestedName = getSuggestedName(container) + "$" + NameSuggestion.sanitizeName(suggestedName);
}
return suggestedName;
}
private JsScope getScopeForPackage(FqName fqName) {
JsScope scope = packageScopes.get(fqName);
if (scope == null) {
if (fqName.isRoot()) {
scope = new JsRootScope(program);
}
else {
JsScope parentScope = getScopeForPackage(fqName.parent());
scope = parentScope.innerObjectScope(fqName.shortName().asString());
}
packageScopes.put(fqName, scope);
}
return scope;
}
private final class ScopeGenerator extends Generator {
public ScopeGenerator() {
Rule generateNewScopesForClassesWithNoAncestors = descriptor -> {
if (!(descriptor instanceof ClassDescriptor)) {
return null;
}
if (getSuperclass((ClassDescriptor) descriptor) == null) {
JsFunction function = new JsFunction(new JsRootScope(program), new JsBlock(), descriptor.toString());
for (String builtinName : BUILTIN_JS_PROPERTIES) {
function.getScope().declareName(builtinName);
}
scopeToFunction.put(function.getScope(), function);
return function.getScope();
}
return null;
};
Rule generateInnerScopesForDerivedClasses = 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 = descriptor -> fragment.getScope();
//TODO: never get there
Rule generateInnerScopesForMembers =
descriptor -> fragment.getScope().innerObjectScope("Scope for member " + descriptor.getName());
Rule createFunctionObjectsForCallableDescriptors = descriptor -> {
if (!(descriptor instanceof CallableDescriptor)) {
return null;
}
JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(fragment.getScope());
assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
correspondingFunction.setSource(PsiSourceElementKt.getPsi(((CallableDescriptor) descriptor).getSource()));
return correspondingFunction.getScope();
};
Rule scopeForPackage = descriptor -> {
if (!(descriptor instanceof PackageFragmentDescriptor)) return null;
PackageFragmentDescriptor packageDescriptor = (PackageFragmentDescriptor) descriptor;
return getScopeForPackage(packageDescriptor.getFqName());
};
addRule(scopeForPackage);
addRule(createFunctionObjectsForCallableDescriptors);
addRule(generateNewScopesForClassesWithNoAncestors);
addRule(generateInnerScopesForDerivedClasses);
addRule(generateNewScopesForPackageDescriptors);
addRule(generateInnerScopesForMembers);
}
}
@Nullable
private JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
JsName name = getModuleInnerName(descriptor);
return name != null ? JsAstUtils.pureFqn(name, null) : null;
}
@Nullable
private JsName getModuleInnerName(@NotNull DeclarationDescriptor descriptor) {
JsImportedModule module = getJsImportedModule(descriptor);
return module == null ? null : module.getInternalName();
}
@Nullable
private JsImportedModule getJsImportedModule(@NotNull DeclarationDescriptor descriptor) {
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
if (currentModule == module) {
return currentModuleAsImported;
}
String moduleName = JsDescriptorUtils.getModuleName(module);
if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null;
return getImportedModule(moduleName, null);
}
@NotNull
public JsImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) {
String plainName = descriptor != null && config.getModuleKind() == ModuleKind.UMD ? getPlainId(descriptor) : null;
JsImportedModuleKey key = new JsImportedModuleKey(baseName, plainName);
JsImportedModule module = importedModules.get(key);
if (module == null) {
JsName internalName = JsScope.declareTemporaryName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName));
module = createImportedModule(key, baseName, internalName, plainName != null ? pureFqn(plainName, null) : null);
}
return module;
}
private JsImportedModule createImportedModule(JsImportedModuleKey key, String baseName, JsName internalName, JsExpression plainName) {
JsImportedModule module = new JsImportedModule(baseName, internalName, plainName);
importedModules.put(key, module);
fragment.getImportedModules().add(module);
return module;
}
@NotNull
private String getPlainId(@NotNull DeclarationDescriptor declaration) {
SuggestedName suggestedName = nameSuggestion.suggest(declaration, getBindingContext());
assert suggestedName != null : "Declaration should not be ModuleDescriptor, therefore suggestedName should be non-null";
return suggestedName.getNames().get(0);
}
private static void applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
if (descriptor instanceof FunctionDescriptor ||
descriptor instanceof PackageFragmentDescriptor ||
descriptor instanceof ClassDescriptor
) {
MetadataProperties.setSideEffects(expression, SideEffectKind.PURE);
}
}
public void putClassOrConstructorClosure(@NotNull MemberDescriptor localClass, @NotNull List closure) {
classOrConstructorClosure.put(localClass, Lists.newArrayList(closure));
}
@Nullable
public List getClassOrConstructorClosure(@NotNull MemberDescriptor descriptor) {
List result = classOrConstructorClosure.get(descriptor);
return result != null ? Lists.newArrayList(result) : null;
}
@NotNull
public Map> getDeferredCallSites() {
return deferredCallSites;
}
@NotNull
public List getTopLevelStatements() {
return fragment.getInitializerBlock().getStatements();
}
@NotNull
public List getDeclarationStatements() {
return fragment.getDeclarationBlock().getStatements();
}
public void addClass(@NotNull ClassDescriptor classDescriptor) {
if (!AnnotationsUtils.isNativeObject(classDescriptor) && !AnnotationsUtils.isLibraryObject(classDescriptor)) {
fragment.getClasses().put(getInnerNameForDescriptor(classDescriptor), classModelGenerator.generateClassModel(classDescriptor));
}
}
public void export(@NotNull MemberDescriptor descriptor, boolean force) {
exporter.export(descriptor, force);
}
@NotNull
public NameSuggestion getNameSuggestion() {
return nameSuggestion;
}
@NotNull
public ModuleDescriptor getCurrentModule() {
return currentModule;
}
public void addInlineCall(@NotNull CallableDescriptor descriptor) {
descriptor = (CallableDescriptor) JsDescriptorUtils.findRealInlineDeclaration(descriptor);
String tag = Namer.getFunctionTag(descriptor, config, getBindingContext());
JsExpression moduleExpression = exportModuleForInline(DescriptorUtils.getContainingModule(descriptor));
if (moduleExpression == null) {
moduleExpression = getModuleExpressionFor(descriptor);
}
fragment.getInlineModuleMap().put(tag, moduleExpression);
}
@NotNull
private JsName getNameForImportsForInline() {
if (nameForImportsForInline == null) {
JsName name = JsScope.declareTemporaryName(Namer.IMPORTS_FOR_INLINE_PROPERTY);
fragment.getNameBindings().add(new JsNameBinding(Namer.IMPORTS_FOR_INLINE_PROPERTY, name));
nameForImportsForInline = name;
return name;
}
else {
return nameForImportsForInline;
}
}
@Nullable
public JsExpression exportModuleForInline(@NotNull ModuleDescriptor declaration) {
String moduleName = JsDescriptorUtils.getModuleName(declaration);
if (moduleName.equals(Namer.KOTLIN_LOWER_NAME)) return null;
JsImportedModule importedModule = getJsImportedModule(declaration);
if (importedModule == null) return null;
return exportModuleForInline(moduleName, importedModule);
}
@NotNull
public JsExpression exportModuleForInline(@NotNull String moduleId, @NotNull JsImportedModule moduleName) {
JsExpression moduleRef = modulesImportedForInline.get(moduleId);
if (moduleRef == null) {
JsExpression currentModuleRef = pureFqn(getInnerNameForDescriptor(getCurrentModule()), null);
JsExpression importsRef = pureFqn(Namer.IMPORTS_FOR_INLINE_PROPERTY, currentModuleRef);
JsExpression currentImports = pureFqn(getNameForImportsForInline(), null);
JsExpression lhsModuleRef;
if (IdentifierPolicyKt.isValidES5Identifier(moduleId)) {
moduleRef = pureFqn(moduleId, importsRef);
lhsModuleRef = pureFqn(moduleId, currentImports);
}
else {
moduleRef = new JsArrayAccess(importsRef, new JsStringLiteral(moduleId));
MetadataProperties.setSideEffects(moduleRef, SideEffectKind.PURE);
lhsModuleRef = new JsArrayAccess(currentImports, new JsStringLiteral(moduleId));
}
MetadataProperties.setLocalAlias(moduleRef, moduleName);
JsExpressionStatement importStmt = new JsExpressionStatement(JsAstUtils.assignment(lhsModuleRef, moduleName.getInternalName().makeRef()));
MetadataProperties.setExportedTag(importStmt, "imports:" + moduleId);
getFragment().getExportBlock().getStatements().add(importStmt);
modulesImportedForInline.put(moduleId, moduleRef);
}
return moduleRef.deepCopy();
}
@NotNull
public JsName getNameForSpecialFunction(@NotNull SpecialFunction specialFunction) {
return specialFunctions.computeIfAbsent(specialFunction, f -> {
JsExpression expression = Namer.createSpecialFunction(specialFunction);
JsName name = importDeclaration(f.getSuggestedName(), TranslationUtils.getTagForSpecialFunction(f), expression);
MetadataProperties.setSpecialFunction(name, f);
return name;
});
}
@NotNull
public JsExpression getReferenceToIntrinsic(@NotNull String name) {
return pureFqn(getNameForIntrinsic(name), null);
}
@NotNull
public JsName getNameForIntrinsic(@NotNull String name) {
JsName resultName = intrinsicNames.computeIfAbsent(name, k -> {
if (isStdlib) {
DeclarationDescriptor descriptor = findDescriptorForIntrinsic(name);
if (descriptor != null) {
return getInnerNameForDescriptor(descriptor);
}
}
return importDeclaration(NameSuggestion.sanitizeName(name), "intrinsic:" + name, TranslationUtils.getIntrinsicFqn(name));
});
return resultName;
}
@Nullable
private DeclarationDescriptor findDescriptorForIntrinsic(@NotNull String name) {
PackageViewDescriptor rootPackage = currentModule.getPackage(FqName.ROOT);
FunctionDescriptor functionDescriptor = DescriptorUtils.getFunctionByNameOrNull(
rootPackage.getMemberScope(), Name.identifier(name));
if (functionDescriptor != null) return functionDescriptor;
ClassifierDescriptor cls = rootPackage.getMemberScope().getContributedClassifier(
Name.identifier(name), NoLookupLocation.FROM_BACKEND);
if (cls != null) return cls;
return null;
}
@NotNull
public JsName getVariableForPropertyMetadata(@NotNull VariableDescriptorWithAccessors property) {
return propertyMetadataVariables.computeIfAbsent(property, p -> {
String id = getSuggestedName(property) + "_metadata";
JsName name = JsScope.declareTemporaryName(NameSuggestion.sanitizeName(id));
// Unexpectedly! However, the only thing, for which 'imported' property is relevant, is a import clener.
// We want similar cleanup to be performed for unused MetadataProperty instances.
// TODO: consider a different name for 'imported' property
MetadataProperties.setImported(name, true);
JsStringLiteral propertyNameLiteral = new JsStringLiteral(property.getName().asString());
JsExpression construction = new JsNew(getReferenceToIntrinsic("PropertyMetadata"),
Collections.singletonList(propertyNameLiteral));
fragment.getDeclarationBlock().getStatements().add(JsAstUtils.newVar(name, construction));
return name;
});
}
}