diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 2027114e495..95b214a48da 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -4796,6 +4796,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("localObjectLiteralWithInheritance.kt") + public void testLocalObjectLiteralWithInheritance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt"); + doTest(fileName); + } + @TestMetadata("method.kt") public void testMethod() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/method.kt"); 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 244add7e113..bb3884c029a 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 @@ -161,7 +161,7 @@ public final class StaticContext { JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME); createImportedModule(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), Namer.KOTLIN_LOWER_NAME, kotlinName, null); - classModelGenerator = new ClassModelGenerator(this); + classModelGenerator = new ClassModelGenerator(TranslationContext.rootContext(this)); } @NotNull 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 2f387a9096a..2b67799ffe4 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 @@ -28,12 +28,14 @@ import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.naming.SuggestedName; +import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator; import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; +import org.jetbrains.kotlin.js.translate.utils.UtilsKt; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.resolve.BindingContext; @@ -279,6 +281,40 @@ public class TranslationContext { return staticContext.getInnerNameForDescriptor(descriptor); } + @NotNull + public JsName getInlineableInnerNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { + JsName name; + if (inlineFunctionContext == null || !isPublicInlineFunction() || + DescriptorUtils.isAncestor(inlineFunctionContext.getDescriptor(), descriptor, false)) { + name = getInnerNameForDescriptor(descriptor); + } + else { + String tag = staticContext.getTag(descriptor); + name = inlineFunctionContext.getImports().computeIfAbsent(tag, t -> { + JsExpression imported = createInlineLocalImportExpression(descriptor); + if (imported instanceof JsNameRef) { + JsNameRef importedNameRef = (JsNameRef) imported; + if (importedNameRef.getQualifier() == null && importedNameRef.getIdent().equals(Namer.getRootPackageName()) && + (descriptor instanceof PackageFragmentDescriptor || descriptor instanceof ModuleDescriptor)) { + return importedNameRef.getName(); + } + } + + JsName result = JsScope.declareTemporaryName(StaticContext.getSuggestedName(descriptor)); + if (isFromCurrentModule(descriptor) && !AnnotationsUtils.isNativeObject(descriptor)) { + MetadataProperties.setLocalAlias(result, getInnerNameForDescriptor(descriptor)); + } + MetadataProperties.setDescriptor(result, descriptor); + MetadataProperties.setStaticRef(result, imported); + MetadataProperties.setImported(result, true); + inlineFunctionContext.getImportBlock().getStatements().add(JsAstUtils.newVar(result, imported)); + return result; + }); + } + + return name; + } + @NotNull public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) { return staticContext.getNameForObjectInstance(descriptor); @@ -340,36 +376,7 @@ public class TranslationContext { @NotNull public JsExpression getInnerReference(@NotNull DeclarationDescriptor descriptor) { - JsName name; - if (inlineFunctionContext == null || !isPublicInlineFunction() || - DescriptorUtils.isAncestor(inlineFunctionContext.getDescriptor(), descriptor, false)) { - name = getInnerNameForDescriptor(descriptor); - } - else { - String tag = staticContext.getTag(descriptor); - name = inlineFunctionContext.getImports().computeIfAbsent(tag, t -> { - JsExpression imported = createInlineLocalImportExpression(descriptor); - if (imported instanceof JsNameRef) { - JsNameRef importedNameRef = (JsNameRef) imported; - if (importedNameRef.getQualifier() == null && importedNameRef.getIdent().equals(Namer.getRootPackageName()) && - (descriptor instanceof PackageFragmentDescriptor || descriptor instanceof ModuleDescriptor)) { - return importedNameRef.getName(); - } - } - - JsName result = JsScope.declareTemporaryName(StaticContext.getSuggestedName(descriptor)); - if (isFromCurrentModule(descriptor) && !AnnotationsUtils.isNativeObject(descriptor)) { - MetadataProperties.setLocalAlias(result, getInnerNameForDescriptor(descriptor)); - } - MetadataProperties.setDescriptor(result, descriptor); - MetadataProperties.setStaticRef(result, imported); - MetadataProperties.setImported(result, true); - inlineFunctionContext.getImportBlock().getStatements().add(JsAstUtils.newVar(result, imported)); - return result; - }); - } - - return pureFqn(name, null); + return pureFqn(getInlineableInnerNameForDescriptor(descriptor), null); } @NotNull @@ -769,7 +776,18 @@ public class TranslationContext { } public void addClass(@NotNull ClassDescriptor classDescriptor) { - staticContext.addClass(classDescriptor); + if (inlineFunctionContext != null) { + JsClassModel classModel = new ClassModelGenerator(this).generateClassModel(classDescriptor); + List targetStatements = inlineFunctionContext.getPrototypeBlock().getStatements(); + JsName superName = classModel.getSuperName(); + if (superName != null) { + targetStatements.addAll(UtilsKt.createPrototypeStatements(superName, classModel.getName())); + } + targetStatements.addAll(classModel.getPostDeclarationBlock().getStatements()); + } + else { + staticContext.addClass(classDescriptor); + } } public void export(@NotNull MemberDescriptor descriptor) { 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 9b3d9439125..83b68ab0bc7 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 @@ -22,7 +22,6 @@ import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDes 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 @@ -33,15 +32,14 @@ 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.resolve.source.getPsi import org.jetbrains.kotlin.utils.identity -class ClassModelGenerator(val context: StaticContext) { +class ClassModelGenerator(val context: TranslationContext) { fun generateClassModel(descriptor: ClassDescriptor): JsClassModel { - val superName = descriptor.getSuperClassNotAny()?.let { context.getInnerNameForDescriptor(it) } - val model = JsClassModel(context.getInnerNameForDescriptor(descriptor), superName) + val superName = descriptor.getSuperClassNotAny()?.let { context.getInlineableInnerNameForDescriptor(it) } + val model = JsClassModel(context.getInlineableInnerNameForDescriptor(descriptor), superName) if (descriptor.kind != ClassKind.ANNOTATION_CLASS && !AnnotationsUtils.isNativeObject(descriptor)) { copyDefaultMembers(descriptor, model) generateBridgeMethods(descriptor, model) @@ -205,12 +203,11 @@ class ClassModelGenerator(val context: StaticContext) { } 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) { - val statement = generateDelegateCall(descriptor, key, value, JsThisRef(), translationContext, false, + val statement = generateDelegateCall(descriptor, key, value, JsThisRef(), context, false, descriptor.source.getPsi()) model.postDeclarationBlock.statements += statement } @@ -246,9 +243,8 @@ class ClassModelGenerator(val context: StaticContext) { if (fromDescriptor.kind.isReal && fromDescriptor.modality != Modality.ABSTRACT && !toDescriptor.kind.isReal) return } - val translationContext = TranslationContext.rootContext(context) model.postDeclarationBlock.statements += generateDelegateCall(descriptor, fromDescriptor, toDescriptor, JsThisRef(), - translationContext, false, descriptor.source.getPsi()) + context, false, descriptor.source.getPsi()) } private fun copyMethod( @@ -258,10 +254,10 @@ class ClassModelGenerator(val context: StaticContext) { targetDescriptor: ClassDescriptor, block: JsBlock ) { - if (targetDescriptor.module != context.currentModule) return + if (!context.isFromCurrentModule(targetDescriptor)) return - val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) - val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) + val targetPrototype = prototypeOf(pureFqn(context.getInlineableInnerNameForDescriptor(targetDescriptor), null)) + val sourcePrototype = prototypeOf(pureFqn(context.getInlineableInnerNameForDescriptor(sourceDescriptor), null)) val targetFunction = JsNameRef(targetName, targetPrototype) val sourceFunction = JsNameRef(sourceName, sourcePrototype) block.statements += JsAstUtils.assignment(targetFunction, sourceFunction).makeStmt() @@ -273,10 +269,10 @@ class ClassModelGenerator(val context: StaticContext) { targetDescriptor: ClassDescriptor, block: JsBlock ) { - if (targetDescriptor.module != context.currentModule) return + if (!context.isFromCurrentModule(targetDescriptor)) return - val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) - val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) + val targetPrototype = prototypeOf(pureFqn(context.getInlineableInnerNameForDescriptor(targetDescriptor), null)) + val sourcePrototype = prototypeOf(pureFqn(context.getInlineableInnerNameForDescriptor(sourceDescriptor), null)) val nameLiteral = JsStringLiteral(name) val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), sourcePrototype, nameLiteral) 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 29b9b24872c..f41bb29250c 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 @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage import org.jetbrains.kotlin.js.backend.ast.metadata.exportedTag import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.createPrototypeStatements import org.jetbrains.kotlin.js.translate.utils.definePackageAlias class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName, val module: ModuleDescriptor) { @@ -193,16 +194,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam addClassPrototypes(superName, visited, statements) - val superclassRef = superName.makeRef() - val superPrototype = JsAstUtils.prototypeOf(superclassRef) - val superPrototypeInstance = JsInvocation(JsNameRef("create", "Object"), superPrototype) - - val classRef = name.makeRef() - val prototype = JsAstUtils.prototypeOf(classRef) - statements += JsAstUtils.assignment(prototype, superPrototypeInstance).makeStmt() - - val constructorRef = JsNameRef("constructor", prototype.deepCopy()) - statements += JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt() + statements += createPrototypeStatements(superName, name) } private fun addClassPostDeclarations(statements: MutableList) { 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 b8d9c29b86e..e244ef783cf 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 @@ -228,3 +228,18 @@ fun TranslationContext.addFunctionButNotExport(name: JsName, expression: JsExpre } return name } + +fun createPrototypeStatements(superName: JsName, name: JsName): List { + val superclassRef = superName.makeRef() + val superPrototype = JsAstUtils.prototypeOf(superclassRef) + val superPrototypeInstance = JsInvocation(JsNameRef("create", "Object"), superPrototype) + + val classRef = name.makeRef() + val prototype = JsAstUtils.prototypeOf(classRef) + val prototypeStatement = JsAstUtils.assignment(prototype, superPrototypeInstance).makeStmt() + + val constructorRef = JsNameRef("constructor", prototype.deepCopy()) + val constructorStatement = JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt() + + return listOf(prototypeStatement, constructorStatement) +} \ No newline at end of file diff --git a/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt b/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt new file mode 100644 index 00000000000..fbc4d570cb7 --- /dev/null +++ b/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt @@ -0,0 +1,24 @@ +// EXPECTED_REACHABLE_NODES: 509 +// MODULE: lib +// FILE: lib.kt + +abstract class A { + fun f() = o() + k() + + abstract fun o(): String + + abstract fun k(): String +} + +inline fun foo(x: String): A { + return object : A() { + override fun o(): String = x + + override fun k(): String = "K" + } +} + +// MODULE: main(lib) +// FILE: main.kt + +fun box(): String = foo("O").f() \ No newline at end of file