[JS IR BE] Initial version of member namer

This commit is contained in:
Svyatoslav Kuzmich
2019-06-17 16:37:33 +03:00
parent 3e10de9cd1
commit 0b19a4a32b
14 changed files with 222 additions and 125 deletions
@@ -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.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder 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.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.* 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.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
// Constructs bridges for inherited generic functions // Constructs bridges for inherited generic functions
@@ -114,7 +113,7 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
): IrFunction { ): IrFunction {
val origin = val origin =
if (bridge.isEffectivelyExternal()) if (bridge.isEffectivelyExternal() || bridge.getJsName() != null)
JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION
else else
IrDeclarationOrigin.BRIDGE IrDeclarationOrigin.BRIDGE
@@ -200,26 +199,7 @@ class FunctionAndSignature(val function: IrSimpleFunction) {
// TODO: Use type-upper-bound-based signature instead of Strings // TODO: Use type-upper-bound-based signature instead of Strings
// Currently strings are used for compatibility with a hack-based name generator // Currently strings are used for compatibility with a hack-based name generator
private data class Signature( private val signature = functionSignature(function)
val name: String,
val extensionReceiverType: String? = null,
val valueParameters: List<String?> = 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 }
)
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -48,7 +48,7 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering
val actualFunction = val actualFunction =
actualCompanion.declarations actualCompanion.declarations
.filterIsInstance<IrSimpleFunction>() .filterIsInstance<IrSimpleFunction>()
.find { it.name == function.name } .single { it.name == function.name }
return actualFunction!! return actualFunction!!
} }
@@ -69,8 +69,7 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
val newCall = super.visitCall(expression) as IrCall val newCall = super.visitCall(expression) as IrCall
val function = expression.symbol.owner as? IrSimpleFunction val function = expression.symbol.owner as IrSimpleFunction
?: return newCall
val actualFunction = getActualPrimitiveCompanionPropertyAccessor(function) val actualFunction = getActualPrimitiveCompanionPropertyAccessor(function)
?: return newCall ?: return newCall
@@ -135,7 +135,6 @@ class IrModuleToJsTransformer(
val program = JsProgram() val program = JsProgram()
val nameGenerator = IrNamerImpl( val nameGenerator = IrNamerImpl(
memberNameGenerator = LegacyMemberNameGenerator(program.rootScope),
newNameTables = namer, newNameTables = namer,
rootScope = program.rootScope rootScope = program.rootScope
) )
@@ -81,12 +81,23 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE) if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE)
continue 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( classBlock.statements += JsExpressionStatement(
defineProperty( defineProperty(
classPrototypeRef, classPrototypeRef,
context.getNameForProperty(property).ident, context.getNameForProperty(property).ident,
getter = property.getter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) }, getter = getterRef,
setter = property.setter?.let { JsNameRef(context.getNameForMemberFunction(it), classPrototypeRef) } setter = setterRef
) )
) )
} }
@@ -121,9 +132,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? { private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? {
val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null } val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null }
if (declaration.isStaticMethodOfClass) { assert(!declaration.isStaticMethodOfClass)
return translatedFunction?.makeStmt()
}
val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget) val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget)
val memberRef = JsNameRef(memberName, classPrototypeRef) val memberRef = JsNameRef(memberName, classPrototypeRef)
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.JsRootScope import org.jetbrains.kotlin.js.backend.ast.JsRootScope
class IrNamerImpl( class IrNamerImpl(
private val memberNameGenerator: LegacyMemberNameGenerator,
private val newNameTables: NameTables, private val newNameTables: NameTables,
private val rootScope: JsRootScope // TODO: Don't use scopes private val rootScope: JsRootScope // TODO: Don't use scopes
) : IrNamer { ) : IrNamer {
@@ -32,12 +31,12 @@ class IrNamerImpl(
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName { override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
require(function.dispatchReceiverParameter != null) require(function.dispatchReceiverParameter != null)
return memberNameGenerator.getNameForMemberFunction(function) return rootScope.declareName(newNameTables.getNameForMemberFunction(function))
} }
override fun getNameForMemberField(field: IrField): JsName { override fun getNameForMemberField(field: IrField): JsName {
require(!field.isStatic) require(!field.isStatic)
return memberNameGenerator.getNameForMemberField(field) return rootScope.declareName(newNameTables.getNameForMemberField(field))
} }
override fun getNameForField(field: IrField): JsName { override fun getNameForField(field: IrField): JsName {
@@ -14,12 +14,11 @@ import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal 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.util.isInlined
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid 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.isES5IdentifierPart
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -73,10 +72,72 @@ fun NameTable<IrDeclaration>.dump(): String =
"--- $declRef => $name" "--- $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<IrPackageFragment>) { class NameTables(packages: List<IrPackageFragment>) {
private val globalNames: NameTable<IrDeclaration> private val globalNames: NameTable<IrDeclaration>
private val memberNames: NameTable<IrDeclaration> private val memberNames: NameTable<Signature>
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>() private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
private val loopNames = mutableMapOf<IrLoop, String>() private val loopNames = mutableMapOf<IrLoop, String>()
@@ -97,15 +158,40 @@ class NameTables(packages: List<IrPackageFragment>) {
for (p in packages) { for (p in packages) {
for (declaration in p.declarations) { for (declaration in p.declarations) {
if (declaration.isEffectivelyExternal())
continue
val localNameGenerator = LocalNameGenerator(declaration) val localNameGenerator = LocalNameGenerator(declaration)
if (declaration is IrClass) { if (declaration is IrClass) {
declaration.thisReceiver!!.acceptVoid(localNameGenerator) if (declaration.isEffectivelyExternal()) {
for (memberDecl in declaration.declarations) { declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
memberDecl.acceptChildrenVoid(LocalNameGenerator(memberDecl)) 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 { } else {
declaration.acceptChildrenVoid(localNameGenerator) declaration.acceptChildrenVoid(localNameGenerator)
@@ -114,6 +200,33 @@ class NameTables(packages: List<IrPackageFragment>) {
} }
} }
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") @Suppress("unused")
fun dump(): String { fun dump(): String {
val local = localNames.toList().joinToString("\n") { (decl, table) -> val local = localNames.toList().joinToString("\n") { (decl, table) ->
@@ -121,7 +234,7 @@ class NameTables(packages: List<IrPackageFragment>) {
"\nLocal names for $declRef:\n${table.dump()}\n" "\nLocal names for $declRef:\n${table.dump()}\n"
} }
return "Global names:\n${globalNames.dump()}" + return "Global names:\n${globalNames.dump()}" +
"\nMember names:\n${memberNames.dump()}" + // "\nMember names:\n${memberNames.dump()}" +
"\nLocal names:\n$local\n" "\nLocal names:\n$local\n"
} }
@@ -148,6 +261,28 @@ class NameTables(packages: List<IrPackageFragment>) {
error("Can't find name for declaration ${declaration.fqNameWhenAvailable}") 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) { private fun generateNamesForTopLevelDecl(declaration: IrDeclaration) {
when { when {
@@ -208,85 +343,6 @@ class NameTables(packages: List<IrPackageFragment>) {
loopNames[loop]!! loopNames[loop]!!
} }
// TODO: implement without JsScope
class LegacyMemberNameGenerator(val scope: JsScope) {
private val fieldCache = mutableMapOf<IrField, JsName>()
private val functionCache = mutableMapOf<IrFunction, JsName>()
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 { fun sanitizeName(name: String): String {
if (name.isEmpty()) return "_" if (name.isEmpty()) return "_"
@@ -103,7 +103,7 @@ val RESERVED_IDENTIFIERS = setOf(
// global identifiers usually declared in a typical JS interpreter // global identifiers usually declared in a typical JS interpreter
"NaN", "isNaN", "Infinity", "undefined", "NaN", "isNaN", "Infinity", "undefined",
"Error", "Object", "Number", "Error", "Object", "Number", "String",
"Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", "Map", "Math", "String", "Boolean", "Date", "Array", "RegExp", "JSON", "Map",
+3
View File
@@ -1,3 +1,6 @@
// Name clashes
// IGNORE_BACKEND: JS_IR
class A (val p: String, p1: String, p2: String) { class A (val p: String, p1: String, p2: String) {
var cond1 :String = "" var cond1 :String = ""
+3
View File
@@ -1,3 +1,6 @@
// Name clashes
// IGNORE_BACKEND: JS_IR
class A (val p: String, p1: String, p2: String) { class A (val p: String, p1: String, p2: String) {
var cond1: String = "" var cond1: String = ""
@@ -6754,6 +6754,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); 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 { 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); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/stdlibTestSnippets"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true);
} }
@@ -6789,6 +6789,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); 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 { public void testAllFilesPresentInStdlibTestSnippets() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/stdlibTestSnippets"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/regression/stdlibTestSnippets"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
} }
@@ -1,3 +1,6 @@
// Name clashes
// IGNORE_BACKEND: JS_IR
// EXPECTED_REACHABLE_NODES: 1295 // EXPECTED_REACHABLE_NODES: 1295
package foo package foo
@@ -0,0 +1,33 @@
// EXPECTED_REACHABLE_NODES: 1750
// KJS_WITH_FULL_RUNTIME
fun abstractCollectionToArray() {
class TestCollection<out E>(val data: Collection<E>) : AbstractCollection<E>() {
val invocations = mutableListOf<String>()
override val size get() = data.size
override fun iterator() = data.iterator()
override fun toArray(): Array<Any?> {
invocations += "toArray1"
return data.toTypedArray()
}
public override fun <T> toArray(array: Array<T>): Array<T> {
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<String> = coll.toArray(Array(coll.size + 1) { "" })
assertEquals(data + listOf(null), arr2.asList())
}
fun box(): String {
abstractCollectionToArray()
return "OK"
}
@@ -4,6 +4,8 @@
*/ */
package kotlin.collections package kotlin.collections
import kotlin.js.JsName
/** /**
* Provides a skeletal implementation of the read-only [Collection] interface. * Provides a skeletal implementation of the read-only [Collection] interface.
* *
@@ -28,6 +30,7 @@ public abstract class AbstractCollection<out E> protected constructor() : Collec
/** /**
* Returns new array of type `Array<Any?>` with the elements of this collection. * Returns new array of type `Array<Any?>` with the elements of this collection.
*/ */
@JsName("toArray")
protected open fun toArray(): Array<Any?> = copyToArrayImpl(this) protected open fun toArray(): Array<Any?> = copyToArrayImpl(this)
/** /**