diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt index 59dad9926eb..fbd0cb9a6bb 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/descriptorUtils/descriptorUtils.kt @@ -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 diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java index 6858b7bec3a..db9bcdfda4d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java @@ -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); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt new file mode 100644 index 00000000000..669624ad78c --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt @@ -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() + private val localPackageNames = mutableMapOf() + val statements = mutableListOf() + + 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() + 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 diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 899a799ab5f..4ccb699168e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -139,17 +139,11 @@ public final class StaticContext { private final List importStatements = new ArrayList(); @NotNull - private final List exportStatements = new ArrayList(); + private final DeclarationExporter exporter = new DeclarationExporter(this); @NotNull private final Set classes = new HashSet(); - @NotNull - private final Set exportedDeclarations = new HashSet(); - - @NotNull - private final Map localPackageNames = new HashMap(); - //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); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt deleted file mode 100644 index 47636d4ec69..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/exportDeclaration.kt +++ /dev/null @@ -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): 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 -) { - 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 -) { - 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 -) { - 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() - 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() -} \ No newline at end of file