From 40229827608e21325980e3c7b00e8d116da5d33b Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 27 Feb 2015 19:07:24 +0300 Subject: [PATCH] Add synthetic argument to default constructors It's need to add synthetic argument (of type that user can't use) to constructors with default arguments to avoid clashing with real user's constructor having the same set of parameters and additional int's arguments. --- .../kotlin/codegen/CallableMethod.java | 1 + .../kotlin/codegen/FunctionCodegen.java | 4 +++ .../kotlin/codegen/state/JetTypeMapper.java | 14 ++++++-- .../clashingDefaultConstructors.kt | 35 +++++++++++++++++++ .../method/throws/DefaultArgs.java | 2 +- .../BlackBoxCodegenTestGenerated.java | 6 ++++ .../jetbrains/kotlin/load/java/JvmAbi.java | 2 +- .../internal/DefaultConstructorMarker.java | 21 +++++++++++ 8 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/box/secondaryConstructors/clashingDefaultConstructors.kt create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/DefaultConstructorMarker.java diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java index d92920f3bbc..e8fe7b5f6c7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java @@ -138,6 +138,7 @@ public class CallableMethod implements Callable { Method method = getAsmMethod(); String desc = JetTypeMapper.getDefaultDescriptor(method, receiverParameterType != null); if ("".equals(method.getName())) { + v.aconst(null); v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "", desc, false); } else { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 168d586455d..d3053162197 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -585,6 +585,10 @@ public class FunctionCodegen { for (int m : masks) { v.iconst(m); } + + // constructors with default arguments has last synthetic argument of specific type + v.aconst(null); + String desc = JetTypeMapper.getDefaultDescriptor(method.getAsmMethod(), false); v.invokespecial(methodOwner.getInternalName(), "", desc, false); v.areturn(Type.VOID_TYPE); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index 536058e3928..eec22104791 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -77,6 +77,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class JetTypeMapper { + private static final String DEFAULT_CONSTRUCTOR_MARKER_INTERNAL_CLASS_NAME = "kotlin/jvm/internal/DefaultConstructorMarker"; private final BindingContext bindingContext; private final ClassBuilderMode classBuilderMode; @@ -713,8 +714,15 @@ public class JetTypeMapper { argumentsCount--; } int maskArgumentsCount = (argumentsCount + Integer.SIZE - 1) / Integer.SIZE; - String maskArguments = StringUtil.repeat(Type.INT_TYPE.getDescriptor(), maskArgumentsCount); - return descriptor.replace(")", maskArguments + ")"); + String additionalArgs = StringUtil.repeat(Type.INT_TYPE.getDescriptor(), maskArgumentsCount); + if (isConstructor(method)) { + additionalArgs += Type.getObjectType(DEFAULT_CONSTRUCTOR_MARKER_INTERNAL_CLASS_NAME).getDescriptor(); + } + return descriptor.replace(")", additionalArgs + ")"); + } + + private static boolean isConstructor(@NotNull Method method) { + return "".equals(method.getName()); } @NotNull @@ -722,7 +730,7 @@ public class JetTypeMapper { Method jvmSignature = mapSignature(functionDescriptor, kind).getAsmMethod(); Type ownerType = mapOwner(functionDescriptor, isCallInsideSameModuleAsDeclared(functionDescriptor, context, getOutDirectory())); String descriptor = getDefaultDescriptor(jvmSignature, functionDescriptor.getExtensionReceiverParameter() != null); - boolean isConstructor = "".equals(jvmSignature.getName()); + boolean isConstructor = isConstructor(jvmSignature); if (!isStaticMethod(kind, functionDescriptor) && !isConstructor) { descriptor = descriptor.replace("(", "(" + ownerType.getDescriptor()); } diff --git a/compiler/testData/codegen/box/secondaryConstructors/clashingDefaultConstructors.kt b/compiler/testData/codegen/box/secondaryConstructors/clashingDefaultConstructors.kt new file mode 100644 index 00000000000..cb59b07a66f --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/clashingDefaultConstructors.kt @@ -0,0 +1,35 @@ +open class A(val x: String = "abc", val y: String = "efg") { + constructor(x: String, y: String, z: Int): this(x, y + "#" + z.toString()) {} + + override fun toString() = "$x#$y" +} + +class B : A { + constructor(x: String, y: String, z: Int): super(x, y + z.toString()) {} + constructor(x: String = "xyz", y: String = "123") : super(x, y) {} + constructor(x: Double): super(x.toString()) {} +} + +fun box(): String { + val a1 = A().toString() + if (a1 != "abc#efg") return "fail1: $a1" + + val a2 = A("hij", "klm", 1).toString() + if (a2 != "hij#klm#1") return "fail2: $a2" + + val a3 = A(x="xyz").toString() + if (a3 != "xyz#efg") return "fail3: $a3" + + val b1 = B().toString() + if (b1 != "xyz#123") return "fail4: $b1" + + val b2 = B("hij", "klm", 2).toString() + if (b2 != "hij#klm2") return "fail5: $b2" + + val b3 = B(123.0).toString() + if (b3 != "123.0#efg") return "fail6: $b3" + + val b4 = B(x="test").toString() + if (b4 != "test#123") return "fail7: $b4" + return "OK" +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java index 43c028fc18b..634b2cd5f52 100644 --- a/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java @@ -8,7 +8,7 @@ class JavaClass { catch (E1 e) {} try { - new One(1, 0); + new One(1, 0, null); } catch (E1 e) {} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index b335a130996..95431d9d2dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -6303,6 +6303,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("clashingDefaultConstructors.kt") + public void testClashingDefaultConstructors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/clashingDefaultConstructors.kt"); + doTest(fileName); + } + @TestMetadata("dataClasses.kt") public void testDataClasses() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java index d39d8d36684..c70aea75920 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java @@ -25,7 +25,7 @@ public final class JvmAbi { * This constant is used to identify binary format (class file) versions * If you change class file metadata format and/or naming conventions, please increase this number */ - public static final int VERSION = 20; + public static final int VERSION = 21; public static final String TRAIT_IMPL_CLASS_NAME = "$TImpl"; public static final String TRAIT_IMPL_SUFFIX = "$" + TRAIT_IMPL_CLASS_NAME; diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/DefaultConstructorMarker.java b/core/runtime.jvm/src/kotlin/jvm/internal/DefaultConstructorMarker.java new file mode 100644 index 00000000000..84ee1eab5e0 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/DefaultConstructorMarker.java @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +final class DefaultConstructorMarker { + private DefaultConstructorMarker() {} +}