JS: refactor code that exports local declaration from current module
This commit is contained in:
@@ -20,9 +20,7 @@ import com.intellij.openapi.util.text.StringUtil
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.js.inline.util.CollectUtilsKt;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.collectNamedFunctions;
|
import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.collectNamedFunctions;
|
||||||
import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.collectJsProperties;
|
|
||||||
|
|
||||||
public class AstSearchUtil {
|
public class AstSearchUtil {
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -37,13 +36,6 @@ public class AstSearchUtil {
|
|||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression getProperty(@NotNull JsNode searchRoot, @NotNull String name) {
|
|
||||||
JsExpression property = findByIdent(collectJsProperties(searchRoot), name);
|
|
||||||
assert property != null: "Property `" + name + "` was not found";
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression getMetadataOrFunction(@NotNull JsNode searchRoot, @NotNull String name) {
|
public static JsExpression getMetadataOrFunction(@NotNull JsNode searchRoot, @NotNull String name) {
|
||||||
JsExpression property = findByIdent(CollectUtilsKt.collectNamedFunctionsOrMetadata(searchRoot), name);
|
JsExpression property = findByIdent(CollectUtilsKt.collectNamedFunctionsOrMetadata(searchRoot), name);
|
||||||
|
|||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* 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.dart.compiler.backend.js.ast.*
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
|
||||||
|
|
||||||
|
internal class DeclarationExporter(val context: StaticContext) {
|
||||||
|
private val objectLikeKinds = setOf(ClassKind.OBJECT, ClassKind.ENUM_ENTRY)
|
||||||
|
private val exportedDeclarations = mutableSetOf<MemberDescriptor>()
|
||||||
|
private val localPackageNames = mutableMapOf<FqName, JsName>()
|
||||||
|
val statements = mutableListOf<JsStatement>()
|
||||||
|
|
||||||
|
fun export(descriptor: MemberDescriptor, force: Boolean) {
|
||||||
|
if (exportedDeclarations.contains(descriptor)) return
|
||||||
|
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) return
|
||||||
|
|
||||||
|
val suggestedName = context.nameSuggestion.suggest(descriptor) ?: return
|
||||||
|
|
||||||
|
val container = suggestedName.scope
|
||||||
|
if (!descriptor.shouldBeExported(force)) return
|
||||||
|
exportedDeclarations.add(descriptor)
|
||||||
|
|
||||||
|
val qualifier = when (container) {
|
||||||
|
is PackageFragmentDescriptor -> getLocalPackageReference(container.fqName)
|
||||||
|
else -> context.getInnerNameForDescriptor(container).makeRef()
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
descriptor is ClassDescriptor && descriptor.kind in objectLikeKinds -> {
|
||||||
|
exportObject(descriptor, qualifier)
|
||||||
|
}
|
||||||
|
descriptor is PropertyDescriptor && container is PackageFragmentDescriptor -> {
|
||||||
|
exportProperty(descriptor, qualifier)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
assign(descriptor, qualifier, context.getInnerNameForDescriptor(descriptor).makeRef())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assign(descriptor: DeclarationDescriptor, qualifier: JsExpression, expression: JsExpression) {
|
||||||
|
val propertyName = context.getNameForDescriptor(descriptor)
|
||||||
|
if (propertyName.staticRef == null) {
|
||||||
|
if (expression !is JsNameRef || expression.name !== propertyName) {
|
||||||
|
propertyName.staticRef = expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statements += assignment(JsNameRef(propertyName, qualifier), expression).makeStmt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exportObject(declaration: ClassDescriptor, qualifier: JsExpression) {
|
||||||
|
val name = context.getNameForDescriptor(declaration)
|
||||||
|
statements += JsAstUtils.defineGetter(context.program, qualifier, name.ident,
|
||||||
|
context.getNameForObjectInstance(declaration).makeRef())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exportProperty(declaration: PropertyDescriptor, qualifier: JsExpression) {
|
||||||
|
val propertyLiteral = JsObjectLiteral(true)
|
||||||
|
|
||||||
|
val name = context.getNameForDescriptor(declaration).ident
|
||||||
|
|
||||||
|
val getterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
|
||||||
|
val accessToField = JsReturn(context.getInnerNameForDescriptor(declaration).makeRef())
|
||||||
|
JsFunction(context.rootFunction.scope, JsBlock(accessToField), "$declaration getter")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.getInnerNameForDescriptor(declaration.getter!!).makeRef()
|
||||||
|
}
|
||||||
|
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("get"), getterBody)
|
||||||
|
|
||||||
|
if (declaration.isVar) {
|
||||||
|
val setterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
|
||||||
|
val statements = mutableListOf<JsStatement>()
|
||||||
|
val function = JsFunction(context.rootFunction.scope, JsBlock(statements), "$declaration setter")
|
||||||
|
val valueName = function.scope.declareFreshName("value")
|
||||||
|
function.parameters += JsParameter(valueName)
|
||||||
|
statements += assignment(context.getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt()
|
||||||
|
function
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.getInnerNameForDescriptor(declaration.setter!!).makeRef()
|
||||||
|
}
|
||||||
|
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("set"), setterBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
statements += JsAstUtils.defineProperty(qualifier, name, propertyLiteral, context.program).makeStmt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLocalPackageReference(packageName: FqName): JsExpression {
|
||||||
|
if (packageName.isRoot) {
|
||||||
|
return context.rootFunction.scope.declareName(Namer.getRootPackageName()).makeRef()
|
||||||
|
}
|
||||||
|
var name = localPackageNames[packageName]
|
||||||
|
if (name == null) {
|
||||||
|
name = context.rootFunction.scope.declareFreshName("package$" + packageName.shortName().asString())
|
||||||
|
localPackageNames.put(packageName, name)
|
||||||
|
|
||||||
|
val parentRef = getLocalPackageReference(packageName.parent())
|
||||||
|
val selfRef = JsNameRef(packageName.shortName().asString(), parentRef)
|
||||||
|
val rhs = JsAstUtils.or(selfRef, assignment(selfRef.deepCopy(), JsObjectLiteral(false)))
|
||||||
|
|
||||||
|
statements.add(JsAstUtils.newVar(name, rhs))
|
||||||
|
}
|
||||||
|
return name.makeRef()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MemberDescriptor.shouldBeExported(force: Boolean) =
|
||||||
|
force || isEffectivelyPublicApi || AnnotationsUtils.getJsNameAnnotation(this) != null
|
||||||
@@ -139,17 +139,11 @@ public final class StaticContext {
|
|||||||
private final List<JsStatement> importStatements = new ArrayList<JsStatement>();
|
private final List<JsStatement> importStatements = new ArrayList<JsStatement>();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final List<JsStatement> exportStatements = new ArrayList<JsStatement>();
|
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Set<ClassDescriptor> classes = new HashSet<ClassDescriptor>();
|
private final Set<ClassDescriptor> classes = new HashSet<ClassDescriptor>();
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Set<MemberDescriptor> exportedDeclarations = new HashSet<MemberDescriptor>();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Map<FqName, JsName> localPackageNames = new HashMap<FqName, JsName>();
|
|
||||||
|
|
||||||
//TODO: too many parameters in constructor
|
//TODO: too many parameters in constructor
|
||||||
private StaticContext(
|
private StaticContext(
|
||||||
@NotNull JsProgram program,
|
@NotNull JsProgram program,
|
||||||
@@ -649,53 +643,7 @@ public final class StaticContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void export(@NotNull MemberDescriptor descriptor, boolean force) {
|
public void export(@NotNull MemberDescriptor descriptor, boolean force) {
|
||||||
if (exportedDeclarations.contains(descriptor)) return;
|
exporter.export(descriptor, force);
|
||||||
|
|
||||||
if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary()) return;
|
|
||||||
|
|
||||||
SuggestedName suggestedName = nameSuggestion.suggest(descriptor);
|
|
||||||
if (suggestedName == null) return;
|
|
||||||
|
|
||||||
DeclarationDescriptor container = suggestedName.getScope();
|
|
||||||
if (!DeclarationExporter.shouldBeExported(descriptor, force)) return;
|
|
||||||
exportedDeclarations.add(descriptor);
|
|
||||||
|
|
||||||
if (container instanceof PackageFragmentDescriptor) {
|
|
||||||
JsExpression packageRef = getLocalPackageReference(((PackageFragmentDescriptor) container).getFqName());
|
|
||||||
|
|
||||||
JsExpression initializerExpr = DeclarationExporter.exportDeclaration(this, descriptor, exportStatements);
|
|
||||||
if (initializerExpr != null) {
|
|
||||||
JsName propertyName = getNameForDescriptor(descriptor);
|
|
||||||
if (MetadataProperties.getStaticRef(propertyName) == null) {
|
|
||||||
if (!(initializerExpr instanceof JsNameRef) || ((JsNameRef) initializerExpr).getName() != propertyName) {
|
|
||||||
MetadataProperties.setStaticRef(propertyName, initializerExpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exportStatements.add(JsAstUtils.assignment(new JsNameRef(propertyName, packageRef), initializerExpr).makeStmt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
JsExpression initializerExpr = DeclarationExporter.exportDeclaration(this, descriptor, exportStatements);
|
|
||||||
assert initializerExpr == null : "Should not produce initializer for non-package declaration";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsExpression getLocalPackageReference(FqName packageName) {
|
|
||||||
if (packageName.isRoot()) {
|
|
||||||
return rootFunction.getScope().declareName(Namer.getRootPackageName()).makeRef();
|
|
||||||
}
|
|
||||||
JsName name = localPackageNames.get(packageName);
|
|
||||||
if (name == null) {
|
|
||||||
name = rootFunction.getScope().declareFreshName("package$" + packageName.shortName().asString());
|
|
||||||
localPackageNames.put(packageName, name);
|
|
||||||
|
|
||||||
JsExpression parentRef = getLocalPackageReference(packageName.parent());
|
|
||||||
JsExpression selfRef = new JsNameRef(packageName.shortName().asString(), parentRef);
|
|
||||||
JsExpression rhs = JsAstUtils.or(selfRef, JsAstUtils.assignment(selfRef.deepCopy(), new JsObjectLiteral(false)));
|
|
||||||
|
|
||||||
exportStatements.add(JsAstUtils.newVar(name, rhs));
|
|
||||||
}
|
|
||||||
return name.makeRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -713,7 +661,7 @@ public final class StaticContext {
|
|||||||
rootFunction.getBody().getStatements().addAll(importStatements);
|
rootFunction.getBody().getStatements().addAll(importStatements);
|
||||||
addClassPrototypes();
|
addClassPrototypes();
|
||||||
rootFunction.getBody().getStatements().addAll(declarationStatements);
|
rootFunction.getBody().getStatements().addAll(declarationStatements);
|
||||||
rootFunction.getBody().getStatements().addAll(exportStatements);
|
rootFunction.getBody().getStatements().addAll(exporter.getStatements());
|
||||||
rootFunction.getBody().getStatements().addAll(topLevelStatements);
|
rootFunction.getBody().getStatements().addAll(topLevelStatements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:JvmName("DeclarationExporter")
|
|
||||||
package org.jetbrains.kotlin.js.translate.context
|
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
|
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
fun StaticContext.exportDeclaration(declaration: MemberDescriptor, additionalStatements: MutableList<in JsStatement>): JsExpression? {
|
|
||||||
val scope = nameSuggestion.suggest(declaration)!!.scope
|
|
||||||
return when {
|
|
||||||
declaration is ClassDescriptor -> when {
|
|
||||||
declaration.kind == ClassKind.OBJECT || declaration.kind == ClassKind.ENUM_ENTRY -> {
|
|
||||||
exportObject(declaration, scope, additionalStatements)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
scope is ClassDescriptor -> {
|
|
||||||
exportNested(declaration, scope, additionalStatements)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
getInnerNameForDescriptor(declaration).makeRef()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declaration is PropertyDescriptor -> {
|
|
||||||
if (scope is PackageFragmentDescriptor) {
|
|
||||||
exportProperty(declaration, scope, additionalStatements)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
scope is PackageFragmentDescriptor -> {
|
|
||||||
getInnerNameForDescriptor(declaration).makeRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MemberDescriptor.shouldBeExported(force: Boolean) =
|
|
||||||
force || isEffectivelyPublicApi || AnnotationsUtils.getJsNameAnnotation(this) != null
|
|
||||||
|
|
||||||
private fun StaticContext.exportNested(
|
|
||||||
descriptor: ClassDescriptor,
|
|
||||||
scope: ClassDescriptor,
|
|
||||||
additionalStatements: MutableList<in JsStatement>
|
|
||||||
) {
|
|
||||||
val qualifier = getBaseReference(scope)
|
|
||||||
val reference = JsAstUtils.pureFqn(getNameForDescriptor(descriptor), qualifier)
|
|
||||||
val instanceRef = JsAstUtils.pureFqn(getInnerNameForDescriptor(descriptor), null)
|
|
||||||
additionalStatements += JsAstUtils.assignment(reference, instanceRef).makeStmt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun StaticContext.exportObject(
|
|
||||||
declaration: ClassDescriptor,
|
|
||||||
scope: DeclarationDescriptor,
|
|
||||||
additionalStatements: MutableList<in JsStatement>
|
|
||||||
) {
|
|
||||||
val qualifier = getBaseReference(scope)
|
|
||||||
val name = getNameForDescriptor(declaration)
|
|
||||||
additionalStatements += JsAstUtils.defineGetter(program, qualifier, name.ident, getNameForObjectInstance(declaration).makeRef())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun StaticContext.getBaseReference(declaration: DeclarationDescriptor) = when (declaration) {
|
|
||||||
is PackageFragmentDescriptor -> getQualifiedReference(declaration)
|
|
||||||
else -> JsAstUtils.pureFqn(getInnerNameForDescriptor(declaration), null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun StaticContext.exportProperty(
|
|
||||||
declaration: PropertyDescriptor,
|
|
||||||
scope: DeclarationDescriptor,
|
|
||||||
additionalStatements: MutableList<in JsStatement>
|
|
||||||
) {
|
|
||||||
val propertyLiteral = JsObjectLiteral(true)
|
|
||||||
|
|
||||||
val qualifier = getBaseReference(scope)
|
|
||||||
val name = getNameForDescriptor(declaration).ident
|
|
||||||
|
|
||||||
val getterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
|
|
||||||
val accessToField = JsReturn(getInnerNameForDescriptor(declaration).makeRef())
|
|
||||||
JsFunction(rootFunction.scope, JsBlock(accessToField), "$declaration getter")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
getInnerNameForDescriptor(declaration.getter!!).makeRef()
|
|
||||||
}
|
|
||||||
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("get"), getterBody)
|
|
||||||
|
|
||||||
if (declaration.isVar) {
|
|
||||||
val setterBody: JsExpression = if (JsDescriptorUtils.isSimpleFinalProperty(declaration)) {
|
|
||||||
val statements = mutableListOf<JsStatement>()
|
|
||||||
val function = JsFunction(rootFunction.scope, JsBlock(statements), "$declaration setter")
|
|
||||||
val valueName = function.scope.declareFreshName("value")
|
|
||||||
function.parameters += JsParameter(valueName)
|
|
||||||
statements += JsAstUtils.assignment(getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt()
|
|
||||||
function
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
getInnerNameForDescriptor(declaration.setter!!).makeRef()
|
|
||||||
}
|
|
||||||
propertyLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef("set"), setterBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
additionalStatements += JsAstUtils.defineProperty(qualifier, name, propertyLiteral, program).makeStmt()
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user