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.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<ClassDescriptor> orderedClasses = DFS.topologicalOrder(classes, new DFS.Neighbors<ClassDescriptor>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterable<? extends ClassDescriptor> 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() {
|
||||
|
||||
@@ -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<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(
|
||||
|
||||
+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("""
|
||||
var properties = [];
|
||||
for (property in o) {
|
||||
for (var property in o) {
|
||||
properties.push(property);
|
||||
}
|
||||
return properties;
|
||||
|
||||
Reference in New Issue
Block a user