From cf31e544eb8204c946ce095aa47717d27c23619a Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Fri, 20 Apr 2018 19:07:32 +0300 Subject: [PATCH] Implement default interface members --- .../org/jetbrains/kotlin/ir/util/IrUtils.kt | 106 ++++++++++++------ .../irToJs/IrModuleToJsTransformer.kt | 31 ++++- .../transformers/irToJs/JsClassGenerator.kt | 82 +++++++++----- .../ir/backend/js/utils/JsStaticContext.kt | 3 + 4 files changed, 159 insertions(+), 63 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 7336b488ffe..01ba1e9d049 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -114,18 +114,18 @@ fun IrMemberAccessExpression.addArguments(args: Map>) = - 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() - .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() + .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 get() = this.owner.declarations.asSequence().filterIsInstance().map { it.symbol } private fun IrClassSymbol.getPropertyDeclaration(name: String) = - this.owner.declarations.filterIsInstance() - .atMostOne { it.descriptor.name == Name.identifier(name) } + this.owner.declarations.filterIsInstance() + .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 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() + val realOverrides = mutableSetOf() + + 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 } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 40c792c30bf..5d31092d466 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -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 { 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() + + for (name in staticContext.classModels.keys) { + addPostDeclaration(name, visited, block.statements, staticContext.classModels) + } + } + + private fun addPostDeclaration(name: JsName, visited: MutableSet, statements: MutableList, classModels: Map) { + if (visited.add(name)) { + classModels[name]?.run { + superName?.let { addPostDeclaration(it, visited, statements, classModels) } + interfaces.forEach { addPostDeclaration(it, visited, statements, classModels) } + + statements += postDeclarationBlock.statements + } + } + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index db00af727b0..b714ef54382 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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 { 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 { - val baseClass = irClass.superClasses.first { it.kind != ClassKind.INTERFACE } - if (baseClass.isAny) { + if (baseClass == null || baseClass.isAny) { return emptyList() } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index 4173b6c7615..8dd76ee3253 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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() fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context) } \ No newline at end of file