From 0f87703c87779e7d5668bee73eef693d447a2f5d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 24 Oct 2016 13:20:49 +0300 Subject: [PATCH] JS: rewrite code that copies interface members with implementation to subtypes --- .../js/translate/context/StaticContext.java | 15 +- .../translate/declaration/ClassTranslator.kt | 84 ----------- .../declaration/InterfaceFunctionCopier.kt | 133 ++++++++++++++++++ .../testData/box/multiModule/_common.kt | 2 +- 4 files changed, 136 insertions(+), 98 deletions(-) create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt 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 778b1726062..9a6362e2d64 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 @@ -34,7 +34,7 @@ import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.SuggestedName; 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.ClassTranslator; +import org.jetbrains.kotlin.js.translate.declaration.InterfaceFunctionCopier; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.name.FqName; @@ -45,7 +45,6 @@ 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.utils.DFS; import java.util.*; @@ -742,17 +741,7 @@ public final class StaticContext { } private void addInterfaceDefaultMethods() { - List orderedClasses = DFS.topologicalOrder(classes, new DFS.Neighbors() { - @NotNull - @Override - public Iterable getNeighbors(ClassDescriptor current) { - return DescriptorUtils.getSuperclassDescriptors(current); - } - }); - Collections.reverse(orderedClasses); - for (ClassDescriptor classDescriptor : orderedClasses) { - ClassTranslator.addInterfaceDefaultMembers(classDescriptor, this); - } + new InterfaceFunctionCopier(this).copyInterfaceFunctions(classes); } public boolean isBuiltinModule() { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 4f491d36b34..b23d3bf0ae0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -44,7 +44,6 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForTypeConstructor -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.TypeConstructor @@ -497,89 +496,6 @@ class ClassTranslator private constructor( @JvmStatic fun translate(classDeclaration: KtEnumEntry, context: TranslationContext, enumInitializerName: JsName, ordinal: Int) { return ClassTranslator(classDeclaration, context, enumInitializerName, ordinal).translate() } - - @JvmStatic fun addInterfaceDefaultMembers(descriptor: ClassDescriptor, context: StaticContext) { - val members = descriptor.unsubstitutedMemberScope - .getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) - .mapNotNull { it as? CallableMemberDescriptor } - for (member in members) { - if (member.kind.isReal) continue - if (member.modality == Modality.ABSTRACT || member.overriddenDescriptors.size != 1) continue - - val overriddenMember = generateSequence(member) { it.overriddenDescriptors.firstOrNull() } - .drop(1) - .dropWhile { !it.kind.isReal && DescriptorUtils.isInterface(it.containingDeclaration) } - .firstOrNull() ?: continue - - if (overriddenMember.modality == Modality.ABSTRACT) continue - - val interfaceDescriptor = overriddenMember.containingDeclaration as ClassDescriptor - if (interfaceDescriptor.kind != ClassKind.INTERFACE) continue - - val directContainer = member.overriddenDescriptors.first().containingDeclaration as ClassDescriptor - - when (overriddenMember) { - is FunctionDescriptor -> { - addDefaultMethodFromInterface(overriddenMember, directContainer, descriptor,context) - } - is PropertyDescriptor -> { - addDefaultPropertyFromInterface(overriddenMember, directContainer, descriptor, context) - } - } - } - } - - private fun addDefaultMethodFromInterface( - function: FunctionDescriptor, - interfaceDescriptor: ClassDescriptor, - descriptor: ClassDescriptor, - context: StaticContext - ) { - for (name in generateAllNames(function, context)) { - val classPrototype = JsAstUtils.prototypeOf(pureFqn(context.getInnerNameForDescriptor(descriptor), null)) - val interfacePrototype = JsAstUtils.prototypeOf(pureFqn(context.getInnerNameForDescriptor(interfaceDescriptor), null)) - val classFunction = JsNameRef(name, classPrototype) - val interfaceFunction = JsNameRef(name, interfacePrototype) - context.declarationStatements += JsAstUtils.assignment(classFunction, interfaceFunction).makeStmt() - } - } - - private fun addDefaultPropertyFromInterface( - property: PropertyDescriptor, - interfaceDescriptor: ClassDescriptor, - descriptor: ClassDescriptor, - context: StaticContext - ) { - for (name in generateAllNames(property, context)) { - val classPrototype = JsAstUtils.prototypeOf(pureFqn(context.getInnerNameForDescriptor(descriptor), null)) - val interfacePrototype = JsAstUtils.prototypeOf(pureFqn(context.getInnerNameForDescriptor(interfaceDescriptor), null)) - val nameLiteral = context.program.getStringLiteral(name.ident) - - val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), interfacePrototype, nameLiteral) - val defineProperty = JsAstUtils.defineProperty(classPrototype, name.ident, getPropertyDescriptor, context.program) - - context.declarationStatements += defineProperty.makeStmt() - } - } - - private fun generateAllNames(member: CallableMemberDescriptor, context: StaticContext): Sequence { - return (generateBridges(member) + member) - .map { context.getNameForDescriptor(it) } - .distinctBy { it.ident } - } - - private fun generateBridges(member: CallableMemberDescriptor): Sequence = when (member) { - is FunctionDescriptor -> { - generateBridgesForFunctionDescriptor(member, identity()) { false } - .map { it.from } - .asSequence() - } - is PropertyDescriptor -> generateBridgesForFunctionDescriptor(member.getter!!, identity()) { false } - .map { it.from } - .map { (it as PropertyAccessorDescriptor).correspondingProperty } - .asSequence() - else -> error("Expected either be function or property: $member") - } } private class ConstructorInfo( diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt new file mode 100644 index 00000000000..781d11aaf32 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt @@ -0,0 +1,133 @@ +/* + * 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.declaration + +import com.google.dart.compiler.backend.js.ast.JsInvocation +import com.google.dart.compiler.backend.js.ast.JsName +import com.google.dart.compiler.backend.js.ast.JsNameRef +import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.translate.context.StaticContext +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.prototypeOf +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.identity + +class InterfaceFunctionCopier(val context: StaticContext) { + private val classModels = mutableMapOf() + + fun copyInterfaceFunctions(classes: Collection) { + val orderedClasses = DFS.topologicalOrder(classes) { current -> DescriptorUtils.getSuperclassDescriptors(current) }.asReversed() + + for (classDescriptor in orderedClasses) { + addInterfaceDefaultMembers(classDescriptor, context) + } + } + + private fun addInterfaceDefaultMembers(descriptor: ClassDescriptor, context: StaticContext) { + val classModel = ClassModel(descriptor) + classModels[descriptor] = classModel + + val members = descriptor.unsubstitutedMemberScope + .getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) + .mapNotNull { it as? CallableMemberDescriptor } + + for (member in members.filter { it.modality != Modality.ABSTRACT && it.kind.isReal }) { + val names = generateAllNames(member, context) + val identifiers = names.map { it.ident } + when (member) { + is FunctionDescriptor -> classModel.copiedFunctions += identifiers + is PropertyDescriptor -> classModel.copiedProperties += identifiers + } + } + + val superModels = DescriptorUtils.getSuperclassDescriptors(descriptor) + .filter { it.kind == ClassKind.INTERFACE } + .map { classModels[it]!! } + for (superModel in superModels) { + for (name in superModel.copiedFunctions) { + if (classModel.copiedFunctions.add(name)) { + addDefaultMethodFromInterface(name, superModel.descriptor, descriptor, context) + } + } + for (name in superModel.copiedProperties) { + if (classModel.copiedProperties.add(name)) { + addDefaultPropertyFromInterface(name, superModel.descriptor, descriptor, context) + } + } + } + } + + private fun addDefaultMethodFromInterface( + name: String, + sourceDescriptor: ClassDescriptor, + targetDescriptor: ClassDescriptor, + context: StaticContext + ) { + if (targetDescriptor.module != context.currentModule) return + + val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) + val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) + val targetFunction = JsNameRef(name, targetPrototype) + val sourceFunction = JsNameRef(name, sourcePrototype) + context.declarationStatements += JsAstUtils.assignment(targetFunction, sourceFunction).makeStmt() + } + + private fun addDefaultPropertyFromInterface( + name: String, + sourceDescriptor: ClassDescriptor, + targetDescriptor: ClassDescriptor, + context: StaticContext + ) { + if (targetDescriptor.module != context.currentModule) return + + val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) + val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) + val nameLiteral = context.program.getStringLiteral(name) + + val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), sourcePrototype, nameLiteral) + val defineProperty = JsAstUtils.defineProperty(targetPrototype, name, getPropertyDescriptor, context.program) + + context.declarationStatements += defineProperty.makeStmt() + } + + private fun generateAllNames(member: CallableMemberDescriptor, context: StaticContext): Sequence { + return (generateBridges(member) + member).map { context.getNameForDescriptor(it) }.distinctBy { it.ident } + } + + private fun generateBridges(member: CallableMemberDescriptor): Sequence = when (member) { + is FunctionDescriptor -> { + generateBridgesForFunctionDescriptor(member, identity()) { false } + .map { it.from } + .asSequence() + } + is PropertyDescriptor -> generateBridgesForFunctionDescriptor(member.getter!!, identity()) { false } + .map { it.from } + .map { (it as PropertyAccessorDescriptor).correspondingProperty } + .asSequence() + else -> error("Expected either be function or property: $member") + } + + private class ClassModel(val descriptor: ClassDescriptor) { + val copiedFunctions = mutableSetOf() + val copiedProperties = mutableSetOf() + } +} \ No newline at end of file diff --git a/js/js.translator/testData/box/multiModule/_common.kt b/js/js.translator/testData/box/multiModule/_common.kt index 8cc2d5747cf..d4c7a425717 100644 --- a/js/js.translator/testData/box/multiModule/_common.kt +++ b/js/js.translator/testData/box/multiModule/_common.kt @@ -12,7 +12,7 @@ fun checkJsNames(base: String, o: Any) { fun getAllProperties(o: dynamic): Array = js(""" var properties = []; - for (property in o) { + for (var property in o) { properties.push(property); } return properties;