Do not copy immediately created arrays in spread arguments

E.g., 'foo(x = *intArrayOf(42))'.

 #KT-20462 Fixed
This commit is contained in:
Dmitry Petrov
2017-09-27 16:14:09 +03:00
parent 0d5a2a7e18
commit 73724bcdc7
8 changed files with 234 additions and 4 deletions
@@ -2656,6 +2656,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return cur;
}
private boolean canSkipArrayCopyForSpreadArgument(KtExpression spreadArgument) {
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(spreadArgument, bindingContext);
if (resolvedCall == null) return false;
CallableDescriptor calleeDescriptor = resolvedCall.getResultingDescriptor();
return (calleeDescriptor instanceof ConstructorDescriptor) ||
CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) ||
(DescriptorUtils.getFqName(calleeDescriptor).asString().equals("kotlin.arrayOfNulls"));
}
@NotNull
public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
@@ -2678,10 +2687,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (size == 1) {
Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType);
return StackValue.operation(type, adapter -> {
gen(arguments.get(0).getArgumentExpression(), type);
v.dup();
v.arraylength();
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
KtExpression spreadArgument = arguments.get(0).getArgumentExpression();
gen(spreadArgument, type);
if (!canSkipArrayCopyForSpreadArgument(spreadArgument)) {
v.dup();
v.arraylength();
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
}
if (arrayOfReferences) {
v.checkcast(type);
}
@@ -0,0 +1,102 @@
fun booleanVararg(vararg xs: Boolean) {
if (xs.size != 1 && xs[0] != true) throw AssertionError()
}
fun byteVararg(vararg xs: Byte) {
if (xs.size != 1 && xs[0] != 1.toByte()) throw AssertionError()
}
fun shortVararg(vararg xs: Short) {
if (xs.size != 1 && xs[0] != 1.toShort()) throw AssertionError()
}
fun intVararg(vararg xs: Int) {
if (xs.size != 1 && xs[0] != 1) throw AssertionError()
}
fun longVararg(vararg xs: Long) {
if (xs.size != 1 && xs[0] != 1L) throw AssertionError()
}
fun floatVararg(vararg xs: Float) {
if (xs.size != 1 && xs[0] != 1.0f) throw AssertionError()
}
fun doubleVararg(vararg xs: Double) {
if (xs.size != 1 && xs[0] != 1.0) throw AssertionError()
}
fun anyVararg(vararg xs: Any?) {
if (xs.size != 1 && (xs[0] != 1 && xs[0] != null)) throw AssertionError()
}
fun <T> genericVararg(vararg xs: T) {
if (xs.size != 1 && (xs[0] != 1 && xs[0] != null)) throw AssertionError()
}
fun box(): String {
booleanVararg(*booleanArrayOf(true))
booleanVararg(*BooleanArray(1))
booleanVararg(*BooleanArray(1) { true })
booleanVararg(xs = *booleanArrayOf(true))
booleanVararg(xs = *BooleanArray(1))
booleanVararg(xs = *BooleanArray(1) { true })
byteVararg(*byteArrayOf(1))
byteVararg(*ByteArray(1))
byteVararg(*ByteArray(1) { 1 })
byteVararg(xs = *byteArrayOf(1))
byteVararg(xs = *ByteArray(1))
byteVararg(xs = *ByteArray(1) { 1 })
shortVararg(*shortArrayOf(1))
shortVararg(*ShortArray(1))
shortVararg(*ShortArray(1) { 1 })
shortVararg(xs = *shortArrayOf(1))
shortVararg(xs = *ShortArray(1))
shortVararg(xs = *ShortArray(1) { 1 })
intVararg(*intArrayOf(1))
intVararg(*IntArray(1))
intVararg(*IntArray(1) { 1 })
intVararg(xs = *intArrayOf(1))
intVararg(xs = *IntArray(1))
intVararg(xs = *IntArray(1) { 1 })
longVararg(*longArrayOf(1L))
longVararg(*LongArray(1))
longVararg(*LongArray(1) { 1L })
longVararg(xs = *longArrayOf(1L))
longVararg(xs = *LongArray(1))
longVararg(xs = *LongArray(1) { 1L })
floatVararg(*floatArrayOf(1.0f))
floatVararg(*FloatArray(1))
floatVararg(*FloatArray(1) { 1.0f })
floatVararg(xs = *floatArrayOf(1.0f))
floatVararg(xs = *FloatArray(1))
floatVararg(xs = *FloatArray(1) { 1.0f })
doubleVararg(*doubleArrayOf(1.0))
doubleVararg(*DoubleArray(1))
doubleVararg(*DoubleArray(1) { 1.0 })
doubleVararg(xs = *doubleArrayOf(1.0))
doubleVararg(xs = *DoubleArray(1))
doubleVararg(xs = *DoubleArray(1) { 1.0 })
anyVararg(*arrayOf(1))
anyVararg(*Array(1) { 1 })
anyVararg(*arrayOfNulls(1))
anyVararg(xs = *arrayOf(1))
anyVararg(xs = *Array(1) { 1 })
anyVararg(xs = *arrayOfNulls(1))
genericVararg(*arrayOf(1))
genericVararg(*Array(1) { 1 })
genericVararg(*arrayOfNulls<Int>(1))
genericVararg(xs = *arrayOf(1))
genericVararg(xs = *Array(1) { 1 })
genericVararg(xs = *arrayOfNulls<Int>(1))
return "OK"
}
@@ -0,0 +1,77 @@
fun booleanVararg(vararg xs: Boolean) {}
fun byteVararg(vararg xs: Byte) {}
fun shortVararg(vararg xs: Short) {}
fun intVararg(vararg xs: Int) {}
fun longVararg(vararg xs: Long) {}
fun floatVararg(vararg xs: Float) {}
fun doubleVararg(vararg xs: Double) {}
fun anyVararg(vararg xs: Any?) {}
fun <T> genericVararg(vararg xs: T) {}
fun test() {
booleanVararg(*booleanArrayOf(true))
booleanVararg(*BooleanArray(1))
booleanVararg(*BooleanArray(1) { true })
booleanVararg(xs = *booleanArrayOf(true))
booleanVararg(xs = *BooleanArray(1))
booleanVararg(xs = *BooleanArray(1) { true })
byteVararg(*byteArrayOf(1))
byteVararg(*ByteArray(1))
byteVararg(*ByteArray(1) { 1 })
byteVararg(xs = *byteArrayOf(1))
byteVararg(xs = *ByteArray(1))
byteVararg(xs = *ByteArray(1) { 1 })
shortVararg(*shortArrayOf(1))
shortVararg(*ShortArray(1))
shortVararg(*ShortArray(1) { 1 })
shortVararg(xs = *shortArrayOf(1))
shortVararg(xs = *ShortArray(1))
shortVararg(xs = *ShortArray(1) { 1 })
intVararg(*intArrayOf(1))
intVararg(*IntArray(1))
intVararg(*IntArray(1) { 1 })
intVararg(xs = *intArrayOf(1))
intVararg(xs = *IntArray(1))
intVararg(xs = *IntArray(1) { 1 })
longVararg(*longArrayOf(1L))
longVararg(*LongArray(1))
longVararg(*LongArray(1) { 1L })
longVararg(xs = *longArrayOf(1L))
longVararg(xs = *LongArray(1))
longVararg(xs = *LongArray(1) { 1L })
floatVararg(*floatArrayOf(1.0f))
floatVararg(*FloatArray(1))
floatVararg(*FloatArray(1) { 1.0f })
floatVararg(xs = *floatArrayOf(1.0f))
floatVararg(xs = *FloatArray(1))
floatVararg(xs = *FloatArray(1) { 1.0f })
doubleVararg(*doubleArrayOf(1.0))
doubleVararg(*DoubleArray(1))
doubleVararg(*DoubleArray(1) { 1.0 })
doubleVararg(xs = *doubleArrayOf(1.0))
doubleVararg(xs = *DoubleArray(1))
doubleVararg(xs = *DoubleArray(1) { 1.0 })
anyVararg(*arrayOf(1))
anyVararg(*Array(1) { 1 })
anyVararg(*arrayOfNulls(1))
anyVararg(xs = *arrayOf(1))
anyVararg(xs = *Array(1) { 1 })
anyVararg(xs = *arrayOfNulls(1))
genericVararg(*arrayOf(1))
genericVararg(*Array(1) { 1 })
genericVararg(*arrayOfNulls<Int>(1))
genericVararg(xs = *arrayOf(1))
genericVararg(xs = *Array(1) { 1 })
genericVararg(xs = *arrayOfNulls<Int>(1))
}
// 0 copyOf
// 0 clone
@@ -19688,6 +19688,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/doNotCopyImmediatelyCreatedArrays.kt");
doTest(fileName);
}
@TestMetadata("kt1978.kt")
public void testKt1978() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/kt1978.kt");
@@ -19688,6 +19688,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/doNotCopyImmediatelyCreatedArrays.kt");
doTest(fileName);
}
@TestMetadata("kt1978.kt")
public void testKt1978() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/kt1978.kt");
@@ -2202,6 +2202,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/varargs")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Varargs extends AbstractBytecodeTextTest {
public void testAllFilesPresentInVarargs() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/varargs"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/varargs/doNotCopyImmediatelyCreatedArrays.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/when")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19688,6 +19688,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/doNotCopyImmediatelyCreatedArrays.kt");
doTest(fileName);
}
@TestMetadata("kt1978.kt")
public void testKt1978() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/kt1978.kt");
@@ -23666,6 +23666,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("doNotCopyImmediatelyCreatedArrays.kt")
public void testDoNotCopyImmediatelyCreatedArrays() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/doNotCopyImmediatelyCreatedArrays.kt");
doTest(fileName);
}
@TestMetadata("kt1978.kt")
public void testKt1978() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/vararg/kt1978.kt");