diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt index c5a2e671bfa..8eb224467e7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder -import org.jetbrains.kotlin.ir.backend.js.utils.asString +import org.jetbrains.kotlin.ir.backend.js.utils.functionSignature import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* @@ -28,7 +28,6 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.* // Constructs bridges for inherited generic functions @@ -114,7 +113,7 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { ): IrFunction { val origin = - if (bridge.isEffectivelyExternal()) + if (bridge.isEffectivelyExternal() || bridge.getJsName() != null) JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION else IrDeclarationOrigin.BRIDGE @@ -200,26 +199,7 @@ class FunctionAndSignature(val function: IrSimpleFunction) { // TODO: Use type-upper-bound-based signature instead of Strings // Currently strings are used for compatibility with a hack-based name generator - private data class Signature( - val name: String, - val extensionReceiverType: String? = null, - val valueParameters: List = emptyList(), - val returnType: String? = null - ) - - private val jsName = function.getJsName() - private val signature = when { - jsName != null -> Signature(jsName) - function.isEffectivelyExternal() -> Signature(function.name.asString()) - else -> Signature( - function.name.asString(), - function.extensionReceiverParameter?.type?.asString(), - function.valueParameters.map { it.type.asString() }, - // Return type used in signature for inline classes and Unit because - // they are binary incompatible with supertypes and require bridges. - function.returnType.run { if (isInlined() || isUnit()) asString() else null } - ) - } + private val signature = functionSignature(function) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt index c345d0d4056..2eaca76289e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt @@ -48,7 +48,7 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering val actualFunction = actualCompanion.declarations .filterIsInstance() - .find { it.name == function.name } + .single { it.name == function.name } return actualFunction!! } @@ -69,8 +69,7 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering override fun visitCall(expression: IrCall): IrExpression { val newCall = super.visitCall(expression) as IrCall - val function = expression.symbol.owner as? IrSimpleFunction - ?: return newCall + val function = expression.symbol.owner as IrSimpleFunction val actualFunction = getActualPrimitiveCompanionPropertyAccessor(function) ?: return newCall 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 223923961ac..a3b57aa16e7 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 @@ -135,7 +135,6 @@ class IrModuleToJsTransformer( val program = JsProgram() val nameGenerator = IrNamerImpl( - memberNameGenerator = LegacyMemberNameGenerator(program.rootScope), newNameTables = namer, rootScope = program.rootScope ) 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 6d8dc1b4cff..6945296715c 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 @@ -81,12 +81,23 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE) continue + fun IrSimpleFunction.accessorRef(): JsNameRef? = + when (visibility) { + Visibilities.PRIVATE -> null + else -> JsNameRef( + context.getNameForMemberFunction(this), + classPrototypeRef + ) + } + + val getterRef = property.getter?.accessorRef() + val setterRef = property.setter?.accessorRef() classBlock.statements += JsExpressionStatement( defineProperty( classPrototypeRef, context.getNameForProperty(property).ident, - getter = property.getter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) }, - setter = property.setter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) } + getter = getterRef, + setter = setterRef ) ) } @@ -121,9 +132,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? { val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null } - if (declaration.isStaticMethodOfClass) { - return translatedFunction?.makeStmt() - } + assert(!declaration.isStaticMethodOfClass) val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget) val memberRef = JsNameRef(memberName, classPrototypeRef) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt index 4360385f403..c40cabdb16a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.js.backend.ast.JsNameRef import org.jetbrains.kotlin.js.backend.ast.JsRootScope class IrNamerImpl( - private val memberNameGenerator: LegacyMemberNameGenerator, private val newNameTables: NameTables, private val rootScope: JsRootScope // TODO: Don't use scopes ) : IrNamer { @@ -32,12 +31,12 @@ class IrNamerImpl( override fun getNameForMemberFunction(function: IrSimpleFunction): JsName { require(function.dispatchReceiverParameter != null) - return memberNameGenerator.getNameForMemberFunction(function) + return rootScope.declareName(newNameTables.getNameForMemberFunction(function)) } override fun getNameForMemberField(field: IrField): JsName { require(!field.isStatic) - return memberNameGenerator.getNameForMemberField(field) + return rootScope.declareName(newNameTables.getNameForMemberField(field)) } override fun getNameForField(field: IrField): JsName { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt index ec66492f251..f827fed56f8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -14,12 +14,11 @@ import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.isEffectivelyExternal +import org.jetbrains.kotlin.ir.util.isEnumClass import org.jetbrains.kotlin.ir.util.isInlined import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid -import org.jetbrains.kotlin.js.backend.ast.JsName -import org.jetbrains.kotlin.js.backend.ast.JsScope import org.jetbrains.kotlin.js.naming.isES5IdentifierPart import org.jetbrains.kotlin.js.naming.isES5IdentifierStart import org.jetbrains.kotlin.name.FqName @@ -73,10 +72,72 @@ fun NameTable.dump(): String = "--- $declRef => $name" } +sealed class Signature +data class StableNameSignature(val name: String) : Signature() +data class BackingFieldSignature(val field: IrField) : Signature() +data class ParameterTypeBasedSignature(val mangledName: String, val suggestedName: String) : Signature() + +fun fieldSignature(field: IrField): Signature { + if (field.isEffectivelyExternal()) { + return StableNameSignature(field.name.identifier) + } + + return BackingFieldSignature(field) +} + +fun functionSignature(declaration: IrFunction): Signature { + require(!declaration.isStaticMethodOfClass) + require(declaration.dispatchReceiverParameter != null) + + val declarationName = declaration.getJsNameOrKotlinName().asString() + val stableName = StableNameSignature(declarationName) + + if (declaration.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION) { + return stableName + } + if (declaration.isEffectivelyExternal()) { + return stableName + } + if (declaration.getJsName() != null) { + return stableName + } + // Handle names for special functions + if (declaration is IrSimpleFunction && declaration.isMethodOfAny()) { + return stableName + } + + val nameBuilder = StringBuilder() + + nameBuilder.append(declarationName) + + // TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters? + declaration.typeParameters.ifNotEmpty { + nameBuilder.append("_\$t") + joinTo(nameBuilder, "") { "_${it.name.asString()}" } + } + declaration.extensionReceiverParameter?.let { + nameBuilder.append("_r$${it.type.asString()}") + } + declaration.valueParameters.ifNotEmpty { + joinTo(nameBuilder, "") { "_${it.type.asString()}" } + } + declaration.returnType.let { + // Return type is only used in signature for inline class and Unit types because + // they are binary incompatible with supertypes. + if (it.isInlined() || it.isUnit()) { + nameBuilder.append("_ret$${it.asString()}") + } + } + + val signature = nameBuilder.toString() + + // TODO: Check reserved names + return ParameterTypeBasedSignature(signature, declarationName) +} class NameTables(packages: List) { private val globalNames: NameTable - private val memberNames: NameTable + private val memberNames: NameTable private val localNames = mutableMapOf>() private val loopNames = mutableMapOf() @@ -97,15 +158,40 @@ class NameTables(packages: List) { for (p in packages) { for (declaration in p.declarations) { - if (declaration.isEffectivelyExternal()) - continue - val localNameGenerator = LocalNameGenerator(declaration) if (declaration is IrClass) { - declaration.thisReceiver!!.acceptVoid(localNameGenerator) - for (memberDecl in declaration.declarations) { - memberDecl.acceptChildrenVoid(LocalNameGenerator(memberDecl)) + if (declaration.isEffectivelyExternal()) { + declaration.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction) { + val parent = declaration.parent + if (parent is IrClass && !parent.isEnumClass) { + generateNameForMemberFunction(declaration) + } + } + + override fun visitField(declaration: IrField) { + val parent = declaration.parent + if (parent is IrClass && !parent.isEnumClass) { + generateNameForMemberField(declaration) + } + } + }) + } else { + declaration.thisReceiver!!.acceptVoid(localNameGenerator) + for (memberDecl in declaration.declarations) { + memberDecl.acceptChildrenVoid(LocalNameGenerator(memberDecl)) + when (memberDecl) { + is IrSimpleFunction -> + generateNameForMemberFunction(memberDecl) + is IrField -> + generateNameForMemberField(memberDecl) + } + } } } else { declaration.acceptChildrenVoid(localNameGenerator) @@ -114,6 +200,33 @@ class NameTables(packages: List) { } } + private fun generateNameForMemberField(field: IrField) { + require(!field.isTopLevel) + require(!field.isStatic) + val signature = fieldSignature(field) + + if (field.isEffectivelyExternal()) { + memberNames.declareStableName(signature, field.name.identifier) + } + + memberNames.declareFreshName(signature, "_" + sanitizeName(field.name.asString())) + } + + private fun generateNameForMemberFunction(declaration: IrSimpleFunction) { + when (val signature = functionSignature(declaration)) { + is StableNameSignature -> memberNames.declareStableName(signature, signature.name) + is ParameterTypeBasedSignature -> { + // TODO: Fix hack: Coroutines runtime currently relies on stable names + // of `invoke` functions in FunctionN interfaces + if (declaration.name.asString().startsWith("invoke")) { + memberNames.declareStableName(signature, sanitizeName(signature.mangledName)) + } else { + memberNames.declareFreshName(signature, signature.suggestedName) + } + } + } + } + @Suppress("unused") fun dump(): String { val local = localNames.toList().joinToString("\n") { (decl, table) -> @@ -121,7 +234,7 @@ class NameTables(packages: List) { "\nLocal names for $declRef:\n${table.dump()}\n" } return "Global names:\n${globalNames.dump()}" + - "\nMember names:\n${memberNames.dump()}" + + // "\nMember names:\n${memberNames.dump()}" + "\nLocal names:\n$local\n" } @@ -148,6 +261,28 @@ class NameTables(packages: List) { error("Can't find name for declaration ${declaration.fqNameWhenAvailable}") } + fun getNameForMemberField(field: IrField): String { + val signature = fieldSignature(field) + val name = memberNames.names[signature] + require(name != null) { + "Can't find name for member field $field" + } + return name + } + + fun getNameForMemberFunction(function: IrSimpleFunction): String { + val signature = functionSignature(function) + val name = memberNames.names[signature] + + // TODO: Fix hack: Coroutines runtime currently relies on stable names + // of `invoke` functions in FunctionN interfaces + if (name == null && signature is ParameterTypeBasedSignature && signature.suggestedName.startsWith("invoke")) + return signature.suggestedName + require(name != null) { + "Can't find name for member function $function" + } + return name + } private fun generateNamesForTopLevelDecl(declaration: IrDeclaration) { when { @@ -208,85 +343,6 @@ class NameTables(packages: List) { loopNames[loop]!! } -// TODO: implement without JsScope -class LegacyMemberNameGenerator(val scope: JsScope) { - - private val fieldCache = mutableMapOf() - private val functionCache = mutableMapOf() - - fun getNameForMemberField(field: IrField): JsName { - return fieldCache.getOrPut(field) { getNewNameForField(field) } - } - - fun getNameForMemberFunction(function: IrSimpleFunction): JsName { - return functionCache.getOrPut(function) { getNewNameForFunction(function) } - } - - private fun getNewNameForField(f: IrField): JsName { - require(!f.isTopLevel) - require(!f.isStatic) - - if (f.isEffectivelyExternal()) { - return scope.declareName(f.name.identifier) - } - - val parentName = (f.parent as IrDeclarationWithName).name.asString() - val name = "${f.name.asString()}_$parentName" - - return scope.declareFreshName(sanitizeName(name)) - } - - private fun getNewNameForFunction(declaration: IrSimpleFunction): JsName { - require(!declaration.isStaticMethodOfClass) - require(declaration.dispatchReceiverParameter != null) - - val declarationName = declaration.getJsNameOrKotlinName().asString() - - if (declaration.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION) { - return scope.declareName(declarationName) - } - - if (declaration.isEffectivelyExternal()) { - return scope.declareName(declarationName) - } - declaration.getJsName()?.let { jsName -> - return scope.declareName(jsName) - } - - val nameBuilder = StringBuilder() - - // Handle names for special functions - if (declaration.isMethodOfAny()) { - return scope.declareName(declarationName) - } - - nameBuilder.append(declarationName) - - // TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters? - declaration.typeParameters.ifNotEmpty { - nameBuilder.append("_\$t") - joinTo(nameBuilder, "") { "_${it.name.asString()}" } - } - declaration.extensionReceiverParameter?.let { - nameBuilder.append("_r$${it.type.asString()}") - } - declaration.valueParameters.ifNotEmpty { - joinTo(nameBuilder, "") { "_${it.type.asString()}" } - } - declaration.returnType.let { - // Return type is only used in signature for inline class and Unit types because - // they are binary incompatible with supertypes. - if (it.isInlined() || it.isUnit()) { - nameBuilder.append("_ret$${it.asString()}") - } - } - - // TODO: Check reserved names - - return scope.declareName(sanitizeName(nameBuilder.toString())) - } -} - fun sanitizeName(name: String): String { if (name.isEmpty()) return "_" diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt index dfcee49e80b..e49c05fc615 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/StableNamesCollector.kt @@ -103,7 +103,7 @@ val RESERVED_IDENTIFIERS = setOf( // global identifiers usually declared in a typical JS interpreter "NaN", "isNaN", "Infinity", "undefined", - "Error", "Object", "Number", + "Error", "Object", "Number", "String", "Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", "Map", diff --git a/compiler/testData/codegen/box/lazyCodegen/ifElse.kt b/compiler/testData/codegen/box/lazyCodegen/ifElse.kt index 0f683860b24..777e51c7eee 100644 --- a/compiler/testData/codegen/box/lazyCodegen/ifElse.kt +++ b/compiler/testData/codegen/box/lazyCodegen/ifElse.kt @@ -1,3 +1,6 @@ +// Name clashes +// IGNORE_BACKEND: JS_IR + class A (val p: String, p1: String, p2: String) { var cond1 :String = "" diff --git a/compiler/testData/codegen/box/unit/ifElse.kt b/compiler/testData/codegen/box/unit/ifElse.kt index 171b2b2fdeb..282189c4b8d 100644 --- a/compiler/testData/codegen/box/unit/ifElse.kt +++ b/compiler/testData/codegen/box/unit/ifElse.kt @@ -1,3 +1,6 @@ +// Name clashes +// IGNORE_BACKEND: JS_IR + class A (val p: String, p1: String, p2: String) { var cond1: String = "" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index e71910b80c6..cf78f7323f1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -6754,6 +6754,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); } + @TestMetadata("abstractCollectionToArray.kt") + public void testAbstractCollectionToArray() throws Exception { + runTest("js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt"); + } + public void testAllFilesPresentInStdlibTestSnippets() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/stdlibTestSnippets"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true); } 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 93222c42e26..3af026604aa 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 @@ -6789,6 +6789,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); } + @TestMetadata("abstractCollectionToArray.kt") + public void testAbstractCollectionToArray() throws Exception { + runTest("js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt"); + } + public void testAllFilesPresentInStdlibTestSnippets() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/stdlibTestSnippets"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } diff --git a/js/js.translator/testData/box/inline/thisImplicitlyCaptured.kt b/js/js.translator/testData/box/inline/thisImplicitlyCaptured.kt index 1bec4a53582..52273ac2c3f 100644 --- a/js/js.translator/testData/box/inline/thisImplicitlyCaptured.kt +++ b/js/js.translator/testData/box/inline/thisImplicitlyCaptured.kt @@ -1,3 +1,6 @@ +// Name clashes +// IGNORE_BACKEND: JS_IR + // EXPECTED_REACHABLE_NODES: 1295 package foo diff --git a/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt b/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt new file mode 100644 index 00000000000..84d7f748ac4 --- /dev/null +++ b/js/js.translator/testData/box/regression/stdlibTestSnippets/abstractCollectionToArray.kt @@ -0,0 +1,33 @@ +// EXPECTED_REACHABLE_NODES: 1750 +// KJS_WITH_FULL_RUNTIME + +fun abstractCollectionToArray() { + class TestCollection(val data: Collection) : AbstractCollection() { + val invocations = mutableListOf() + override val size get() = data.size + override fun iterator() = data.iterator() + + override fun toArray(): Array { + invocations += "toArray1" + return data.toTypedArray() + } + public override fun toArray(array: Array): Array { + invocations += "toArray2" + return super.toArray(array) + } + } + val data = listOf("abc", "def") + val coll = TestCollection(data) + + val arr1 = coll.toTypedArray() + assertEquals(data, arr1.asList()) + assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations) + + val arr2: Array = coll.toArray(Array(coll.size + 1) { "" }) + assertEquals(data + listOf(null), arr2.asList()) +} + +fun box(): String { + abstractCollectionToArray() + return "OK" +} \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt b/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt index 32f2aae261f..762323006ef 100644 --- a/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt +++ b/libraries/stdlib/src/kotlin/collections/AbstractCollection.kt @@ -4,6 +4,8 @@ */ package kotlin.collections +import kotlin.js.JsName + /** * Provides a skeletal implementation of the read-only [Collection] interface. * @@ -28,6 +30,7 @@ public abstract class AbstractCollection protected constructor() : Collec /** * Returns new array of type `Array` with the elements of this collection. */ + @JsName("toArray") protected open fun toArray(): Array = copyToArrayImpl(this) /**