JS: rewrite code that copies interface members with implementation to subtypes

This commit is contained in:
Alexey Andreev
2016-10-24 13:20:49 +03:00
parent 8eca8efae5
commit 0f87703c87
4 changed files with 136 additions and 98 deletions
@@ -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(
@@ -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
View File
@@ -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;