From e65382e6ecfc047ec19625162d57dca7d8b5315e Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 24 Feb 2015 19:47:16 +0300 Subject: [PATCH] Support secondary constructors in JVM backend There is a lot of changes about closures calculating and generating. 1. As classes can have more than one constructor each of them should have closure arguments. 2. Captured variables set is the same for all of them. 3. Within constructors bodies/delegating calls closure parameters should be accessed through method arguments because fields may be not initialized yet. --- .../kotlin/codegen/ExpressionCodegen.java | 28 ++- .../codegen/ImplementationBodyCodegen.java | 184 +++++++++++++++--- .../codegen/binding/MutableClosure.java | 14 +- .../signature/BothSignatureWriter.java | 6 + .../kotlin/codegen/state/JetTypeMapper.java | 26 ++- .../closures/subclosuresWithinInitializers.kt | 24 +++ .../basicNoPrimaryManySinks.kt | 35 ++++ .../basicNoPrimaryOneSink.kt | 41 ++++ .../box/secondaryConstructors/basicPrimary.kt | 39 ++++ .../box/secondaryConstructors/dataClasses.kt | 53 +++++ .../box/secondaryConstructors/defaultArgs.kt | 34 ++++ .../box/secondaryConstructors/enums.kt | 62 ++++++ .../box/secondaryConstructors/generics.kt | 23 +++ .../box/secondaryConstructors/innerClasses.kt | 79 ++++++++ .../innerClassesInheritance.kt | 66 +++++++ .../box/secondaryConstructors/localClasses.kt | 77 ++++++++ .../secondaryConstructors/superCallPrimary.kt | 53 +++++ .../superCallSecondary.kt | 64 ++++++ .../box/secondaryConstructors/varargs.kt | 31 +++ .../withNonLocalReturn.kt | 21 ++ .../box/secondaryConstructors/withReturn.kt | 18 ++ .../secondaryConstructors/withReturnUnit.kt | 18 ++ .../constructor/secondaryConstructor.java | 24 +++ .../constructor/secondaryConstructor.kt | 19 ++ .../withGenerics/WithGenerics.java | 11 ++ .../withGenerics/WithGenerics.kt | 20 ++ .../withPrimary/WithPrimary.java | 14 ++ .../withPrimary/WithPrimary.kt | 21 ++ .../withVarargs/WithVarargs.java | 5 + .../withVarargs/withVarargs.kt | 23 +++ .../withoutPrimary/WithoutPrimary.java | 14 ++ .../withoutPrimary/WithoutPrimary.kt | 27 +++ ...ackBoxAgainstJavaCodegenTestGenerated.java | 6 + .../BlackBoxCodegenTestGenerated.java | 112 +++++++++++ .../BlackBoxWithJavaCodegenTestGenerated.java | 37 ++++ 35 files changed, 1290 insertions(+), 39 deletions(-) create mode 100644 compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryManySinks.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/basicNoPrimaryOneSink.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/basicPrimary.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/dataClasses.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/defaultArgs.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/enums.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/generics.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/localClasses.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/superCallSecondary.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/varargs.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/withNonLocalReturn.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/withReturn.kt create mode 100644 compiler/testData/codegen/box/secondaryConstructors/withReturnUnit.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.java create mode 100644 compiler/testData/codegen/boxAgainstJava/constructor/secondaryConstructor.kt create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.java create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withGenerics/WithGenerics.kt create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.java create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withPrimary/WithPrimary.kt create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/WithVarargs.java create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withVarargs/withVarargs.kt create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.java create mode 100644 compiler/testData/codegen/boxWithJava/secondaryConstructors/withoutPrimary/WithoutPrimary.kt 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); + } + + } + }