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() {} +}