[JS IR BE] Initial version of member namer
This commit is contained in:
+3
-23
@@ -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<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 }
|
||||
)
|
||||
}
|
||||
private val signature = functionSignature(function)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
||||
+2
-3
@@ -48,7 +48,7 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering
|
||||
val actualFunction =
|
||||
actualCompanion.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.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
|
||||
|
||||
-1
@@ -135,7 +135,6 @@ class IrModuleToJsTransformer(
|
||||
val program = JsProgram()
|
||||
|
||||
val nameGenerator = IrNamerImpl(
|
||||
memberNameGenerator = LegacyMemberNameGenerator(program.rootScope),
|
||||
newNameTables = namer,
|
||||
rootScope = program.rootScope
|
||||
)
|
||||
|
||||
+14
-5
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+145
-89
@@ -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<IrDeclaration>.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<IrPackageFragment>) {
|
||||
private val globalNames: NameTable<IrDeclaration>
|
||||
private val memberNames: NameTable<IrDeclaration>
|
||||
private val memberNames: NameTable<Signature>
|
||||
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
|
||||
private val loopNames = mutableMapOf<IrLoop, String>()
|
||||
|
||||
@@ -97,15 +158,40 @@ class NameTables(packages: List<IrPackageFragment>) {
|
||||
|
||||
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<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")
|
||||
fun dump(): String {
|
||||
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"
|
||||
}
|
||||
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<IrPackageFragment>) {
|
||||
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<IrPackageFragment>) {
|
||||
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 {
|
||||
if (name.isEmpty()) return "_"
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name clashes
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
class A (val p: String, p1: String, p2: String) {
|
||||
|
||||
var cond1 :String = ""
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name clashes
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
class A (val p: String, p1: String, p2: String) {
|
||||
|
||||
var cond1: String = ""
|
||||
|
||||
+5
@@ -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);
|
||||
}
|
||||
|
||||
+5
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name clashes
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
// EXPECTED_REACHABLE_NODES: 1295
|
||||
package foo
|
||||
|
||||
|
||||
+33
@@ -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
|
||||
|
||||
import kotlin.js.JsName
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@JsName("toArray")
|
||||
protected open fun toArray(): Array<Any?> = copyToArrayImpl(this)
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user