Implement default interface members

This commit is contained in:
Roman Artemev
2018-04-20 19:07:32 +03:00
committed by Roman Artemev
parent d861fb498f
commit cf31e544eb
4 changed files with 159 additions and 63 deletions
@@ -114,18 +114,18 @@ fun IrMemberAccessExpression.addArguments(args: Map<ParameterDescriptor, IrExpre
} }
fun IrMemberAccessExpression.addArguments(args: List<Pair<ParameterDescriptor, IrExpression>>) = fun IrMemberAccessExpression.addArguments(args: List<Pair<ParameterDescriptor, IrExpression>>) =
this.addArguments(args.toMap()) this.addArguments(args.toMap())
fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
fun IrMemberAccessExpression.usesDefaultArguments(): Boolean = fun IrMemberAccessExpression.usesDefaultArguments(): Boolean =
this.descriptor.valueParameters.any { this.getValueArgument(it) == null} this.descriptor.valueParameters.any { this.getValueArgument(it) == null }
fun IrFunction.createParameterDeclarations() { fun IrFunction.createParameterDeclarations() {
fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl( fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl(
innerStartOffset(this), innerEndOffset(this), innerStartOffset(this), innerEndOffset(this),
IrDeclarationOrigin.DEFINED, IrDeclarationOrigin.DEFINED,
this this
).also { ).also {
it.parent = this@createParameterDeclarations it.parent = this@createParameterDeclarations
} }
@@ -139,9 +139,9 @@ fun IrFunction.createParameterDeclarations() {
assert(typeParameters.isEmpty()) assert(typeParameters.isEmpty())
descriptor.typeParameters.mapTo(typeParameters) { descriptor.typeParameters.mapTo(typeParameters) {
IrTypeParameterImpl( IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it), innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED, IrDeclarationOrigin.DEFINED,
it it
).also { typeParameter -> ).also { typeParameter ->
typeParameter.parent = this typeParameter.parent = this
} }
@@ -151,18 +151,18 @@ fun IrFunction.createParameterDeclarations() {
fun IrClass.createParameterDeclarations() { fun IrClass.createParameterDeclarations() {
descriptor.thisAsReceiverParameter.let { descriptor.thisAsReceiverParameter.let {
thisReceiver = IrValueParameterImpl( thisReceiver = IrValueParameterImpl(
innerStartOffset(it), innerEndOffset(it), innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.INSTANCE_RECEIVER, IrDeclarationOrigin.INSTANCE_RECEIVER,
it it
) )
} }
assert(typeParameters.isEmpty()) assert(typeParameters.isEmpty())
descriptor.declaredTypeParameters.mapTo(typeParameters) { descriptor.declaredTypeParameters.mapTo(typeParameters) {
IrTypeParameterImpl( IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it), innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED, IrDeclarationOrigin.DEFINED,
it it
) )
} }
} }
@@ -173,34 +173,34 @@ fun IrClass.addFakeOverrides() {
val endOffset = this.endOffset val endOffset = this.endOffset
fun FunctionDescriptor.createFunction(): IrFunction = IrFunctionImpl( fun FunctionDescriptor.createFunction(): IrFunction = IrFunctionImpl(
startOffset, endOffset, startOffset, endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE, this IrDeclarationOrigin.FAKE_OVERRIDE, this
).apply { ).apply {
createParameterDeclarations() createParameterDeclarations()
} }
descriptor.unsubstitutedMemberScope.getContributedDescriptors() descriptor.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<CallableMemberDescriptor>() .filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE } .filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
.mapTo(this.declarations) { .mapTo(this.declarations) {
when (it) { when (it) {
is FunctionDescriptor -> it.createFunction() is FunctionDescriptor -> it.createFunction()
is PropertyDescriptor -> is PropertyDescriptor ->
IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, it).apply { IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, it).apply {
// TODO: add field if getter is missing? // TODO: add field if getter is missing?
getter = it.getter?.createFunction() getter = it.getter?.createFunction()
setter = it.setter?.createFunction() setter = it.setter?.createFunction()
} }
else -> TODO(it.toString()) else -> TODO(it.toString())
}
} }
}
} }
private fun IrElement.innerStartOffset(descriptor: DeclarationDescriptorWithSource): Int = private fun IrElement.innerStartOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.startOffset ?: this.startOffset descriptor.startOffset ?: this.startOffset
private fun IrElement.innerEndOffset(descriptor: DeclarationDescriptorWithSource): Int = private fun IrElement.innerEndOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.endOffset ?: this.endOffset descriptor.endOffset ?: this.endOffset
val DeclarationDescriptorWithSource.startOffset: Int? get() = (this.source as? PsiSourceElement)?.psi?.startOffset val DeclarationDescriptorWithSource.startOffset: Int? get() = (this.source as? PsiSourceElement)?.psi?.startOffset
val DeclarationDescriptorWithSource.endOffset: Int? get() = (this.source as? PsiSourceElement)?.psi?.endOffset val DeclarationDescriptorWithSource.endOffset: Int? get() = (this.source as? PsiSourceElement)?.psi?.endOffset
@@ -215,14 +215,14 @@ val IrClassSymbol.constructors: Sequence<IrConstructorSymbol>
get() = this.owner.declarations.asSequence().filterIsInstance<IrConstructor>().map { it.symbol } get() = this.owner.declarations.asSequence().filterIsInstance<IrConstructor>().map { it.symbol }
private fun IrClassSymbol.getPropertyDeclaration(name: String) = private fun IrClassSymbol.getPropertyDeclaration(name: String) =
this.owner.declarations.filterIsInstance<IrProperty>() this.owner.declarations.filterIsInstance<IrProperty>()
.atMostOne { it.descriptor.name == Name.identifier(name) } .atMostOne { it.descriptor.name == Name.identifier(name) }
fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.getter?.symbol this.getPropertyDeclaration(name)?.getter?.symbol
fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol this.getPropertyDeclaration(name)?.setter?.symbol
val IrFunction.explicitParameters: List<IrValueParameterSymbol> val IrFunction.explicitParameters: List<IrValueParameterSymbol>
get() = (listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters).map { it.symbol } get() = (listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters).map { it.symbol }
@@ -232,3 +232,39 @@ val IrValueParameter.type: KotlinType
val IrClass.defaultType: KotlinType val IrClass.defaultType: KotlinType
get() = this.descriptor.defaultType get() = this.descriptor.defaultType
val IrSimpleFunction.isReal: Boolean get() = descriptor.kind.isReal
fun IrSimpleFunction.resolveFakeOverride(): IrSimpleFunction? {
if (isReal) return this
val visited = mutableSetOf<IrSimpleFunction>()
val realOverrides = mutableSetOf<IrSimpleFunction>()
fun collectRealOverrides(func: IrSimpleFunction) {
if (!visited.add(func)) return
if (func.isReal) {
realOverrides += func
} else {
func.overriddenSymbols.forEach { collectRealOverrides(it.owner) }
}
}
overriddenSymbols.forEach { collectRealOverrides(it.owner) }
fun excludeRepeated(func: IrSimpleFunction) {
if (!visited.add(func)) return
func.overriddenSymbols.forEach {
realOverrides.remove(it.owner)
excludeRepeated(it.owner)
}
}
visited.clear()
realOverrides.asSequence().forEach { excludeRepeated(it) }
return realOverrides.singleOrNull { it.modality != Modality.ABSTRACT }
}
@@ -8,9 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.JsNode import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> { class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode { override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
@@ -21,6 +19,33 @@ class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrEl
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext)) program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
} }
// sort member forwarding code
addPostDeclarations(rootContext)
return program return program
} }
private fun addPostDeclarations(context: JsGenerationContext) {
val staticContext = context.staticContext
val program = context.currentScope.program
val block = program.globalBlock
val visited = mutableSetOf<JsName>()
for (name in staticContext.classModels.keys) {
addPostDeclaration(name, visited, block.statements, staticContext.classModels)
}
}
private fun addPostDeclaration(name: JsName, visited: MutableSet<JsName>, statements: MutableList<JsStatement>, classModels: Map<JsName, JsClassModel>) {
if (visited.add(name)) {
classModels[name]?.run {
superName?.let { addPostDeclaration(it, visited, statements, classModels) }
interfaces.forEach { addPostDeclaration(it, visited, statements, classModels) }
statements += postDeclarationBlock.statements
}
}
}
} }
@@ -6,22 +6,27 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.onlyIf import org.jetbrains.kotlin.backend.common.onlyIf
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) { class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
private val className = context.getNameForSymbol(irClass.symbol) private val className = context.getNameForSymbol(irClass.symbol)
private val classNameRef = className.makeRef() private val classNameRef = className.makeRef()
private val baseClass = irClass.superClasses.firstOrNull { it.kind != ClassKind.INTERFACE }
private val baseClassName = baseClass?.let { context.getNameForSymbol(it) }
private val classPrototypeRef = prototypeOf(classNameRef) private val classPrototypeRef = prototypeOf(classNameRef)
private val classBlock = JsBlock() private val classBlock = JsBlock()
private val classModel = JsClassModel(className, baseClassName)
fun generate(): JsStatement { fun generate(): JsStatement {
@@ -29,34 +34,62 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val transformer = IrFunctionToJsTransformer() val transformer = IrFunctionToJsTransformer()
for (declaration in irClass.declarations) { for (declaration in irClass.declarations) {
if (declaration is IrConstructor) { when (declaration) {
classBlock.statements += declaration.accept(transformer, context).makeStmt() is IrConstructor -> {
classBlock.statements += generateInheritanceCode() classBlock.statements += declaration.accept(transformer, context).makeStmt()
} else if (declaration is IrFunction) { classBlock.statements += generateInheritanceCode()
if (declaration.symbol.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE && }
declaration.symbol.modality != Modality.ABSTRACT is IrSimpleFunction -> {
) { generateMemberFunction(declaration)?.let { classBlock.statements += it }
classBlock.statements += declaration.accept(transformer, context).let { }
if (declaration.isStatic) { is IrClass -> {
it.makeStmt() classBlock.statements += JsClassGenerator(declaration, context).generate()
} else { }
val memberName = context.getNameForSymbol(declaration.symbol) else -> {
val memberRef = JsNameRef(memberName, classPrototypeRef)
jsAssignment(memberRef, it).makeStmt()
}
}
} }
} else if (declaration is IrClass) {
//TODO: redesign inner classes generation
classBlock.statements += JsClassGenerator(declaration, context).generate()
} }
} }
classBlock.statements += generateClassMetadata() classBlock.statements += generateClassMetadata()
irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() } irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() }
context.staticContext.classModels[className] = classModel
return classBlock return classBlock
} }
private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? {
val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null }
if (declaration.isStatic) {
return translatedFunction!!.makeStmt()
}
val memberName = context.getNameForSymbol(declaration.symbol)
val memberRef = JsNameRef(memberName, classPrototypeRef)
translatedFunction?.let { return jsAssignment(memberRef, it).makeStmt() }
// do not generate code like
// interface I { foo() = "OK" }
// interface II : I
// II.prototype.foo = I.prototype.foo
if (irClass.kind != ClassKind.INTERFACE) {
declaration.resolveFakeOverride()?.let {
val implClassDesc = it.descriptor.containingDeclaration as ClassDescriptor
if (!KotlinBuiltIns.isAny(implClassDesc)) {
val implMethodName = context.getNameForSymbol(it.symbol)
val implClassName = context.getNameForSymbol(IrClassSymbolImpl(implClassDesc))
val implClassPrototype = prototypeOf(implClassName.makeRef())
val implMemberRef = JsNameRef(implMethodName, implClassPrototype)
classModel.postDeclarationBlock.statements += jsAssignment(memberRef, implMemberRef).makeStmt()
}
}
}
return null
}
private fun maybeGenerateObjectInstance(): List<JsStatement> { private fun maybeGenerateObjectInstance(): List<JsStatement> {
val instanceVarName = "${className.ident}_instance" val instanceVarName = "${className.ident}_instance"
val getInstanceFunName = "${className.ident}_getInstance" val getInstanceFunName = "${className.ident}_getInstance"
@@ -90,8 +123,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
private fun generateInheritanceCode(): List<JsStatement> { private fun generateInheritanceCode(): List<JsStatement> {
val baseClass = irClass.superClasses.first { it.kind != ClassKind.INTERFACE } if (baseClass == null || baseClass.isAny) {
if (baseClass.isAny) {
return emptyList() return emptyList()
} }
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.JsClassModel
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
import org.jetbrains.kotlin.js.backend.ast.JsName
import org.jetbrains.kotlin.js.backend.ast.JsRootScope import org.jetbrains.kotlin.js.backend.ast.JsRootScope
@@ -19,6 +21,7 @@ class JsStaticContext(
backendContext: JsIrBackendContext backendContext: JsIrBackendContext
) { ) {
val intrinsics = JsIntrinsicTransformers(backendContext) val intrinsics = JsIntrinsicTransformers(backendContext)
val classModels = mutableMapOf<JsName, JsClassModel>()
fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context) fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context)
} }