diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 62bf4712f7e..fc907090a7c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1960,9 +1960,32 @@ public class ExpressionCodegen extends JetVisitor implem return stackValueForLocal(descriptor, index); } + if (context instanceof ConstructorContext) { + return lookupCapturedValueInConstructorParameters(descriptor); + } + return context.lookupInContext(descriptor, StackValue.LOCAL_0, state, false); } + @Nullable + private StackValue lookupCapturedValueInConstructorParameters(@NotNull DeclarationDescriptor descriptor) { + StackValue parentResult = context.lookupInContext(descriptor, StackValue.LOCAL_0, state, false); + if (context.closure == null || parentResult == null) return parentResult; + + int parameterOffsetInConstructor = context.closure.getCapturedParameterOffsetInConstructor(descriptor); + // when captured parameter is singleton + // see compiler/testData/codegen/box/objects/objectInLocalAnonymousObject.kt (fun local() captured in A) + if (parameterOffsetInConstructor == -1) return parentResult; + + assert parentResult instanceof StackValue.Field || parentResult instanceof StackValue.FieldForSharedVar + : "Part of closure should be either Field or FieldForSharedVar"; + + if (parentResult instanceof StackValue.FieldForSharedVar) { + return StackValue.shared(parameterOffsetInConstructor, parentResult.type); + } + + return StackValue.local(parameterOffsetInConstructor, parentResult.type); + } private StackValue stackValueForLocal(DeclarationDescriptor descriptor, int index) { if (descriptor instanceof VariableDescriptor) { @@ -2207,8 +2230,9 @@ public class ExpressionCodegen extends JetVisitor implem } } - final Callable callable = resolveToCallable(accessibleFunctionDescriptor(fd), superCall); - final Type returnType = typeMapper.mapReturnType(resolvedCall.getResultingDescriptor()); + FunctionDescriptor accessibleFunctionDescriptor = accessibleFunctionDescriptor(fd); + final Callable callable = resolveToCallable(accessibleFunctionDescriptor, superCall); + final Type returnType = typeMapper.mapReturnType(accessibleFunctionDescriptor); if (callable instanceof CallableMethod) { return StackValue.functionCall(returnType, new Function1() { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 3e8dbab9d51..2f6c94a3662 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -334,7 +334,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } for (JetDelegationSpecifier specifier : delegationSpecifiers) { - if (specifier instanceof JetDelegatorToSuperCall) { + if (specifier instanceof JetDelegatorToSuperClass || specifier instanceof JetDelegatorToSuperCall) { JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference()); assert superType != null : String.format("No type recorded for \n---\n%s\n---\n", JetPsiUtil.getElementTextWithContext(specifier)); @@ -374,6 +374,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { try { lookupConstructorExpressionsInClosureIfPresent(); generatePrimaryConstructor(delegationFieldsInfo); + for (ConstructorDescriptor secondaryConstructor : descriptor.getConstructors()) { + if (secondaryConstructor.isPrimary()) continue; + generateSecondaryConstructor(secondaryConstructor); + } } catch (CompilationException e) { throw e; @@ -382,7 +386,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { throw e; } catch (RuntimeException e) { - throw new RuntimeException("Error generating primary constructor of class " + myClass.getName() + " with kind " + kind, e); + throw new RuntimeException("Error generating constructors of class " + myClass.getName() + " with kind " + kind, e); } generateTraitMethods(); @@ -1097,23 +1101,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } + private void generateSecondaryConstructor(@NotNull ConstructorDescriptor constructorDescriptor) { + ConstructorContext constructorContext = context.intoConstructor(constructorDescriptor); + + functionCodegen.generateMethod(OtherOrigin(myClass, constructorDescriptor), constructorDescriptor, constructorContext, + new FunctionGenerationStrategy.CodegenBased(state, constructorDescriptor) { + @Override + public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { + generateSecondaryConstructorImpl(callableDescriptor, codegen); + } + } + ); + + functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION, + DefaultParameterValueLoader.DEFAULT, null); + } + private void generatePrimaryConstructorImpl( @NotNull ConstructorDescriptor constructorDescriptor, - @NotNull final ExpressionCodegen codegen, + @NotNull ExpressionCodegen codegen, @NotNull DelegationFieldsInfo fieldsInfo ) { InstructionAdapter iv = codegen.v; - MutableClosure closure = context.closure; - if (closure != null) { - List argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, classAsmType); - int k = 1; - for (FieldInfo info : argsFromClosure) { - k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv); - } - } + generateClosureInitialization(iv); - generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor); + generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor, + getDelegationConstructorCall(bindingContext, constructorDescriptor)); if (isNonDefaultObject(descriptor)) { StackValue.singleton(descriptor, typeMapper).store(StackValue.LOCAL_0, iv); @@ -1151,17 +1165,58 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { }); } else { - generateInitializers(new Function0() { - @Override - public ExpressionCodegen invoke() { - return codegen; - } - }); + generateInitializers(codegen); } iv.visitInsn(RETURN); } + private void generateSecondaryConstructorImpl( + @NotNull ConstructorDescriptor constructorDescriptor, + @NotNull ExpressionCodegen codegen + ) { + InstructionAdapter iv = codegen.v; + + ResolvedCall constructorDelegationCall = + getDelegationConstructorCall(bindingContext, constructorDescriptor); + ConstructorDescriptor delegateConstructor = constructorDelegationCall == null ? null : + constructorDelegationCall.getResultingDescriptor(); + + generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor, constructorDelegationCall); + if (!isSameClassConstructor(delegateConstructor)) { + // Initialization happens only for constructors delegating to super + generateClosureInitialization(iv); + generateInitializers(codegen); + } + + JetSecondaryConstructor constructor = + (JetSecondaryConstructor) DescriptorToSourceUtils.descriptorToDeclaration(constructorDescriptor); + assert constructor != null; + codegen.gen(constructor.getBodyExpression(), Type.VOID_TYPE); + + iv.visitInsn(RETURN); + } + + private void generateInitializers(@NotNull final ExpressionCodegen codegen) { + generateInitializers(new Function0() { + @Override + public ExpressionCodegen invoke() { + return codegen; + } + }); + } + + private void generateClosureInitialization(@NotNull InstructionAdapter iv) { + MutableClosure closure = context.closure; + if (closure != null) { + List argsFromClosure = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, classAsmType); + int k = 1; + for (FieldInfo info : argsFromClosure) { + k = AsmUtil.genAssignInstanceFieldFromParam(info, k, iv); + } + } + } + private void genSimpleSuperCall(InstructionAdapter iv) { iv.load(0, superClassAsmType); if (descriptor.getKind() == ClassKind.ENUM_CLASS || descriptor.getKind() == ClassKind.ENUM_ENTRY) { @@ -1316,6 +1371,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { JetClassInitializer initializer = (JetClassInitializer) declaration; initializer.accept(visitor); } + else if (declaration instanceof JetSecondaryConstructor) { + JetSecondaryConstructor constructor = (JetSecondaryConstructor) declaration; + constructor.accept(visitor); + } } for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) { @@ -1332,9 +1391,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { context.lookupInContext(superClass.getContainingDeclaration(), StackValue.LOCAL_0, state, true); } - if (!isAnonymousObject(descriptor)) { - ResolvedCall delegationCall = - getDelegationConstructorCall(bindingContext, descriptor.getUnsubstitutedPrimaryConstructor()); + ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor(); + if (primaryConstructor != null && !isAnonymousObject(descriptor)) { + ResolvedCall delegationCall = getDelegationConstructorCall(bindingContext, primaryConstructor); JetValueArgumentList argumentList = delegationCall != null ? delegationCall.getCall().getValueArgumentList() : null; if (argumentList != null) { argumentList.accept(visitor); @@ -1415,22 +1474,54 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateDelegatorToConstructorCall( @NotNull InstructionAdapter iv, @NotNull ExpressionCodegen codegen, - @NotNull ConstructorDescriptor constructorDescriptor + @NotNull ConstructorDescriptor constructorDescriptor, + @Nullable ResolvedCall delegationConstructorCall ) { - ResolvedCall resolvedCall = getDelegationConstructorCall(bindingContext, constructorDescriptor); - if (resolvedCall == null) { + if (delegationConstructorCall == null) { genSimpleSuperCall(iv); return; } iv.load(0, OBJECT_TYPE); - ConstructorDescriptor superConstructor = (ConstructorDescriptor) resolvedCall.getResultingDescriptor(); + ConstructorDescriptor delegateConstructor = delegationConstructorCall.getResultingDescriptor(); - CallableMethod superCallable = typeMapper.mapToCallableMethod(superConstructor); + CallableMethod delegateConstructorCallable = typeMapper.mapToCallableMethod(delegateConstructor); CallableMethod callable = typeMapper.mapToCallableMethod(constructorDescriptor); - List superParameters = superCallable.getValueParameters(); + List delegatingParameters = delegateConstructorCallable.getValueParameters(); List parameters = callable.getValueParameters(); + ArgumentGenerator argumentGenerator; + if (isSameClassConstructor(delegateConstructor)) { + // if it's the same class constructor we should just pass all synthetic parameters + argumentGenerator = + generateThisCallImplicitArguments(iv, codegen, delegateConstructor, delegateConstructorCallable, delegatingParameters, + parameters); + } + else { + argumentGenerator = + generateSuperCallImplicitArguments(iv, codegen, constructorDescriptor, delegateConstructor, delegateConstructorCallable, + delegatingParameters, + parameters); + } + + codegen.invokeMethodWithArguments( + delegateConstructorCallable, delegationConstructorCall, StackValue.none(), codegen.defaultCallGenerator, argumentGenerator); + } + + private boolean isSameClassConstructor(@Nullable ConstructorDescriptor delegatingConstructor) { + return delegatingConstructor != null && delegatingConstructor.getContainingDeclaration() == descriptor; + } + + @NotNull + private ArgumentGenerator generateSuperCallImplicitArguments( + @NotNull InstructionAdapter iv, + @NotNull ExpressionCodegen codegen, + @NotNull ConstructorDescriptor constructorDescriptor, + @NotNull ConstructorDescriptor superConstructor, + @NotNull CallableMethod superCallable, + @NotNull List superParameters, + @NotNull List parameters + ) { int offset = 1; int superIndex = 0; @@ -1469,18 +1560,47 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { offset += type.getSize(); } - ArgumentGenerator argumentGenerator; if (isAnonymousObject(descriptor)) { List superValues = superParameters.subList(superIndex, superParameters.size()); - argumentGenerator = new ObjectSuperCallArgumentGenerator(superValues, iv, offset); + return new ObjectSuperCallArgumentGenerator(superValues, iv, offset); } else { - argumentGenerator = - new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, superConstructor.getValueParameters(), + return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, superConstructor.getValueParameters(), superCallable.getValueParameterTypes()); } + } - codegen.invokeMethodWithArguments(superCallable, resolvedCall, StackValue.none(), codegen.defaultCallGenerator, argumentGenerator); + @NotNull + private static ArgumentGenerator generateThisCallImplicitArguments( + @NotNull InstructionAdapter iv, + @NotNull ExpressionCodegen codegen, + @NotNull ConstructorDescriptor delegatingConstructor, + @NotNull CallableMethod delegatingCallable, + @NotNull List delegatingParameters, + @NotNull List parameters + ) { + int offset = 1; + int index = 0; + for (; index < delegatingParameters.size(); index++) { + JvmMethodParameterKind delegatingKind = delegatingParameters.get(index).getKind(); + if (delegatingKind == JvmMethodParameterKind.VALUE) { + assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE: + "Delegating constructor has not enough implicit parameters"; + break; + } + assert index < parameters.size() && parameters.get(index).getKind() == delegatingKind : + "Constructors of the same class should have the same set of implicit arguments"; + JvmMethodParameterSignature parameter = parameters.get(index); + + iv.load(offset, parameter.getAsmType()); + offset += parameter.getAsmType().getSize(); + } + + assert index == parameters.size() || parameters.get(index).getKind() == JvmMethodParameterKind.VALUE : + "Delegating constructor has not enough parameters"; + + return new CallBasedArgumentGenerator(codegen, codegen.defaultCallGenerator, delegatingConstructor.getValueParameters(), + delegatingCallable.getValueParameterTypes()); } private static class ObjectSuperCallArgumentGenerator extends ArgumentGenerator { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java index d6add3408eb..ae33343bdea 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor; import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.org.objectweb.asm.Type; @@ -37,6 +36,7 @@ public final class MutableClosure implements CalculatedClosure { private boolean captureReceiver; private Map captureVariables; + private Map parameterOffsetInConstructor; private List> recordedFields; MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) { @@ -114,6 +114,18 @@ public final class MutableClosure implements CalculatedClosure { captureVariables.put(value.getDescriptor(), value); } + public void setCapturedParameterOffsetInConstructor(DeclarationDescriptor descriptor, int offset) { + if (parameterOffsetInConstructor == null) { + parameterOffsetInConstructor = new LinkedHashMap(); + } + parameterOffsetInConstructor.put(descriptor, offset); + } + + public int getCapturedParameterOffsetInConstructor(DeclarationDescriptor descriptor) { + Integer result = parameterOffsetInConstructor != null ? parameterOffsetInConstructor.get(descriptor) : null; + return result != null ? result.intValue() : -1; + } + @Nullable public ReceiverParameterDescriptor getEnclosingReceiverDescriptor() { return enclosingFunWithReceiverDescriptor != null ? enclosingFunWithReceiverDescriptor.getExtensionReceiverParameter() : null; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java index 155e20b1c9b..f7ef4ab2e15 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java @@ -59,6 +59,8 @@ public class BothSignatureWriter { private boolean generic = false; + private int currentSignatureSize = 0; + public BothSignatureWriter(@NotNull Mode mode) { this.signatureVisitor = new CheckSignatureAdapter(mode.asmType, signatureWriter); } @@ -210,6 +212,7 @@ public class BothSignatureWriter { pop(); kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind)); + currentSignatureSize += jvmCurrentType.getSize(); currentParameterKind = null; jvmCurrentType = null; @@ -260,6 +263,9 @@ public class BothSignatureWriter { return new JvmMethodSignature(asmMethod, makeJavaGenericSignature(), kotlinParameterTypes); } + public int getCurrentSignatureSize() { + return currentSignatureSize; + } @Override public String toString() { 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 962843f6520..536058e3928 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -23,8 +23,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicObjects; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.codegen.*; -import org.jetbrains.kotlin.codegen.binding.CalculatedClosure; import org.jetbrains.kotlin.codegen.binding.CodegenBinding; +import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.binding.PsiCodegenPredictor; import org.jetbrains.kotlin.codegen.context.CodegenContext; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; @@ -168,6 +168,11 @@ public class JetTypeMapper { private Type mapReturnType(@NotNull CallableDescriptor descriptor, @Nullable BothSignatureWriter sw) { JetType returnType = descriptor.getReturnType(); assert returnType != null : "Function has no return type: " + descriptor; + + if (descriptor instanceof ConstructorDescriptor) { + return Type.VOID_TYPE; + } + if (returnType.equals(KotlinBuiltIns.getInstance().getUnitType()) && !TypeUtils.isNullableType(returnType) && !(descriptor instanceof PropertyGetterDescriptor)) { @@ -854,7 +859,7 @@ public class JetTypeMapper { } private void writeAdditionalConstructorParameters(@NotNull ConstructorDescriptor descriptor, @NotNull BothSignatureWriter sw) { - CalculatedClosure closure = bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()); + MutableClosure closure = bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()); ClassDescriptor captureThis = getDispatchReceiverParameterForConstructorCall(descriptor, closure); if (captureThis != null) { @@ -892,14 +897,16 @@ public class JetTypeMapper { } if (type != null) { + closure.setCapturedParameterOffsetInConstructor(variableDescriptor, sw.getCurrentSignatureSize() + 1); writeParameter(sw, JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE, type); } } - ResolvedCall superCall = getDelegationConstructorCall(bindingContext, descriptor); // We may generate a slightly wrong signature for a local class / anonymous object in light classes mode but we don't care, // because such classes are not accessible from the outside world - if (superCall != null && classBuilderMode == ClassBuilderMode.FULL) { + if (classBuilderMode == ClassBuilderMode.FULL) { + ResolvedCall superCall = findFirstDelegatingSuperCall(descriptor); + if (superCall == null) return; writeSuperConstructorCallParameters(sw, descriptor, superCall, captureThis != null); } } @@ -945,6 +952,17 @@ public class JetTypeMapper { } } + @Nullable + private ResolvedCall findFirstDelegatingSuperCall(@NotNull ConstructorDescriptor descriptor) { + ClassDescriptor classDescriptor = descriptor.getContainingDeclaration(); + while (true) { + ResolvedCall next = getDelegationConstructorCall(bindingContext, descriptor); + if (next == null) return null; + descriptor = next.getResultingDescriptor(); + if (descriptor.getContainingDeclaration() != classDescriptor) return next; + } + } + @NotNull public JvmMethodSignature mapScriptSignature(@NotNull ScriptDescriptor script, @NotNull List importedScripts) { BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD); diff --git a/compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt b/compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt new file mode 100644 index 00000000000..3f76b76193e --- /dev/null +++ b/compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt @@ -0,0 +1,24 @@ +fun run(block: () -> R) = block() +inline fun inlineRun(block: () -> R) = block() + +class Outer(val outerProp: String) { + fun foo(arg: String): String { + class Local { + val work1 = run { outerProp + arg } + val work2 = inlineRun { outerProp + arg } + val obj = object : Any() { + override fun toString() = outerProp + arg + } + + override fun toString() = "${work1}#${work2}#${obj.toString()}" + } + + return Local().toString() + } +} + +fun box(): String { + val res = Outer("O").foo("K") + if (res != "OK#OK#OK") return "fail: $res" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryManySinks.kt b/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryManySinks.kt new file mode 100644 index 00000000000..862ed7e242c --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryManySinks.kt @@ -0,0 +1,35 @@ +var sideEffects: String = "" + +class A { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor(x: String) { + prop = x + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int) { + prop += "$x#int" + sideEffects += "#fourth" + } +} + +fun box(): String { + val a1 = A("abc") + if (a1.prop != "abc") return "fail1: ${a1.prop}" + if (sideEffects != "first#second#third") return "fail1-sideEffects: ${sideEffects}" + + sideEffects = "" + val a2 = A(123) + if (a2.prop != "123#int") return "fail2: ${a2.prop}" + if (sideEffects != "first#second#fourth") return "fail2-sideEffects: ${sideEffects}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryOneSink.kt b/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryOneSink.kt new file mode 100644 index 00000000000..77a553ff7c3 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryOneSink.kt @@ -0,0 +1,41 @@ +var sideEffects: String = "" + +class A { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor() {} + + constructor(x: String): this() { + prop = x + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): this(x.toString()) { + prop += "#int" + sideEffects += "#fourth" + } +} + +fun box(): String { + val a1 = A("abc") + if (a1.prop != "abc") return "fail1: ${a1.prop}" + if (sideEffects != "first#second#third") return "fail1-sideEffects: ${sideEffects}" + + sideEffects = "" + val a2 = A(123) + if (a2.prop != "123#int") return "fail2: ${a2.prop}" + if (sideEffects != "first#second#third#fourth") return "fail2-sideEffects: ${sideEffects}" + + sideEffects = "" + val a3 = A() + if (a3.prop != "") return "fail2: ${a3.prop}" + if (sideEffects != "first#second") return "fail3-sideEffects: ${sideEffects}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/basicPrimary.kt b/compiler/testData/codegen/box/secondaryConstructors/basicPrimary.kt new file mode 100644 index 00000000000..6195900e1d8 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/basicPrimary.kt @@ -0,0 +1,39 @@ +var sideEffects: String = "" + +class A() { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor(x: String): this() { + prop = x + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): this(x.toString()) { + prop += "#int" + sideEffects += "#fourth" + } +} + +fun box(): String { + val a1 = A("abc") + if (a1.prop != "abc") return "fail1: ${a1.prop}" + if (sideEffects != "first#second#third") return "fail1-sideEffects: ${sideEffects}" + + sideEffects = "" + val a2 = A(123) + if (a2.prop != "123#int") return "fail2: ${a2.prop}" + if (sideEffects != "first#second#third#fourth") return "fail2-sideEffects: ${sideEffects}" + + sideEffects = "" + val a3 = A() + if (a3.prop != "") return "fail2: ${a3.prop}" + if (sideEffects != "first#second") return "fail3-sideEffects: ${sideEffects}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt b/compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt new file mode 100644 index 00000000000..1db78322746 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt @@ -0,0 +1,53 @@ +data class A1(val prop1: String) { + val prop2: String = "const2" + var prop3: String = "" + + constructor(): this("default") { + prop3 = "empty" + } + constructor(x: Int): this(x.toString()) { + prop3 = "int" + } + + fun f(): String = "$prop1#$prop2#$prop3" +} + +data class A2 private () { + var prop1: String = "" + var prop2: String = "const2" + var prop3: String = "" + + constructor(arg: String): this() { + prop1 = arg + } + constructor(x: Double): this() { + prop1 = "default" + prop3 = "empty" + } + constructor(x: Int): this(x.toString()) { + prop3 = "int" + } + + fun f(): String = "$prop1#$prop2#$prop3" +} + +fun box(): String { + val a1x = A1("asd") + if (a1x.f() != "asd#const2#") return "fail1: ${a1x.f()}" + if (a1x.toString() != "A1(prop1=asd)") return "fail1s: ${a1x.toString()}" + val a1y = A1() + if (a1y.f() != "default#const2#empty") return "fail2: ${a1y.f()}" + if (a1y.toString() != "A1(prop1=default)") return "fail2s: ${a1y.toString()}" + val a1z = A1(5) + if (a1z.f() != "5#const2#int") return "fail3: ${a1z.f()}" + if (a1z.toString() != "A1(prop1=5)") return "fail3s: ${a1z.toString()}" + + val a2x = A2("asd") + if (a2x.f() != "asd#const2#") return "fail4: ${a2x.f()}" + val a2y = A2(123.0) + if (a2y.f() != "default#const2#empty") return "fail5: ${a2y.f()}" + val a2z = A2(5) + if (a2z.f() != "5#const2#int") return "fail6: ${a2z.f()}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/defaultArgs.kt b/compiler/testData/codegen/box/secondaryConstructors/defaultArgs.kt new file mode 100644 index 00000000000..a7984976b0e --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/defaultArgs.kt @@ -0,0 +1,34 @@ +val global = "OK" +class A { + val prop: String + constructor(arg1: String = global) { + prop = arg1 + } + constructor(arg1: String = global, arg2: Long) { + prop = "$arg1#$arg2" + } + constructor(arg1: String = global, argDouble: Double, arg3: Long = 1L) { + prop = "$arg1#$argDouble#$arg3" + } +} + +fun box(): String { + val a1 = A() + if (a1.prop != "OK") return "fail1: ${a1.prop}" + val a2 = A("A") + if (a2.prop != "A") return "fail2: ${a2.prop}" + + val a3 = A(arg2=123) + if (a3.prop != "OK#123") return "fail3: ${a3.prop}" + val a4 = A("A", arg2=123) + if (a4.prop != "A#123") return "fail4: ${a4.prop}" + + val a5 = A(argDouble=23.0) + if (a5.prop != "OK#23.0#1") return "fail5: ${a5.prop}" + val a6 = A("A", argDouble=23.0) + if (a6.prop != "A#23.0#1") return "fail6: ${a6.prop}" + val a7 = A("A", arg3=2L, argDouble=23.0) + if (a7.prop != "A#23.0#2") return "fail7: ${a7.prop}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/enums.kt b/compiler/testData/codegen/box/secondaryConstructors/enums.kt new file mode 100644 index 00000000000..5c88c89f8d2 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/enums.kt @@ -0,0 +1,62 @@ +enum class A1(val prop1: String) { + X: A1("asd") + Y: A1() { + override fun f() = super.f() + "#Y" + } + Z: A1(5) + + val prop2: String = "const2" + var prop3: String = "" + + constructor(): this("default") { + prop3 = "empty" + } + constructor(x: Int): this(x.toString()) { + prop3 = "int" + } + + open fun f(): String = "$prop1#$prop2#$prop3" +} + +enum class A2 { + val prop1: String + X: A2("asd") + Y: A2() { + override fun f() = super.f() + "#Y" + } + Z: A2(5) + + val prop2: String = "const2" + var prop3: String = "" + + constructor(arg: String) { + prop1 = arg + } + constructor() { + prop1 = "default" + prop3 = "empty" + } + constructor(x: Int): this(x.toString()) { + prop3 = "int" + } + + open fun f(): String = "$prop1#$prop2#$prop3" +} + +fun box(): String { + val a1x = A1.X + if (a1x.f() != "asd#const2#") return "fail1: ${a1x.f()}" + val a1y = A1.Y + if (a1y.f() != "default#const2#empty#Y") return "fail2: ${a1y.f()}" + val a1z = A1.Z + if (a1z.f() != "5#const2#int") return "fail3: ${a1z.f()}" + + val a2x = A2.X + if (a2x.f() != "asd#const2#") return "fail4: ${a2x.f()}" + val a2y = A2.Y + if (a2y.f() != "default#const2#empty#Y") return "fail5: ${a2y.f()}" + val a2z = A2.Z + if (a2z.f() != "5#const2#int") return "fail6: ${a2z.f()}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/generics.kt b/compiler/testData/codegen/box/secondaryConstructors/generics.kt new file mode 100644 index 00000000000..c00ff9fa401 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/generics.kt @@ -0,0 +1,23 @@ +open class B(val x: T, val y: T) { + constructor(x: T): this(x, x) {} + override fun toString() = "$x#$y" +} + +class A : B { + constructor(): super("default") {} + constructor(x: String): super(x, "default") {} +} + +fun box(): String { + val b1 = B("1", "2").toString() + if (b1 != "1#2") return "fail1: $b1" + val b2 = B("abc").toString() + if (b2 != "abc#abc") return "fail2: $b2" + + val a1 = A().toString() + if (a1 != "default#default") return "fail3: $a1" + val a2 = A("xyz").toString() + if (a2 != "xyz#default") return "fail4: $a2" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt b/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt new file mode 100644 index 00000000000..9d0851c426b --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt @@ -0,0 +1,79 @@ +class Outer { + val outerProp: String + constructor(x: String) { + outerProp = x + } + + var sideEffects = "" + + inner class A1() { + var prop: String = "" + init { + sideEffects += outerProp + "#" + prop + "first" + } + + constructor(x: String): this() { + prop = x + "#${outerProp}" + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): this(x.toString() + "#" + outerProp) { + prop += "#int" + sideEffects += "#fourth" + } + } + + inner class A2 { + var prop: String = "" + init { + sideEffects += outerProp + "#" + prop + "first" + } + + constructor(x: String) { + prop = x + "#$outerProp" + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int) { + prop += "$x#$outerProp#int" + sideEffects += "#fourth" + } + } +} + +fun box(): String { + val outer1 = Outer("propValue1") + val a1 = outer1.A1("abc") + if (a1.prop != "abc#propValue1") return "fail1: ${a1.prop}" + if (outer1.sideEffects != "propValue1#first#second#third") return "fail1-sideEffects: ${outer1.sideEffects}" + + val outer2 = Outer("propValue2") + val a2 = outer2.A1(123) + if (a2.prop != "123#propValue2#propValue2#int") return "fail2: ${a2.prop}" + if (outer2.sideEffects != "propValue2#first#second#third#fourth") return "fail2-sideEffects: ${outer2.sideEffects}" + + val outer3 = Outer("propValue3") + val a3 = outer3.A1() + if (a3.prop != "") return "fail2: ${a3.prop}" + if (outer3.sideEffects != "propValue3#first#second") return "fail3-sideEffects: ${outer3.sideEffects}" + + val outer4 = Outer("propValue4") + val a4 = outer4.A2("abc") + if (a4.prop != "abc#propValue4") return "fail4: ${a4.prop}" + if (outer4.sideEffects != "propValue4#first#second#third") return "fail4-sideEffects: ${outer4.sideEffects}" + + val outer5 = Outer("propValue5") + val a5 = outer5.A2(123) + if (a5.prop != "123#propValue5#int") return "fail5: ${a5.prop}" + if (outer5.sideEffects != "propValue5#first#second#fourth") return "fail5-sideEffects: ${outer5.sideEffects}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt b/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt new file mode 100644 index 00000000000..f68b538d5ef --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt @@ -0,0 +1,66 @@ +class Outer { + val outerProp: String + constructor(x: String) { + outerProp = x + } + + var sideEffects = "" + + abstract inner class A1 { + var parentProp: String = "" + init { + sideEffects += outerProp + "#" + parentProp + "first" + } + + protected constructor(x: String) { + parentProp = x + "#${outerProp}" + sideEffects += "#second#" + } + + init { + sideEffects += parentProp + "#third" + } + + protected constructor(x: Int): this(x.toString() + "#" + outerProp) { + parentProp += "#int" + sideEffects += "fourth#" + } + } + + inner class A2 : A1 { + var prop: String = "" + init { + sideEffects += outerProp + "#" + prop + "fifth" + } + + constructor(x: String): super(x + "#" + outerProp) { + prop = x + "#$outerProp" + sideEffects += "#sixth" + } + + init { + sideEffects += prop + "#seventh" + } + + constructor(x: Int): super(x + 1) { + prop += "$x#$outerProp#int" + sideEffects += "#eighth" + } + } +} + +fun box(): String { + val outer1 = Outer("propValue1") + val a1 = outer1.A2("abc") + if (a1.parentProp != "abc#propValue1#propValue1") return "fail1: ${a1.parentProp}" + if (a1.prop != "abc#propValue1") return "fail2: ${a1.prop}" + if (outer1.sideEffects != "propValue1#first#third#second#propValue1#fifth#seventh#sixth") return "fail1-sideEffects: ${outer1.sideEffects}" + + val outer2 = Outer("propValue2") + val a2 = outer2.A2(123) + if (a2.parentProp != "124#propValue2#propValue2#int") return "fail3: ${a2.parentProp}" + if (a2.prop != "123#propValue2#int") return "fail4: ${a2.prop}" + if (outer2.sideEffects != "propValue2#first#third#second#fourth#propValue2#fifth#seventh#eighth") return "fail2-sideEffects: ${outer2.sideEffects}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt b/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt new file mode 100644 index 00000000000..fb26a6ba6aa --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt @@ -0,0 +1,77 @@ +open class C(val grandParentProp: String) +fun box(): String { + var sideEffects: String = "" + var parentSideEffects: String = "" + val justForUsageInClosure = 7 + val justForUsageInParentClosure = "parentCaptured" + + abstract class B : C { + val parentProp: String + init { + sideEffects += "minus-one#" + parentSideEffects += "1" + } + protected constructor(arg: Int): super(justForUsageInParentClosure) { + parentProp = (arg).toString() + sideEffects += "0.5#" + parentSideEffects += "#" + justForUsageInParentClosure + } + protected constructor(arg1: Int, arg2: Int): super(justForUsageInParentClosure) { + parentProp = (arg1 + arg2).toString() + sideEffects += "0.7#" + parentSideEffects += "#3" + } + init { + sideEffects += "zero#" + parentSideEffects += "#4" + } + } + + class A : B { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor(x1: Int, x2: Int): super(x1, x2) { + prop = x1.toString() + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): super(justForUsageInClosure + x) { + prop += "${x}#int" + sideEffects += "#fourth" + } + + constructor(): this(justForUsageInClosure) { + sideEffects += "#fifth" + } + + override fun toString() = "$prop#$parentProp#$grandParentProp" + } + + val a1 = A(5, 10).toString() + if (a1 != "5#15#parentCaptured") return "fail1: $a1" + if (sideEffects != "minus-one#zero#0.7#first#second#third") return "fail2: ${sideEffects}" + if (parentSideEffects != "1#4#3") return "fail3: ${parentSideEffects}" + + sideEffects = "" + parentSideEffects = "" + val a2 = A(123).toString() + if (a2 != "123#int#130#parentCaptured") return "fail1: $a2" + if (sideEffects != "minus-one#zero#0.5#first#second#fourth") return "fail4: ${sideEffects}" + if (parentSideEffects != "1#4#parentCaptured") return "fail5: ${parentSideEffects}" + + sideEffects = "" + parentSideEffects = "" + val a3 = A().toString() + if (a3 != "7#int#14#parentCaptured") return "fail6: $a3" + if (sideEffects != "minus-one#zero#0.5#first#second#fourth#fifth") return "fail7: ${sideEffects}" + if (parentSideEffects != "1#4#parentCaptured") return "fail8: ${parentSideEffects}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt b/compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt new file mode 100644 index 00000000000..3fe1038172e --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt @@ -0,0 +1,53 @@ +var sideEffects: String = "" + +abstract class B protected(val arg: Int) { + val parentProp: String + init { + sideEffects += "zero#" + parentProp = arg.toString() + } +} + +class A(x: Boolean) : B(if (x) 1 else 2) { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor(x: String): this(x == "abc") { + prop = x + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): this(x < 0) { + prop += "${x}#int" + sideEffects += "#fourth" + } +} + +fun box(): String { + val a1 = A("abc") + if (a1.prop != "abc") return "fail0: ${a1.prop}" + if (a1.parentProp != "1") return "fail1: ${a1.parentProp}" + if (a1.arg != 1) return "fail1': ${a1.arg}" + if (sideEffects != "zero#first#second#third") return "fail2: ${sideEffects}" + + sideEffects = "" + val a2 = A(123) + if (a2.prop != "123#int") return "fail3: ${a2.prop}" + if (a2.parentProp != "2") return "fail4: ${a2.parentProp}" + if (a2.arg != 2) return "fail5': ${a2.arg}" + if (sideEffects != "zero#first#second#fourth") return "fail6: ${sideEffects}" + + sideEffects = "" + val a3 = A(false) + if (a3.prop != "") return "fail7: ${a3.prop}" + if (a3.parentProp != "2") return "fail8: ${a3.parentProp}" + if (a3.arg != 2) return "fail9': ${a3.arg}" + if (sideEffects != "zero#first#second") return "fail10: ${sideEffects}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt b/compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt new file mode 100644 index 00000000000..6dd64d5a921 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt @@ -0,0 +1,64 @@ +var sideEffects: String = "" + +abstract class B { + val parentProp: String + init { + sideEffects += "minus-one#" + } + protected constructor(arg: Int) { + parentProp = (arg).toString() + sideEffects += "0.5#" + } + protected constructor(arg1: Int, arg2: Int) { + parentProp = (arg1 + arg2).toString() + sideEffects += "0.7#" + } + init { + sideEffects += "zero#" + } +} + +class A : B { + var prop: String = "" + init { + sideEffects += prop + "first" + } + + constructor(x1: Int, x2: Int): super(x1, x2) { + prop = x1.toString() + sideEffects += "#third" + } + + init { + sideEffects += prop + "#second" + } + + constructor(x: Int): super(3 + x) { + prop += "${x}#int" + sideEffects += "#fourth" + } + + constructor(): this(7) { + sideEffects += "#fifth" + } +} + +fun box(): String { + val a1 = A(5, 10) + if (a1.prop != "5") return "fail0: ${a1.prop}" + if (a1.parentProp != "15") return "fail1: ${a1.parentProp}" + if (sideEffects != "minus-one#zero#0.7#first#second#third") return "fail2: ${sideEffects}" + + sideEffects = "" + val a2 = A(123) + if (a2.prop != "123#int") return "fail3: ${a2.prop}" + if (a2.parentProp != "126") return "fail4: ${a2.parentProp}" + if (sideEffects != "minus-one#zero#0.5#first#second#fourth") return "fail5: ${sideEffects}" + + sideEffects = "" + val a3 = A() + if (a3.prop != "7#int") return "fail6: ${a3.prop}" + if (a3.parentProp != "10") return "fail7: ${a3.parentProp}" + if (sideEffects != "minus-one#zero#0.5#first#second#fourth#fifth") return "fail8: ${sideEffects}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/varargs.kt b/compiler/testData/codegen/box/secondaryConstructors/varargs.kt new file mode 100644 index 00000000000..39bfb8bf7a9 --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/varargs.kt @@ -0,0 +1,31 @@ +fun join(x: Array): String { + var result = "" + for (i in x) { + result += i + result += "#" + } + + return result +} + +open class B { + val parentProp: String + constructor(vararg x: String) { + parentProp = join(x) + } +} + +class A : B { + val prop: String + constructor(vararg x: String): super("0", *x, "4") { + prop = join(x) + } +} + +fun box(): String { + val a1 = A("1", "2", "3") + if (a1.prop != "1#2#3#") return "fail1: ${a1.prop}" + if (a1.parentProp != "0#1#2#3#4#") return "fail2: ${a1.parentProp}" + + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/withNonLocalReturn.kt b/compiler/testData/codegen/box/secondaryConstructors/withNonLocalReturn.kt new file mode 100644 index 00000000000..0b0be6bbbac --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/withNonLocalReturn.kt @@ -0,0 +1,21 @@ +inline fun run(block: () -> Unit) = block() + +class A { + val prop: Int + constructor(arg: Boolean) { + if (arg) { + prop = 1 + run { return } + throw RuntimeException("fail 0") + } + prop = 2 + } +} + +fun box(): String { + val a1 = A(true) + if (a1.prop != 1) return "fail1: ${a1.prop}" + val a2 = A(false) + if (a2.prop != 2) return "fail2: ${a2.prop}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/withReturn.kt b/compiler/testData/codegen/box/secondaryConstructors/withReturn.kt new file mode 100644 index 00000000000..b6cb18cf2af --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/withReturn.kt @@ -0,0 +1,18 @@ +class A { + val prop: Int + constructor(arg: Boolean) { + if (arg) { + prop = 1 + return + } + prop = 2 + } +} + +fun box(): String { + val a1 = A(true) + if (a1.prop != 1) return "fail1: ${a1.prop}" + val a2 = A(false) + if (a2.prop != 2) return "fail2: ${a2.prop}" + return "OK" +} diff --git a/compiler/testData/codegen/box/secondaryConstructors/withReturnUnit.kt b/compiler/testData/codegen/box/secondaryConstructors/withReturnUnit.kt new file mode 100644 index 00000000000..956c6db367e --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/withReturnUnit.kt @@ -0,0 +1,18 @@ +class A { + val prop: Int + constructor(arg: Boolean) { + if (arg) { + prop = 1 + return Unit + } + prop = 2 + } +} + +fun box(): String { + val a1 = A(true) + if (a1.prop != 1) return "fail1: ${a1.prop}" + val a2 = A(false) + if (a2.prop != 2) return "fail2: ${a2.prop}" + return "OK" +} diff --git a/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.java b/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.java new file mode 100644 index 00000000000..3d2afa074a1 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.java @@ -0,0 +1,24 @@ +package test; + +public class secondaryConstructor { + private final String x; + private final String y; + + public secondaryConstructor(String x, String y) { + this.x = x; + this.y = y; + } + + public secondaryConstructor(String x) { + this(x, "def_y"); + } + + public secondaryConstructor() { + this("def_x"); + } + + @Override + public String toString() { + return x + "#" + y; + } +} diff --git a/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.kt b/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.kt new file mode 100644 index 00000000000..2dbb0ea6393 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.kt @@ -0,0 +1,19 @@ +import test.secondaryConstructor; + +class Child : secondaryConstructor { + constructor(): super() {} + constructor(x: String): super(x) {} + constructor(x: String, y: String): super(x, y) {} +} + +fun box(): String { + val c1 = Child().toString() + if (c1 != "def_x#def_y") return "fail1: $c1" + + val c2 = Child("abc").toString() + if (c2 != "abc#def_y") return "fail2: $c2" + + val c3 = Child("abc", "def").toString() + if (c3 != "abc#def") return "fail3: $c3" + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.java b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.java new file mode 100644 index 00000000000..9c4f43d07d8 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.java @@ -0,0 +1,11 @@ +class WithGenerics { + public static String foo1() { + A x = new A("OK"); + return x.toString(); + } + + public static String foo2() { + A x = new A(123); + return x.toString(); + } +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.kt b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.kt new file mode 100644 index 00000000000..d978d9530e9 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.kt @@ -0,0 +1,20 @@ +open class A { + val prop: String + constructor(x: String) { + prop = x + } + constructor(x: T) { + prop = x.toString() + } + + override fun toString() = prop +} + +fun box(): String { + val a1 = WithGenerics.foo1() + if (a1 != "OK") return "fail1: $a1" + val a2 = WithGenerics.foo2() + if (a2 != "123") return "fail2: $a2" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.java b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.java new file mode 100644 index 00000000000..504d408a718 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.java @@ -0,0 +1,14 @@ +class WithPrimary { + public static A test1() { + return new A("123", "abc"); + } + public static A test2() { + return new A(); + } + public static A test3() { + return new A("123", 456); + } + public static A test4() { + return new A(1.0); + } +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.kt b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.kt new file mode 100644 index 00000000000..fd847910b19 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.kt @@ -0,0 +1,21 @@ +class A(val x: String = "def_x", val y: String = "1") { + constructor(x: String, y: Int): this(x, y.toString()) {} + constructor(x: Double): this(x.toString(), "def_y") {} + override fun toString() = "$x#$y" +} + +fun box(): String { + val test1 = WithPrimary.test1().toString() + if (test1 != "123#abc") return "fail1: $test1" + + val test2 = WithPrimary.test2().toString() + if (test2 != "def_x#1") return "fail2: $test2" + + val test3 = WithPrimary.test3().toString() + if (test3 != "123#456") return "fail3: $test3" + + val test4 = WithPrimary.test4().toString() + if (test4 != "1.0#def_y") return "fail4: $test4" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/WithVarargs.java b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/WithVarargs.java new file mode 100644 index 00000000000..f8645637258 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/WithVarargs.java @@ -0,0 +1,5 @@ +public class WithVarargs { + public static String foo() { + return new A("1", "2", "3").getProp(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/withVarargs.kt b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/withVarargs.kt new file mode 100644 index 00000000000..c38d977573f --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/withVarargs.kt @@ -0,0 +1,23 @@ +fun join(x: Array): String { + var result = "" + for (i in x) { + result += i + result += "#" + } + + return result +} + +class A { + val prop: String + constructor(vararg x: String) { + prop = join(x) + } +} + +fun box(): String { + val a1 = WithVarargs.foo() + if (a1 != "1#2#3#") return "fail1: ${a1}" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.java b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.java new file mode 100644 index 00000000000..155cc497e70 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.java @@ -0,0 +1,14 @@ +class WithoutPrimary { + public static A test1() { + return new A("123", "abc"); + } + public static A test2() { + return new A(null, 1, 3, null); + } + public static A test3() { + return new A("123", 456); + } + public static A test4() { + return new A(1.0); + } +} diff --git a/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.kt b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.kt new file mode 100644 index 00000000000..d8f88d7dce2 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.kt @@ -0,0 +1,27 @@ +class A { + val x: String + val y: String + constructor(x: String, y: String) { + this.x = x + this.y = y + } + constructor(x: String = "def_x", y: Int = 1): this(x, y.toString()) {} + constructor(x: Double): this(x.toString(), "def_y") {} + override fun toString() = "$x#$y" +} + +fun box(): String { + val test1 = WithoutPrimary.test1().toString() + if (test1 != "123#abc") return "fail1: $test1" + + val test2 = WithoutPrimary.test2().toString() + if (test2 != "def_x#1") return "fail2: $test2" + + val test3 = WithoutPrimary.test3().toString() + if (test3 != "123#456") return "fail3: $test3" + + val test4 = WithoutPrimary.test4().toString() + if (test4 != "1.0#def_y") return "fail4: $test4" + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index 11ea5a8306f..55587ee04fa 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -143,6 +143,12 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/constructor/genericConstructor.kt"); doTestAgainstJava(fileName); } + + @TestMetadata("secondaryConstructor.kt") + public void testSecondaryConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.kt"); + doTestAgainstJava(fileName); + } } @TestMetadata("compiler/testData/codegen/boxAgainstJava/delegation") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 08b3d375407..b335a130996 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -70,6 +70,7 @@ import java.util.regex.Pattern; BlackBoxCodegenTestGenerated.Regressions.class, BlackBoxCodegenTestGenerated.SafeCall.class, BlackBoxCodegenTestGenerated.SamConstructors.class, + BlackBoxCodegenTestGenerated.SecondaryConstructors.class, BlackBoxCodegenTestGenerated.StaticFields.class, BlackBoxCodegenTestGenerated.Strings.class, BlackBoxCodegenTestGenerated.Super.class, @@ -1715,6 +1716,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("subclosuresWithinInitializers.kt") + public void testSubclosuresWithinInitializers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/box/closures/captureOuterProperty") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -6270,6 +6277,111 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/secondaryConstructors") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SecondaryConstructors extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSecondaryConstructors() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/secondaryConstructors"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("basicNoPrimaryManySinks.kt") + public void testBasicNoPrimaryManySinks() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryManySinks.kt"); + doTest(fileName); + } + + @TestMetadata("basicNoPrimaryOneSink.kt") + public void testBasicNoPrimaryOneSink() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryOneSink.kt"); + doTest(fileName); + } + + @TestMetadata("basicPrimary.kt") + public void testBasicPrimary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/basicPrimary.kt"); + doTest(fileName); + } + + @TestMetadata("dataClasses.kt") + public void testDataClasses() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt"); + doTest(fileName); + } + + @TestMetadata("defaultArgs.kt") + public void testDefaultArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/defaultArgs.kt"); + doTest(fileName); + } + + @TestMetadata("enums.kt") + public void testEnums() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/enums.kt"); + doTest(fileName); + } + + @TestMetadata("generics.kt") + public void testGenerics() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/generics.kt"); + doTest(fileName); + } + + @TestMetadata("innerClasses.kt") + public void testInnerClasses() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt"); + doTest(fileName); + } + + @TestMetadata("innerClassesInheritance.kt") + public void testInnerClassesInheritance() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt"); + doTest(fileName); + } + + @TestMetadata("localClasses.kt") + public void testLocalClasses() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/localClasses.kt"); + doTest(fileName); + } + + @TestMetadata("superCallPrimary.kt") + public void testSuperCallPrimary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt"); + doTest(fileName); + } + + @TestMetadata("superCallSecondary.kt") + public void testSuperCallSecondary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt"); + doTest(fileName); + } + + @TestMetadata("varargs.kt") + public void testVarargs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/varargs.kt"); + doTest(fileName); + } + + @TestMetadata("withNonLocalReturn.kt") + public void testWithNonLocalReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withNonLocalReturn.kt"); + doTest(fileName); + } + + @TestMetadata("withReturn.kt") + public void testWithReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withReturn.kt"); + doTest(fileName); + } + + @TestMetadata("withReturnUnit.kt") + public void testWithReturnUnit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withReturnUnit.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/staticFields") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index c3fca0c8e24..1b385637e0b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -36,6 +36,7 @@ import java.util.regex.Pattern; BlackBoxWithJavaCodegenTestGenerated.PlatformStatic.class, BlackBoxWithJavaCodegenTestGenerated.Properties.class, BlackBoxWithJavaCodegenTestGenerated.Reflection.class, + BlackBoxWithJavaCodegenTestGenerated.SecondaryConstructors.class, }) @RunWith(JUnit3RunnerWithInners.class) public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodegenTest { @@ -181,4 +182,40 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege } + @TestMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors") + @TestDataPath("$PROJECT_ROOT") + @InnerTestClasses({ + }) + @RunWith(JUnit3RunnerWithInners.class) + public static class SecondaryConstructors extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSecondaryConstructors() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/secondaryConstructors"), Pattern.compile("^([^\\.]+)$"), true); + } + + @TestMetadata("withGenerics") + public void testWithGenerics() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/"); + doTestWithJava(fileName); + } + + @TestMetadata("withPrimary") + public void testWithPrimary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/"); + doTestWithJava(fileName); + } + + @TestMetadata("withVarargs") + public void testWithVarargs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/"); + doTestWithJava(fileName); + } + + @TestMetadata("withoutPrimary") + public void testWithoutPrimary() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/"); + doTestWithJava(fileName); + } + + } + }