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>>) =
this.addArguments(args.toMap())
this.addArguments(args.toMap())
fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
fun IrMemberAccessExpression.usesDefaultArguments(): Boolean =
this.descriptor.valueParameters.any { this.getValueArgument(it) == null}
this.descriptor.valueParameters.any { this.getValueArgument(it) == null }
fun IrFunction.createParameterDeclarations() {
fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl(
innerStartOffset(this), innerEndOffset(this),
IrDeclarationOrigin.DEFINED,
this
innerStartOffset(this), innerEndOffset(this),
IrDeclarationOrigin.DEFINED,
this
).also {
it.parent = this@createParameterDeclarations
}
@@ -139,9 +139,9 @@ fun IrFunction.createParameterDeclarations() {
assert(typeParameters.isEmpty())
descriptor.typeParameters.mapTo(typeParameters) {
IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
).also { typeParameter ->
typeParameter.parent = this
}
@@ -151,18 +151,18 @@ fun IrFunction.createParameterDeclarations() {
fun IrClass.createParameterDeclarations() {
descriptor.thisAsReceiverParameter.let {
thisReceiver = IrValueParameterImpl(
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.INSTANCE_RECEIVER,
it
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.INSTANCE_RECEIVER,
it
)
}
assert(typeParameters.isEmpty())
descriptor.declaredTypeParameters.mapTo(typeParameters) {
IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
)
}
}
@@ -173,34 +173,34 @@ fun IrClass.addFakeOverrides() {
val endOffset = this.endOffset
fun FunctionDescriptor.createFunction(): IrFunction = IrFunctionImpl(
startOffset, endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE, this
startOffset, endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE, this
).apply {
createParameterDeclarations()
}
descriptor.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
.mapTo(this.declarations) {
when (it) {
is FunctionDescriptor -> it.createFunction()
is PropertyDescriptor ->
IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, it).apply {
// TODO: add field if getter is missing?
getter = it.getter?.createFunction()
setter = it.setter?.createFunction()
}
else -> TODO(it.toString())
}
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
.mapTo(this.declarations) {
when (it) {
is FunctionDescriptor -> it.createFunction()
is PropertyDescriptor ->
IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, it).apply {
// TODO: add field if getter is missing?
getter = it.getter?.createFunction()
setter = it.setter?.createFunction()
}
else -> TODO(it.toString())
}
}
}
private fun IrElement.innerStartOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.startOffset ?: this.startOffset
descriptor.startOffset ?: this.startOffset
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.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 }
private fun IrClassSymbol.getPropertyDeclaration(name: String) =
this.owner.declarations.filterIsInstance<IrProperty>()
.atMostOne { it.descriptor.name == Name.identifier(name) }
this.owner.declarations.filterIsInstance<IrProperty>()
.atMostOne { it.descriptor.name == Name.identifier(name) }
fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.getter?.symbol
this.getPropertyDeclaration(name)?.getter?.symbol
fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol
this.getPropertyDeclaration(name)?.setter?.symbol
val IrFunction.explicitParameters: List<IrValueParameterSymbol>
get() = (listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters).map { it.symbol }
@@ -232,3 +232,39 @@ val IrValueParameter.type: KotlinType
val IrClass.defaultType: KotlinType
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.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.JsNode
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
import org.jetbrains.kotlin.js.backend.ast.*
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
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))
}
// sort member forwarding code
addPostDeclarations(rootContext)
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
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.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass
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.*
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
private val className = context.getNameForSymbol(irClass.symbol)
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 classBlock = JsBlock()
private val classModel = JsClassModel(className, baseClassName)
fun generate(): JsStatement {
@@ -29,34 +34,62 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val transformer = IrFunctionToJsTransformer()
for (declaration in irClass.declarations) {
if (declaration is IrConstructor) {
classBlock.statements += declaration.accept(transformer, context).makeStmt()
classBlock.statements += generateInheritanceCode()
} else if (declaration is IrFunction) {
if (declaration.symbol.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
declaration.symbol.modality != Modality.ABSTRACT
) {
classBlock.statements += declaration.accept(transformer, context).let {
if (declaration.isStatic) {
it.makeStmt()
} else {
val memberName = context.getNameForSymbol(declaration.symbol)
val memberRef = JsNameRef(memberName, classPrototypeRef)
jsAssignment(memberRef, it).makeStmt()
}
}
when (declaration) {
is IrConstructor -> {
classBlock.statements += declaration.accept(transformer, context).makeStmt()
classBlock.statements += generateInheritanceCode()
}
is IrSimpleFunction -> {
generateMemberFunction(declaration)?.let { classBlock.statements += it }
}
is IrClass -> {
classBlock.statements += JsClassGenerator(declaration, context).generate()
}
else -> {
}
} else if (declaration is IrClass) {
//TODO: redesign inner classes generation
classBlock.statements += JsClassGenerator(declaration, context).generate()
}
}
classBlock.statements += generateClassMetadata()
irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() }
context.staticContext.classModels[className] = classModel
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> {
val instanceVarName = "${className.ident}_instance"
val getInstanceFunName = "${className.ident}_getInstance"
@@ -90,8 +123,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
private fun generateInheritanceCode(): List<JsStatement> {
val baseClass = irClass.superClasses.first { it.kind != ClassKind.INTERFACE }
if (baseClass.isAny) {
if (baseClass == null || baseClass.isAny) {
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.transformers.irToJs.JsIntrinsicTransformers
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.JsName
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
@@ -19,6 +21,7 @@ class JsStaticContext(
backendContext: JsIrBackendContext
) {
val intrinsics = JsIntrinsicTransformers(backendContext)
val classModels = mutableMapOf<JsName, JsClassModel>()
fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context)
}