diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsClassModel.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsClassModel.kt index 05fb1be5c76..13eed8b27cb 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsClassModel.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsClassModel.kt @@ -16,4 +16,6 @@ package org.jetbrains.kotlin.js.backend.ast -class JsClassModel(val name: JsName, val superName: JsName?) +class JsClassModel(val name: JsName, val superName: JsName?) { + val postDeclarationBlock = JsGlobalBlock() +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 12feb50f153..2b5a23ffeb7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -752,13 +752,4 @@ public final class StaticContext { String tag = Namer.getFunctionTag(descriptor); fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor)); } - - /*public void postProcess() { - addInterfaceDefaultMethods(); - rootFunction.getBody().getStatements().addAll(importStatements); - addClassPrototypes(); - rootFunction.getBody().getStatements().addAll(declarationStatements); - rootFunction.getBody().getStatements().addAll(exporter.getStatements()); - rootFunction.getBody().getStatements().addAll(topLevelStatements); - }*/ } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 8855122c60c..357397e62bd 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.js.translate.context; import com.intellij.psi.PsiElement; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; @@ -633,6 +635,19 @@ public class TranslationContext { staticContext.getDeclarationStatements().add(statement); } + private final Function1 declarationStatementConsumer = new Function1() { + @Override + public Unit invoke(JsStatement statement) { + addDeclarationStatement(statement); + return Unit.INSTANCE; + } + }; + + @NotNull + public Function1 getDeclarationStatementConsumer() { + return declarationStatementConsumer; + } + public void addTopLevelStatement(@NotNull JsStatement statement) { staticContext.getTopLevelStatements().add(statement); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt index 7748c85625c..89a2c16e03c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. + * 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. @@ -16,15 +16,255 @@ package org.jetbrains.kotlin.js.translate.declaration -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.js.backend.ast.JsClassModel +import org.jetbrains.kotlin.backend.common.CodegenUtil +import org.jetbrains.kotlin.backend.common.bridges.Bridge +import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.StaticContext +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils +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.js.translate.utils.generateDelegateCall +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue +import org.jetbrains.kotlin.resolve.descriptorUtil.hasOwnParametersWithDefaultValue +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.utils.identity class ClassModelGenerator(val context: StaticContext) { fun generateClassModel(descriptor: ClassDescriptor): JsClassModel { val superName = descriptor.getSuperClassNotAny()?.let { context.getInnerNameForDescriptor(it) } val model = JsClassModel(context.getInnerNameForDescriptor(descriptor), superName) + if (descriptor.kind != ClassKind.ANNOTATION_CLASS && !AnnotationsUtils.isNativeObject(descriptor)) { + copyDefaultMembers(descriptor, model) + generateBridgeMethods(descriptor, model) + } return model } -} + + private fun copyDefaultMembers(descriptor: ClassDescriptor, model: JsClassModel) { + val members = descriptor.unsubstitutedMemberScope + .getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) + .mapNotNull { it as? CallableMemberDescriptor } + + // Traverse fake non-abstract member. Current class does not provide their implementation, + // it can be inherited from interface. + for (member in members.filter { !it.kind.isReal && it.modality != Modality.ABSTRACT }) { + if (member is FunctionDescriptor) { + tryCopyWhenImplementingInterfaceWithDefaultArgs(member, model) + } + + copySimpleMember(descriptor, member, model) + + // Copy *implementation* functions (i.e. those ones which end with `$default` suffix) + // of Kotlin functions with optional parameters. + if (member is FunctionDescriptor && !hasImplementationInPrototype(member)) { + copyMemberWithOptionalArgs(descriptor, member, model, Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX) + } + } + + // Traverse non-fake non-abstract members. Current class provides their implementation, but the implementation + // may override function with optional parameters. In this case we already copied *implementation* function + // (with `$default` suffix) but we also need *dispatcher* function (without suffix). + // Case of fake member is covered by previous loop. + for (function in members.filterIsInstance().filter { it.modality != Modality.ABSTRACT && it.kind.isReal }) { + copyMemberWithOptionalArgs(descriptor, function, model, "") + } + } + + private fun hasImplementationInPrototype(member: CallableMemberDescriptor): Boolean { + return member.overriddenDescriptors.any { + it.modality != Modality.ABSTRACT && !DescriptorUtils.isInterface(it.containingDeclaration) + } + } + + // Cover very special case. Consider + // + // open class B { fun foo(x: Int): Unit } + // interface I { fun foo(x: Int = ...): Unit } + // class D : B(), I + // + // Interface I provides dispatcher function, but no implementation function. It's expected that D + // inherits dispatcher function from I (by copying it) and implementation function from B. + // However, D inherits `foo` without suffix (i.e. it corresponds to I's dispatcher function). + // We must copy B.foo to D.foo$default and then I.foo to D.foo + private fun tryCopyWhenImplementingInterfaceWithDefaultArgs(member: FunctionDescriptor, model: JsClassModel) { + val fromInterface = member.overriddenDescriptors.firstOrNull { it.hasOwnParametersWithDefaultValue() } ?: return + if (!DescriptorUtils.isInterface(fromInterface.containingDeclaration)) return + val fromClass = member.overriddenDescriptors.firstOrNull { !DescriptorUtils.isInterface(it.containingDeclaration) } ?: return + + val targetClass = member.containingDeclaration as ClassDescriptor + val fromInterfaceName = context.getNameForDescriptor(fromInterface).ident + + copyMethod(context.getNameForDescriptor(fromClass).ident, fromInterfaceName + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX, + fromClass.containingDeclaration as ClassDescriptor, targetClass, model.postDeclarationBlock) + copyMethod(fromInterfaceName, context.getNameForDescriptor(member).ident, + fromInterface.containingDeclaration as ClassDescriptor, targetClass, + model.postDeclarationBlock) + } + + private fun copySimpleMember(descriptor: ClassDescriptor, member: CallableMemberDescriptor, model: JsClassModel) { + // Special case: fake descriptor denotes (possible multiple) private members from different super interfaces + if (member.visibility == Visibilities.INVISIBLE_FAKE) return copyInvisibleFakeMember(descriptor, member, model) + + val memberToCopy = findMemberToCopy(member) ?: return + val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor + if (classToCopyFrom.kind != ClassKind.INTERFACE || AnnotationsUtils.isNativeObject(classToCopyFrom)) return + + val name = context.getNameForDescriptor(member).ident + when (member) { + is FunctionDescriptor -> { + copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock) + } + is PropertyDescriptor -> copyProperty(name, classToCopyFrom, descriptor, model.postDeclarationBlock) + } + } + + private fun copyInvisibleFakeMember(descriptor: ClassDescriptor, member: CallableMemberDescriptor, model: JsClassModel) { + for (overriddenMember in member.overriddenDescriptors) { + val memberToCopy = if (overriddenMember.kind.isReal) overriddenMember else findMemberToCopy(overriddenMember) ?: continue + val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor + if (classToCopyFrom.kind != ClassKind.INTERFACE) continue + + val name = context.getNameForDescriptor(memberToCopy).ident + when (member) { + is FunctionDescriptor -> { + copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock) + } + is PropertyDescriptor -> copyProperty(name, classToCopyFrom, descriptor, model.postDeclarationBlock) + } + } + } + + private fun copyMemberWithOptionalArgs(descriptor: ClassDescriptor, member: FunctionDescriptor, model: JsClassModel, suffix: String) { + val memberToCopy = findOptionalArgsMemberToCopy(member) ?: return + val classToCopyFrom = memberToCopy.containingDeclaration as ClassDescriptor + if (classToCopyFrom.kind != ClassKind.INTERFACE || AnnotationsUtils.isNativeObject(classToCopyFrom)) return + + val name = context.getNameForDescriptor(member).ident + suffix + copyMethod(name, name, classToCopyFrom, descriptor, model.postDeclarationBlock) + } + + private fun findMemberToCopy(member: CallableMemberDescriptor): CallableMemberDescriptor? { + // If one of overridden members is non-abstract, copy it. + // When none found, we have nothing to copy, ignore. + // When multiple found, our current class should provide implementation, ignore. + val memberToCopy = member.overriddenDescriptors + .filter { it.modality != Modality.ABSTRACT } + .singleOrNull() ?: return null + + // If found member is not from interface, we don't need to copy it, it's already in prototype + if ((memberToCopy.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) return null + + // If found member is fake itself, repeat search for it, until we find actual implementation + return if (!memberToCopy.kind.isReal) findMemberToCopy(memberToCopy) else memberToCopy + } + + private fun findOptionalArgsMemberToCopy(member: FunctionDescriptor): FunctionDescriptor? { + // If one of overridden members has parameters with default value, copy it. + // When non found, we have nothing to copy, ignore. + // When multiple found, our current class should provide implementation, ignore. + val memberToCopy = member.overriddenDescriptors + .filter { it.hasOrInheritsParametersWithDefaultValue() } + .singleOrNull() ?: return null + + // If found member is not from interface, we don't need to copy it, it's already in prototype + if ((memberToCopy.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) return null + + // If found member is fake itself, repeat search for it, until we find actual implementation + return if (!memberToCopy.kind.isReal) findOptionalArgsMemberToCopy(memberToCopy) else memberToCopy + } + + private fun generateBridgeMethods(descriptor: ClassDescriptor, model: JsClassModel) { + generateBridgesToTraitImpl(descriptor, model) + generateOtherBridges(descriptor, model) + } + + private fun generateBridgesToTraitImpl(descriptor: ClassDescriptor, model: JsClassModel) { + val translationContext = TranslationContext.rootContext(context) + for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) { + val sourceName = context.getNameForDescriptor(key).ident + val targetName = context.getNameForDescriptor(value).ident + if (sourceName != targetName) { + generateDelegateCall(descriptor, key, value, JsLiteral.THIS, translationContext) { + model.postDeclarationBlock.statements += it + } + } + } + } + + private fun generateOtherBridges(descriptor: ClassDescriptor, model: JsClassModel) { + for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { + if (memberDescriptor is FunctionDescriptor) { + val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) { + //There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation + false + } + + for (bridge in bridgesToGenerate) { + generateBridge(descriptor, model, bridge) + } + } + } + } + + private fun generateBridge(descriptor: ClassDescriptor, model: JsClassModel, bridge: Bridge) { + val fromDescriptor = bridge.from + val toDescriptor = bridge.to + + if (toDescriptor.visibility == Visibilities.INVISIBLE_FAKE) return + + val sourceName = context.getNameForDescriptor(fromDescriptor).ident + val targetName = context.getNameForDescriptor(toDescriptor).ident + if (sourceName == targetName) return + + if ((fromDescriptor.containingDeclaration as ClassDescriptor).kind != ClassKind.INTERFACE) { + if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) return + } + + val translationContext = TranslationContext.rootContext(context) + generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, translationContext) { + model.postDeclarationBlock.statements += it + } + } + + private fun copyMethod( + sourceName: String, + targetName: String, + sourceDescriptor: ClassDescriptor, + targetDescriptor: ClassDescriptor, + block: JsBlock + ) { + 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(targetName, targetPrototype) + val sourceFunction = JsNameRef(sourceName, sourcePrototype) + block.statements += JsAstUtils.assignment(targetFunction, sourceFunction).makeStmt() + } + + private fun copyProperty( + name: String, + sourceDescriptor: ClassDescriptor, + targetDescriptor: ClassDescriptor, + block: JsBlock + ) { + 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) + + block.statements += defineProperty.makeStmt() + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index d92530e7882..ccdb1840fc7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -16,9 +16,6 @@ package org.jetbrains.kotlin.js.translate.declaration -import org.jetbrains.kotlin.backend.common.CodegenUtil -import org.jetbrains.kotlin.backend.common.bridges.Bridge -import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.descriptors.* @@ -50,7 +47,6 @@ import org.jetbrains.kotlin.types.CommonSupertypes.topologicallySortSuperclasses import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.utils.DFS -import org.jetbrains.kotlin.utils.identity /** * Generates a definition of a single class. @@ -70,8 +66,6 @@ class ClassTranslator private constructor( private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE - private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS - private fun translate() { val scope = context().getScopeForDescriptor(descriptor) val context = context().newDeclaration(descriptor) @@ -105,8 +99,6 @@ class ClassTranslator private constructor( addSuperclassReferences() classDeclaration.secondaryConstructors.forEach { generateSecondaryConstructor(context, it) } - generatedBridgeMethods() - if (descriptor.isData) { JsDataClassGenerator(classDeclaration, context).generate() } @@ -491,54 +483,6 @@ class ClassTranslator private constructor( } } - private fun generatedBridgeMethods() { - if (isAnnotation()) return - - generateBridgesToTraitImpl() - - generateOtherBridges() - } - - private fun generateBridgesToTraitImpl() { - for ((key, value) in CodegenUtil.getNonPrivateTraitMethods(descriptor)) { - if (!areNamesEqual(key, value)) { - generateDelegateCall(descriptor, value, key, JsLiteral.THIS, context()) - } - } - } - - private fun generateOtherBridges() { - for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { - if (memberDescriptor is FunctionDescriptor) { - val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) { - //There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation - false - } - - for (bridge in bridgesToGenerate) { - generateBridge(bridge) - } - } - } - } - - private fun generateBridge(bridge: Bridge) { - val fromDescriptor = bridge.from - val toDescriptor = bridge.to - if (areNamesEqual(fromDescriptor, toDescriptor)) return - - if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) - return - - generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsLiteral.THIS, context()) - } - - private fun areNamesEqual(first: FunctionDescriptor, second: FunctionDescriptor): Boolean { - val firstName = context().getNameForDescriptor(first) - val secondName = context().getNameForDescriptor(second) - return firstName.ident == secondName.ident - } - companion object { @JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext, enumInitializerName: JsName?) { return ClassTranslator(classDeclaration, context, enumInitializerName, null).translate() diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt index f35e7237357..3c1fd929ff5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt @@ -99,7 +99,7 @@ class DeclarationBodyVisitor( override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression?) { if (!descriptor.hasOrInheritsParametersWithDefaultValue() || !descriptor.isOverridableOrOverrides) { if (expression != null) { - context.addFunctionToPrototype(containingClass, descriptor, expression) + context.addFunctionToPrototype(containingClass, descriptor, expression, context.declarationStatementConsumer) } } else { @@ -139,7 +139,7 @@ class DeclarationBodyVisitor( } caller.body.statements += statement - context.addFunctionToPrototype(containingClass, descriptor, caller) + context.addFunctionToPrototype(containingClass, descriptor, caller, context.declarationStatementConsumer) } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt index 99283e3be6e..9c0802ef970 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt @@ -89,7 +89,7 @@ class DelegationTranslator( } private fun getSuperClass(specifier: KtSuperTypeListEntry): ClassDescriptor = - CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext()) + CodegenUtil.getSuperClassBySuperTypeListEntry(specifier, bindingContext()) private fun generateDelegates(toClass: ClassDescriptor, field: Field) { for ((descriptor, overriddenDescriptor) in DelegationResolver.getDelegates(classDescriptor, toClass)) { @@ -174,10 +174,12 @@ class DelegationTranslator( // TODO: same logic as in AbstractDeclarationVisitor if (descriptor.isExtensionProperty || TranslationUtils.shouldAccessViaFunctions(descriptor)) { val getter = descriptor.getter!! - context().addFunctionToPrototype(classDescriptor, getter, generateDelegateGetterFunction(getter)) + context().addFunctionToPrototype(classDescriptor, getter, generateDelegateGetterFunction(getter), + context().declarationStatementConsumer) if (descriptor.isVar) { val setter = descriptor.setter!! - context().addFunctionToPrototype(classDescriptor, setter, generateDelegateSetterFunction(setter)) + context().addFunctionToPrototype(classDescriptor, setter, generateDelegateSetterFunction(setter), + context().declarationStatementConsumer) } } else { @@ -194,6 +196,7 @@ class DelegationTranslator( delegateName: JsName ) { val delegateRef = JsNameRef(delegateName, JsLiteral.THIS) - generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context()) + generateDelegateCall(classDescriptor, descriptor, overriddenDescriptor, delegateRef, context(), + context().declarationStatementConsumer) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java index 11d354f36bf..c5357bc4a83 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java @@ -192,7 +192,8 @@ class JsDataClassGenerator extends DataClassMethodGenerator { private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) { JsFunction functionObject = context.createRootScopedFunction(functionDescriptor); ClassDescriptor containingClass = (ClassDescriptor) functionDescriptor.getContainingDeclaration(); - UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject); + UtilsKt.addFunctionToPrototype(context, containingClass, functionDescriptor, functionObject, + context.getDeclarationStatementConsumer()); return functionObject; } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt index b5a11f07530..901f3a6a2ff 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt @@ -49,6 +49,13 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam declarationBlock.statements += fragment.declarationBlock initializerBlock.statements += fragment.initializerBlock exportBlock.statements += fragment.exportBlock + + classes += fragment.classes.values.map { cls -> + val name = nameMap.rename(cls.name) + name to JsClassModel(name, cls.superName?.let { nameMap.rename(it) }).also { copy -> + copy.postDeclarationBlock.statements += cls.postDeclarationBlock.statements.map { nameMap.rename(it) } + } + } } val importedModules: List @@ -116,6 +123,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam addClassPrototypes(this) this += declarationBlock.statements this += exportBlock.statements + addClassPostDeclarations(this) this += initializerBlock.statements } } @@ -149,4 +157,25 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam val constructorRef = JsNameRef("constructor", prototype.deepCopy()) statements += JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt() } + + private fun addClassPostDeclarations(statements: MutableList) { + val visited = mutableSetOf() + for (cls in classes.keys) { + addClassPostDeclarations(cls, visited, statements) + } + } + + private fun addClassPostDeclarations( + name: JsName, + visited: MutableSet, + statements: MutableList + ) { + if (!visited.add(name)) return + val cls = classes[name] ?: return + val superName = cls.superName + if (superName != null) { + addClassPostDeclarations(superName, visited, statements) + } + statements += cls.postDeclarationBlock.statements + } } \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index b1e1de7ca30..48de13ca334 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -42,7 +42,8 @@ fun generateDelegateCall( fromDescriptor: FunctionDescriptor, toDescriptor: FunctionDescriptor, thisObject: JsExpression, - context: TranslationContext + context: TranslationContext, + consumer: (JsStatement) -> Unit ) { val overriddenMemberFunctionName = context.getNameForDescriptor(toDescriptor) val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, thisObject) @@ -75,7 +76,7 @@ fun generateDelegateCall( val functionObject = simpleReturnFunction(context.getScopeForDescriptor(fromDescriptor), invocation) functionObject.parameters.addAll(parameters) - context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject) + context.addFunctionToPrototype(classDescriptor, fromDescriptor, functionObject, consumer) } fun List.splitToRanges(classifier: (T) -> S): List, S>> { @@ -120,10 +121,15 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpr return referenceToJsClass } -fun TranslationContext.addFunctionToPrototype(classDescriptor: ClassDescriptor, descriptor: FunctionDescriptor, function: JsExpression) { +fun TranslationContext.addFunctionToPrototype( + classDescriptor: ClassDescriptor, + descriptor: FunctionDescriptor, + function: JsExpression, + consumer: (JsStatement) -> Unit +) { val prototypeRef = JsAstUtils.prototypeOf(getInnerReference(classDescriptor)) val functionRef = JsNameRef(getNameForDescriptor(descriptor), prototypeRef) - addDeclarationStatement(JsAstUtils.assignment(functionRef, function).makeStmt()) + consumer(JsAstUtils.assignment(functionRef, function).makeStmt()) } fun TranslationContext.addAccessorsToPrototype(