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.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.js.inline.util.CollectUtilsKt;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.collectNamedFunctions;
|
||||
import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.collectJsProperties;
|
||||
|
||||
public class AstSearchUtil {
|
||||
@NotNull
|
||||
@@ -37,13 +36,6 @@ public class AstSearchUtil {
|
||||
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
|
||||
public static JsExpression getMetadataOrFunction(@NotNull JsNode searchRoot, @NotNull String 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>();
|
||||
|
||||
@NotNull
|
||||
private final List<JsStatement> exportStatements = new ArrayList<JsStatement>();
|
||||
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
||||
|
||||
@NotNull
|
||||
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
|
||||
private StaticContext(
|
||||
@NotNull JsProgram program,
|
||||
@@ -649,53 +643,7 @@ public final class StaticContext {
|
||||
}
|
||||
|
||||
public void export(@NotNull MemberDescriptor descriptor, boolean force) {
|
||||
if (exportedDeclarations.contains(descriptor)) return;
|
||||
|
||||
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();
|
||||
exporter.export(descriptor, force);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -713,7 +661,7 @@ public final class StaticContext {
|
||||
rootFunction.getBody().getStatements().addAll(importStatements);
|
||||
addClassPrototypes();
|
||||
rootFunction.getBody().getStatements().addAll(declarationStatements);
|
||||
rootFunction.getBody().getStatements().addAll(exportStatements);
|
||||
rootFunction.getBody().getStatements().addAll(exporter.getStatements());
|
||||
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