JS: rewrite code that copies interface members with implementation to subtypes
This commit is contained in:
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.js.naming.NameSuggestion;
|
|||||||
import org.jetbrains.kotlin.js.naming.SuggestedName;
|
import org.jetbrains.kotlin.js.naming.SuggestedName;
|
||||||
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
|
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
|
||||||
import org.jetbrains.kotlin.js.translate.context.generator.Rule;
|
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.intrinsic.Intrinsics;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
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.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
|
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
import org.jetbrains.kotlin.utils.DFS;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -742,17 +741,7 @@ public final class StaticContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addInterfaceDefaultMethods() {
|
private void addInterfaceDefaultMethods() {
|
||||||
List<ClassDescriptor> orderedClasses = DFS.topologicalOrder(classes, new DFS.Neighbors<ClassDescriptor>() {
|
new InterfaceFunctionCopier(this).copyInterfaceFunctions(classes);
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterable<? extends ClassDescriptor> getNeighbors(ClassDescriptor current) {
|
|
||||||
return DescriptorUtils.getSuperclassDescriptors(current);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Collections.reverse(orderedClasses);
|
|
||||||
for (ClassDescriptor classDescriptor : orderedClasses) {
|
|
||||||
ClassTranslator.addInterfaceDefaultMembers(classDescriptor, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBuiltinModule() {
|
public boolean isBuiltinModule() {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType
|
import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForTypeConstructor
|
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.CommonSupertypes.topologicallySortSuperclassesAndRecordAllInstances
|
||||||
import org.jetbrains.kotlin.types.SimpleType
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
import org.jetbrains.kotlin.types.TypeConstructor
|
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) {
|
@JvmStatic fun translate(classDeclaration: KtEnumEntry, context: TranslationContext, enumInitializerName: JsName, ordinal: Int) {
|
||||||
return ClassTranslator(classDeclaration, context, enumInitializerName, ordinal).translate()
|
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<JsName> {
|
|
||||||
return (generateBridges(member) + member)
|
|
||||||
.map { context.getNameForDescriptor(it) }
|
|
||||||
.distinctBy { it.ident }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateBridges(member: CallableMemberDescriptor): Sequence<CallableMemberDescriptor> = 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(
|
private class ConstructorInfo(
|
||||||
|
|||||||
+133
@@ -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<ClassDescriptor, ClassModel>()
|
||||||
|
|
||||||
|
fun copyInterfaceFunctions(classes: Collection<ClassDescriptor>) {
|
||||||
|
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<JsName> {
|
||||||
|
return (generateBridges(member) + member).map { context.getNameForDescriptor(it) }.distinctBy { it.ident }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateBridges(member: CallableMemberDescriptor): Sequence<CallableMemberDescriptor> = 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<String>()
|
||||||
|
val copiedProperties = mutableSetOf<String>()
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -12,7 +12,7 @@ fun checkJsNames(base: String, o: Any) {
|
|||||||
|
|
||||||
fun getAllProperties(o: dynamic): Array<String> = js("""
|
fun getAllProperties(o: dynamic): Array<String> = js("""
|
||||||
var properties = [];
|
var properties = [];
|
||||||
for (property in o) {
|
for (var property in o) {
|
||||||
properties.push(property);
|
properties.push(property);
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
|
|||||||
Reference in New Issue
Block a user