JVM_IR KT-45187 use Arrays.copyOf to copy an array in spread operator

Creating a new array (and copying data into it with System.arraycopy)
doesn't work in generic case, because the actual array class depends on
call site.
This commit is contained in:
Dmitry Petrov
2021-04-13 14:35:18 +03:00
committed by TeamCityServer
parent 334d0a8b5a
commit 7d62f0b5aa
10 changed files with 192 additions and 23 deletions
@@ -40940,6 +40940,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/vararg/kt37779.kt");
}
@Test
@TestMetadata("kt45187.kt")
public void testKt45187() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt45187.kt");
}
@Test
@TestMetadata("kt581.kt")
public void testKt581() throws Exception {
@@ -204,6 +204,15 @@ open class BuiltinSymbolsBase(protected val irBuiltIns: IrBuiltIns, protected va
val doubleArray = primitiveArrayClass(PrimitiveType.DOUBLE)
val booleanArray = primitiveArrayClass(PrimitiveType.BOOLEAN)
val byteArrayType get() = byteArray.owner.defaultType
val charArrayType get() = charArray.owner.defaultType
val shortArrayType get() = shortArray.owner.defaultType
val intArrayType get() = intArray.owner.defaultType
val longArrayType get() = longArray.owner.defaultType
val floatArrayType get() = floatArray.owner.defaultType
val doubleArrayType get() = doubleArray.owner.defaultType
val booleanArrayType get() = booleanArray.owner.defaultType
val unsignedArrays = UnsignedType.values().mapNotNull { unsignedType ->
unsignedArrayClass(unsignedType)?.let { unsignedType to it }
}.toMap()
@@ -23,12 +23,12 @@ import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
@@ -51,6 +51,7 @@ class JvmSymbols(
private val kotlinJvmFunctionsPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.functions"))
private val kotlinReflectPackage: IrPackageFragment = createPackage(FqName("kotlin.reflect"))
private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang"))
private val javaUtilPackage: IrPackageFragment = createPackage(FqName("java.util"))
// Special package for functions representing dynamic symbols referenced by 'INVOKEDYNAMIC' instruction - e.g.,
// 'get(Ljava/lang/String;)Ljava/util/function/Supplier;'
@@ -90,6 +91,7 @@ class JvmSymbols(
"kotlin.jvm" -> kotlinJvmPackage
"kotlin.reflect" -> kotlinReflectPackage
"java.lang" -> javaLangPackage
"java.util" -> javaUtilPackage
else -> error("Other packages are not supported yet: $fqName")
}
createImplicitParameterDeclarationWithWrappedDescriptor()
@@ -548,6 +550,7 @@ class JvmSymbols(
}.symbol
val arrayOfAnyType = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyType)
val arrayOfAnyNType = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyNType)
// Intrinsic to represent closure creation using INVOKEDYNAMIC with LambdaMetafactory.{metafactory, altMetafactory}
// as a bootstrap method.
@@ -742,13 +745,40 @@ class JvmSymbols(
}
}
private val systemClass: IrClassSymbol = createClass(FqName("java.lang.System")) { klass ->
klass.addFunction("arraycopy", irBuiltIns.unitType, isStatic = true).apply {
addValueParameter("src", irBuiltIns.anyNType)
addValueParameter("srcPos", irBuiltIns.intType)
addValueParameter("dest", irBuiltIns.anyNType)
addValueParameter("destPos", irBuiltIns.intType)
addValueParameter("length", irBuiltIns.intType)
private val arraysCopyOfFunctions = HashMap<IrSimpleType, IrSimpleFunction>()
private fun IrClass.addArraysCopyOfFunction(arrayType: IrSimpleType) {
addFunction("copyOf", arrayType, isStatic = true).apply {
addValueParameter("original", arrayType)
addValueParameter("newLength", irBuiltIns.intType)
arraysCopyOfFunctions[arrayType] = this
}
}
val arraysClass: IrClassSymbol =
createClass(FqName("java.util.Arrays")) { irClass ->
irClass.addArraysCopyOfFunction(booleanArrayType)
irClass.addArraysCopyOfFunction(byteArrayType)
irClass.addArraysCopyOfFunction(charArrayType)
irClass.addArraysCopyOfFunction(shortArrayType)
irClass.addArraysCopyOfFunction(intArrayType)
irClass.addArraysCopyOfFunction(longArrayType)
irClass.addArraysCopyOfFunction(floatArrayType)
irClass.addArraysCopyOfFunction(doubleArrayType)
// public static <T> T[] copyOf(T[] original, int newLength)
irClass.addArraysCopyOfFunction(arrayOfAnyNType)
}
fun getArraysCopyOfFunction(arrayType: IrSimpleType): IrSimpleFunctionSymbol {
val copyOf = arraysCopyOfFunctions[arrayType]
return when {
copyOf != null ->
copyOf.symbol
arrayType.classifierOrFail == array ->
arraysCopyOfFunctions[arrayOfAnyNType]!!.symbol
else ->
throw AssertionError("Array type expected: ${arrayType.render()}")
}
}
@@ -798,8 +828,6 @@ class JvmSymbols(
val remainderUnsignedLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("remainderUnsigned")
val toUnsignedStringLong: IrSimpleFunctionSymbol = javaLangLong.functionByName("toUnsignedString")
val systemArraycopy: IrSimpleFunctionSymbol = systemClass.functionByName("arraycopy")
val signatureStringIntrinsic: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<signature-string>")
@@ -11,10 +11,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getArrayElementType
import org.jetbrains.kotlin.ir.types.isBoxedArray
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.getPropertyGetter
@@ -105,16 +102,12 @@ class IrArrayBuilder(val builder: JvmIrBuilder, val arrayType: IrType) {
return builder.irBlock {
val spreadVar = if (spread is IrGetValue) spread.symbol.owner else irTemporary(spread)
val size = unwrappedArrayType.classOrNull!!.getPropertyGetter("size")!!
fun getSize() = irCall(size).apply { dispatchReceiver = irGet(spreadVar) }
val result = irTemporary(newArray(getSize()))
+irCall(builder.irSymbols.systemArraycopy).apply {
val arrayCopyOf = builder.irSymbols.getArraysCopyOfFunction(unwrappedArrayType as IrSimpleType)
// TODO consider using System.arraycopy if the requested array type is non-generic.
+irCall(arrayCopyOf).apply {
putValueArgument(0, coerce(irGet(spreadVar), unwrappedArrayType))
putValueArgument(1, irInt(0))
putValueArgument(2, irGet(result))
putValueArgument(3, irInt(0))
putValueArgument(4, getSize())
putValueArgument(1, irCall(size).apply { dispatchReceiver = irGet(spreadVar) })
}
+irGet(result)
}
}
+13
View File
@@ -0,0 +1,13 @@
// TARGET_BACKEND: JVM
fun box(): String =
object : A<Void, Void>() {
override fun f(vararg params: Void): Void? = null
}.execute()
abstract class A<P, R> {
protected abstract fun f(vararg params: P): R?
fun execute(vararg params: P): String =
if (f(*params) == null) "OK" else "Fail"
}
+49 -1
View File
@@ -8,7 +8,15 @@ fun <T> copyArray(vararg data: T): Array<out T> = data
inline fun <reified T> reifiedCopyArray(vararg data: T): Array<out T> = data
fun copyBooleanArray(vararg data: Boolean): BooleanArray = data
fun copyByteArray(vararg data: Byte): ByteArray = data
fun copyCharArray(vararg data: Char): CharArray = data
fun copyShortArray(vararg data: Short): ShortArray = data
fun copyIntArray(vararg data: Int): IntArray = data
fun copyLongArray(vararg data: Long): LongArray = data
fun copyFloatArray(vararg data: Float): FloatArray = data
fun copyDoubleArray(vararg data: Double): DoubleArray = data
fun copyStringArray(vararg data: String): Array<out String> = data
fun box(): String {
val sarr = arrayOf("OK")
@@ -21,11 +29,51 @@ fun box(): String {
rsarr[0] = "Array was not copied"
assertEquals(rsarr2[0], "OK", "Failed: Array<String>, reified copy")
val boolArray = booleanArrayOf(true)
val boolArray2 = copyBooleanArray(*boolArray)
boolArray[0] = false
assertEquals(boolArray2[0], true, "Failed: BooleanArray")
val byteArray = byteArrayOf(1)
val byteArray2 = copyByteArray(*byteArray)
byteArray[0] = 42
assertEquals(1, byteArray2[0], "Failed: ByteArray")
val charArray = charArrayOf('a')
val charArray2 = copyCharArray(*charArray)
charArray[0] = 'b'
assertEquals(charArray2[0], 'a', "Failed: CharArray")
val shortArray = shortArrayOf(1)
val shortArray2 = copyShortArray(*shortArray)
shortArray[0] = 42
assertEquals(1, shortArray2[0], "Failed: ShortArray")
val iarr = IntArray(1)
iarr[0] = 1
val iarr2 = copyIntArray(*iarr)
iarr[0] = 42
assertEquals(iarr2[0], 1, "Failed: IntArray")
assertEquals(1, iarr2[0], "Failed: IntArray")
val longArray = longArrayOf(1L)
val longArray2 = copyLongArray(*longArray)
longArray[0] = 42L
assertEquals(1L, longArray2[0], "Failed: LongArray")
val floatArray = floatArrayOf(1.0f)
val floatArray2 = copyFloatArray(*floatArray)
floatArray[0] = 42.0f
assertEquals(1.0f, floatArray2[0], "Failed: FloatArray")
val doubleArray = doubleArrayOf(1.0)
val doubleArray2 = copyDoubleArray(*doubleArray)
doubleArray[0] = 42.0
assertEquals(1.0, doubleArray2[0], "Failed: DoubleArray")
val stringArray = arrayOf("abc")
val stringArray2 = copyStringArray(*stringArray)
stringArray[0] = "def"
assertEquals("abc", stringArray2[0], "Failed: Array<String>")
return "OK"
}
+55
View File
@@ -0,0 +1,55 @@
FILE fqName:<root> fileName:/kt45853.kt
CLASS CLASS name:A modality:ABSTRACT visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.A
CONSTRUCTOR visibility:public <> () returnType:<root>.A [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:A modality:ABSTRACT visibility:public superTypes:[kotlin.Any]'
PROPERTY name:a visibility:public modality:ABSTRACT [val]
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-a> visibility:public modality:ABSTRACT <> ($this:<root>.A) returnType:<root>.A?
correspondingProperty: PROPERTY name:a visibility:public modality:ABSTRACT [val]
$this: VALUE_PARAMETER name:<this> type:<root>.A
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:B modality:FINAL visibility:public superTypes:[<root>.AX]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.B
CONSTRUCTOR visibility:public <> () returnType:<root>.B [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.AX'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:B modality:FINAL visibility:public superTypes:[<root>.AX]'
FUN name:getA visibility:public modality:FINAL <> ($this:<root>.B) returnType:<root>.X?
$this: VALUE_PARAMETER name:<this> type:<root>.B
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun getA (): <root>.X? declared in <root>.B'
CALL 'public open fun <get-a> (): @[FlexibleNullability] <root>.AX? declared in <root>.AX' superQualifier='CLASS IR_EXTERNAL_JAVA_DECLARATION_STUB CLASS name:AX modality:ABSTRACT visibility:public superTypes:[<root>.A; <root>.X]' type=@[FlexibleNullability] <root>.AX? origin=GET_PROPERTY
$this: GET_VAR '<this>: <root>.B declared in <root>.B.getA' type=<root>.B origin=null
PROPERTY FAKE_OVERRIDE name:a visibility:public modality:OPEN [fake_override,val]
FUN FAKE_OVERRIDE name:<get-a> visibility:public modality:OPEN <> ($this:<root>.AX) returnType:@[FlexibleNullability] <root>.AX? [fake_override]
correspondingProperty: PROPERTY FAKE_OVERRIDE name:a visibility:public modality:OPEN [fake_override,val]
overridden:
public open fun <get-a> (): @[FlexibleNullability] <root>.AX? declared in <root>.AX
$this: VALUE_PARAMETER name:<this> type:<root>.AX
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [fake_override,operator] declared in <root>.AX
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int [fake_override] declared in <root>.AX
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String [fake_override] declared in <root>.AX
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
@@ -40922,6 +40922,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/vararg/kt37779.kt");
}
@Test
@TestMetadata("kt45187.kt")
public void testKt45187() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt45187.kt");
}
@Test
@TestMetadata("kt581.kt")
public void testKt581() throws Exception {
@@ -40940,6 +40940,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/vararg/kt37779.kt");
}
@Test
@TestMetadata("kt45187.kt")
public void testKt45187() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt45187.kt");
}
@Test
@TestMetadata("kt581.kt")
public void testKt581() throws Exception {
@@ -32907,6 +32907,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/vararg/kt37779.kt");
}
@TestMetadata("kt45187.kt")
public void testKt45187() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt45187.kt");
}
@TestMetadata("kt581.kt")
public void testKt581() throws Exception {
runTest("compiler/testData/codegen/box/vararg/kt581.kt");