diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/DefaultParameterValueLoader.java b/compiler/backend/src/org/jetbrains/jet/codegen/DefaultParameterValueLoader.java new file mode 100644 index 00000000000..ab1d2766597 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/DefaultParameterValueLoader.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen; + +import org.jetbrains.asm4.Type; +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; +import org.jetbrains.jet.lang.psi.JetParameter; + +import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration; + +public interface DefaultParameterValueLoader { + + void putValueOnStack(ValueParameterDescriptor descriptor, ExpressionCodegen codegen); + + DefaultParameterValueLoader DEFAULT = new DefaultParameterValueLoader() { + @Override + public void putValueOnStack( + ValueParameterDescriptor descriptor, + ExpressionCodegen codegen + ) { + JetParameter jetParameter = (JetParameter) descriptorToDeclaration(codegen.getBindingContext(), descriptor); + assert jetParameter != null; + Type propertyType = codegen.typeMapper.mapType(descriptor.getType()); + codegen.gen(jetParameter.getDefaultValue(), propertyType); + } + }; +} + diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 0d346dac21b..73574a5d0c2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -33,12 +33,12 @@ import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter; import org.jetbrains.jet.codegen.signature.kotlin.JetValueParameterAnnotationWriter; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.GenerationStateAware; +import org.jetbrains.jet.codegen.state.JetTypeMapper; import org.jetbrains.jet.codegen.state.JetTypeMapperMode; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.JetDeclarationWithBody; import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression; import org.jetbrains.jet.lang.psi.JetNamedFunction; -import org.jetbrains.jet.lang.psi.JetParameter; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JvmAbi; @@ -107,7 +107,7 @@ public class FunctionCodegen extends GenerationStateAware { } } - generateDefaultIfNeeded(context, state, v, jvmSignature.getAsmMethod(), functionDescriptor, kind); + generateDefaultIfNeeded(context, state, v, jvmSignature.getAsmMethod(), functionDescriptor, kind, DefaultParameterValueLoader.DEFAULT); } private void generateMethodHeaderAndBody( @@ -142,10 +142,7 @@ public class FunctionCodegen extends GenerationStateAware { return; } - LocalVariablesInfo localVariablesInfo = new LocalVariablesInfo(); - for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) { - localVariablesInfo.names.add(parameter.getName().getName()); - } + LocalVariablesInfo localVariablesInfo = generateLocalVariablesInfo(functionDescriptor); MethodBounds methodBounds = generateMethodBody(mv, fun, functionDescriptor, context, asmMethod, localVariablesInfo); @@ -161,7 +158,7 @@ public class FunctionCodegen extends GenerationStateAware { thisType = null; } - generateLocalVariableTable(mv, functionDescriptor, thisType, localVariablesInfo, methodBounds); + generateLocalVariableTable(typeMapper, mv, functionDescriptor, thisType, localVariablesInfo, methodBounds); endVisit(mv, null, fun); } @@ -218,24 +215,33 @@ public class FunctionCodegen extends GenerationStateAware { return new MethodBounds(methodBegin, methodEnd); } - private static class MethodBounds { + public static class MethodBounds { @NotNull private final Label begin; @NotNull private final Label end; - private MethodBounds(@NotNull Label begin, @NotNull Label end) { + public MethodBounds(@NotNull Label begin, @NotNull Label end) { this.begin = begin; this.end = end; } } - private static class LocalVariablesInfo { - @NotNull private final Collection names = new HashSet(); + public static class LocalVariablesInfo { + @NotNull public final Collection names = new HashSet(); - @NotNull private final Map labelsForSharedVars = new HashMap(); + @NotNull public final Map labelsForSharedVars = new HashMap(); } - private void generateLocalVariableTable( + public static LocalVariablesInfo generateLocalVariablesInfo(FunctionDescriptor functionDescriptor) { + LocalVariablesInfo localVariablesInfo = new LocalVariablesInfo(); + for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) { + localVariablesInfo.names.add(parameter.getName().getName()); + } + return localVariablesInfo; + } + + public static void generateLocalVariableTable( + @NotNull JetTypeMapper typeMapper, @NotNull MethodVisitor mv, @NotNull FunctionDescriptor functionDescriptor, @Nullable Type thisType, @@ -525,7 +531,8 @@ public class FunctionCodegen extends GenerationStateAware { ClassBuilder v, Method jvmSignature, @NotNull FunctionDescriptor functionDescriptor, - OwnerKind kind + OwnerKind kind, + DefaultParameterValueLoader loadStrategy ) { DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration(); @@ -575,7 +582,7 @@ public class FunctionCodegen extends GenerationStateAware { else if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { generateDefaultImpl(owner, state, jvmSignature, functionDescriptor, kind, receiverParameter, hasReceiver, isStatic, ownerInternalName, - isConstructor, mv, iv); + isConstructor, mv, iv, loadStrategy); } } @@ -591,7 +598,8 @@ public class FunctionCodegen extends GenerationStateAware { JvmClassName ownerInternalName, boolean constructor, MethodVisitor mv, - InstructionAdapter iv + InstructionAdapter iv, + DefaultParameterValueLoader loadStrategy ) { mv.visitCode(); @@ -656,9 +664,7 @@ public class FunctionCodegen extends GenerationStateAware { Label loadArg = new Label(); iv.ifeq(loadArg); - JetParameter jetParameter = (JetParameter) descriptorToDeclaration(state.getBindingContext(), parameterDescriptor); - assert jetParameter != null; - codegen.gen(jetParameter.getDefaultValue(), t); + loadStrategy.putValueOnStack(parameterDescriptor, codegen); int ind = frameMap.getIndex(parameterDescriptor); iv.store(ind, t); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index cce35cbd9fe..fc60dae974f 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -32,6 +32,7 @@ import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.codegen.binding.MutableClosure; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.ConstructorContext; +import org.jetbrains.jet.codegen.context.MethodContext; import org.jetbrains.jet.codegen.signature.*; import org.jetbrains.jet.codegen.signature.kotlin.JetMethodAnnotationWriter; import org.jetbrains.jet.codegen.signature.kotlin.JetValueParameterAnnotationWriter; @@ -41,6 +42,7 @@ import org.jetbrains.jet.codegen.state.JetTypeMapperMode; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.OverridingUtil; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; @@ -414,6 +416,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if (!KotlinBuiltIns.getInstance().isData(descriptor)) return; generateComponentFunctionsForDataClasses(); + generateCopyFunctionForDataClasses(); List properties = getDataProperties(); if (!properties.isEmpty()) { @@ -423,6 +426,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } + private void generateCopyFunctionForDataClasses() { + FunctionDescriptor copyFunction = bindingContext.get(BindingContext.DATA_CLASS_COPY_FUNCTION, descriptor); + if (copyFunction != null) { + generateCopyFunction(copyFunction); + } + } + private void generateDataClassToStringIfNeeded(List properties) { ClassDescriptor stringClass = KotlinBuiltIns.getInstance().getString(); if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("toString"), stringClass) == null) { @@ -640,6 +650,76 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { FunctionCodegen.endVisit(mv, function.getName().getName(), myClass); } + private void generateCopyFunction(@NotNull final FunctionDescriptor function) { + JetType returnType = function.getReturnType(); + assert returnType != null : "Return type of copy function should not be null: " + function; + + JvmMethodSignature methodSignature = typeMapper.mapSignature(function.getName(), function); + final String methodDesc = methodSignature.getAsmMethod().getDescriptor(); + + MethodVisitor mv = v.newMethod(myClass, FunctionCodegen.getMethodAsmFlags(function, OwnerKind.IMPLEMENTATION), + function.getName().getName(), methodDesc, + null, null); + + FunctionCodegen.genJetAnnotations(state, function, null, null, mv); + + mv.visitCode(); + InstructionAdapter iv = new InstructionAdapter(mv); + + ConstructorDescriptor constructor = DescriptorUtils.getConstructorOfDataClass(descriptor); + + + Label methodBegin = new Label(); + mv.visitLabel(methodBegin); + + final Type thisDescriptorType = typeMapper.mapType(descriptor.getDefaultType()); + iv.anew(thisDescriptorType); + iv.dup(); + + String thisInternalName = thisDescriptorType.getInternalName(); + + assert function.getValueParameters().size() == constructor.getValueParameters().size() : + "Number of parameters of copy function and constructor are different. Copy: " + function.getValueParameters().size() + ", constructor: " + constructor.getValueParameters().size(); + + int parameterIndex = 0; + for (ValueParameterDescriptor parameterDescriptor : function.getValueParameters()) { + iv.load(parameterIndex + 1, typeMapper.mapType(parameterDescriptor.getType())); + parameterIndex++; + } + + String constructorJvmDescriptor = typeMapper.mapToCallableMethod(constructor).getSignature().getAsmMethod().getDescriptor(); + iv.invokespecial(thisInternalName, "", constructorJvmDescriptor); + + iv.areturn(thisDescriptorType); + + Label methodEnd = new Label(); + mv.visitLabel(methodEnd); + + FunctionCodegen.MethodBounds methodBounds = new FunctionCodegen.MethodBounds(methodBegin, methodEnd); + FunctionCodegen.generateLocalVariableTable(typeMapper, mv, function, thisDescriptorType, FunctionCodegen.generateLocalVariablesInfo(function), methodBounds); + + FunctionCodegen.endVisit(mv, function.getName().getName(), myClass); + + final MethodContext functionContext = context.intoFunction(function); + FunctionCodegen.generateDefaultIfNeeded(functionContext, state, v, methodSignature.getAsmMethod(), function, OwnerKind.IMPLEMENTATION, + new DefaultParameterValueLoader() { + @Override + public void putValueOnStack( + ValueParameterDescriptor descriptor, + ExpressionCodegen codegen + ) { + assert (KotlinBuiltIns.getInstance().isData((ClassDescriptor) function.getContainingDeclaration())) + : "Trying to create function with default arguments for function that isn't presented in code for class without data annotation"; + PropertyDescriptor propertyDescriptor = codegen.getBindingContext().get( + BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor); + assert propertyDescriptor != null : "Trying to generate default value for parameter of copy function that doesn't correspond to any property"; + codegen.v.load(0, thisDescriptorType); + Type propertyType = codegen.typeMapper.mapType(propertyDescriptor.getType()); + codegen.intermediateValueForProperty(propertyDescriptor, false, null).put(propertyType, codegen.v); + } + }); + } + private void generateEnumMethods() { if (myEnumConstants.size() > 0) { { @@ -971,7 +1051,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { assert constructorDescriptor != null; FunctionCodegen.generateDefaultIfNeeded(constructorContext, state, v, constructorMethod.getAsmMethod(), constructorDescriptor, - OwnerKind.IMPLEMENTATION); + OwnerKind.IMPLEMENTATION, DefaultParameterValueLoader.DEFAULT); } private void genSuperCallToDelegatorToSuperClass(InstructionAdapter iv) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index e100ff4da38..c7b0de69f1a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -238,6 +238,8 @@ public interface BindingContext { WritableSlice DATA_CLASS_COMPONENT_FUNCTION = Slices.sliceBuilder().build(); + WritableSlice DATA_CLASS_COPY_FUNCTION = + Slices.sliceBuilder().build(); WritableSlice FQNAME_TO_CLASS_DESCRIPTOR = new BasicWritableSlice(DO_NOTHING, true); WritableSlice FQNAME_TO_NAMESPACE_DESCRIPTOR = diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java index 8620e33d5ba..887242da058 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java @@ -91,7 +91,7 @@ public class DeclarationResolver { resolveConstructorHeaders(); resolveAnnotationStubsOnClassesAndConstructors(); resolveFunctionAndPropertyHeaders(); - createComponentFunctionsForDataClasses(); + createFunctionsForDataClasses(); importsResolver.processMembersImports(rootScope); checkRedeclarationsInNamespaces(); checkRedeclarationsInInnerClassNames(); @@ -219,24 +219,22 @@ public class DeclarationResolver { } } - private void createComponentFunctionsForDataClasses() { + private void createFunctionsForDataClasses() { for (Map.Entry entry : context.getClasses().entrySet()) { JetClass jetClass = entry.getKey(); MutableClassDescriptor classDescriptor = entry.getValue(); if (jetClass.hasPrimaryConstructor() && KotlinBuiltIns.getInstance().isData(classDescriptor)) { - createComponentFunctions(classDescriptor); + ConstructorDescriptor constructor = DescriptorUtils.getConstructorOfDataClass(classDescriptor); + createComponentFunctions(classDescriptor, constructor); + createCopyFunction(classDescriptor, constructor); } } } - private void createComponentFunctions(MutableClassDescriptor classDescriptor) { - Set constructors = classDescriptor.getConstructors(); - assert constructors.size() == 1 : "Data class hasn't a single constructor: " + constructors.size(); - ConstructorDescriptor constructor = constructors.iterator().next(); - + private void createComponentFunctions(@NotNull MutableClassDescriptor classDescriptor, @NotNull ConstructorDescriptor constructorDescriptor) { int parameterIndex = 0; - for (ValueParameterDescriptor parameter : constructor.getValueParameters()) { + for (ValueParameterDescriptor parameter : constructorDescriptor.getValueParameters()) { if (!ErrorUtils.isErrorType(parameter.getType())) { PropertyDescriptor property = trace.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter); if (property != null) { @@ -251,6 +249,13 @@ public class DeclarationResolver { } } + private void createCopyFunction(@NotNull MutableClassDescriptor classDescriptor, @NotNull ConstructorDescriptor constructorDescriptor) { + SimpleFunctionDescriptor functionDescriptor = DescriptorResolver.createCopyFunctionDescriptor( + constructorDescriptor.getValueParameters(), classDescriptor, trace); + + classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor); + } + private void processPrimaryConstructor(MutableClassDescriptor classDescriptor, JetClass klass) { if (classDescriptor.getKind() == ClassKind.TRAIT) { JetParameterList primaryConstructorParameterList = klass.getPrimaryConstructorParameterList(); @@ -356,8 +361,8 @@ public class DeclarationResolver { descriptorMap.put(desc.getName(), desc); } } - - reportRedeclarations(descriptorMap); + + reportRedeclarations(descriptorMap); } } @@ -392,5 +397,5 @@ public class DeclarationResolver { } } - + } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java index fcfbb212764..66a01344a29 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java @@ -57,6 +57,7 @@ import static org.jetbrains.jet.lexer.JetTokens.OVERRIDE_KEYWORD; public class DescriptorResolver { public static final Name VALUE_OF_METHOD_NAME = Name.identifier("valueOf"); public static final Name VALUES_METHOD_NAME = Name.identifier("values"); + public static final Name COPY_METHOD_NAME = Name.identifier("copy"); public static final String COMPONENT_FUNCTION_NAME_PREFIX = "component"; @NotNull @@ -349,6 +350,53 @@ public class DescriptorResolver { return functionDescriptor; } + @NotNull + public static SimpleFunctionDescriptor createCopyFunctionDescriptor( + @NotNull Collection constructorParameters, + @NotNull ClassDescriptor classDescriptor, + @NotNull BindingTrace trace + ) { + JetType returnType = classDescriptor.getDefaultType(); + + SimpleFunctionDescriptorImpl functionDescriptor = new SimpleFunctionDescriptorImpl( + classDescriptor, + Collections.emptyList(), + COPY_METHOD_NAME, + CallableMemberDescriptor.Kind.SYNTHESIZED + ); + + List parameterDescriptors = Lists.newArrayList(); + + for (ValueParameterDescriptor parameter : constructorParameters) { + PropertyDescriptor propertyDescriptor = trace.getBindingContext().get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter); + // If parameter hasn't corresponding property, so it mustn't have default value as a parameter in copy function for data class + boolean declaresDefaultValue = propertyDescriptor != null; + ValueParameterDescriptorImpl parameterDescriptor = + new ValueParameterDescriptorImpl(functionDescriptor, parameter.getIndex(), parameter.getAnnotations(), + parameter.getName(), parameter.isVar(), parameter.getType(), + declaresDefaultValue, + parameter.getVarargElementType()); + parameterDescriptors.add(parameterDescriptor); + if (declaresDefaultValue) { + trace.record(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameterDescriptor, propertyDescriptor); + } + } + + functionDescriptor.initialize( + null, + classDescriptor.getImplicitReceiver(), + Collections.emptyList(), + parameterDescriptors, + returnType, + Modality.FINAL, + classDescriptor.getVisibility(), + true + ); + + trace.record(BindingContext.DATA_CLASS_COPY_FUNCTION, classDescriptor, functionDescriptor); + return functionDescriptor; + } + public static Visibility getDefaultVisibility(JetModifierListOwner modifierListOwner, DeclarationDescriptor containingDescriptor) { Visibility defaultVisibility; if (containingDescriptor instanceof ClassDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index c762e0877ef..deec0a1a4b5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -365,4 +365,10 @@ public class DescriptorUtils { + (classifier == null ? "null" : classifier.getClass()); return (ClassDescriptor) classifier; } + + public static ConstructorDescriptor getConstructorOfDataClass(ClassDescriptor classDescriptor) { + Collection constructors = classDescriptor.getConstructors(); + assert constructors.size() == 1 : "Data class must have only one constructor: " + constructors; + return constructors.iterator().next(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassMemberScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassMemberScope.java index b048765e302..caea41803a2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassMemberScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassMemberScope.java @@ -174,6 +174,12 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope result, @NotNull Name name) { @@ -307,6 +313,7 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope().getDeclaredMethods()) { + if (method.getName() == "copy"){ + val parameterTypes = method.getParameterTypes() + if (parameterTypes != null && parameterTypes.size == 2) { + val copy = A(1, "a").copy(a = 2, b = "b") + return "OK" + } + else { + return "Method copy has " + (if (parameterTypes == null) "0" else parameterTypes.size) + " parameters, expected 2" + } + } + } + return "fail" +} diff --git a/compiler/testData/codegen/dataClasses/copy/valInConstructorParams.kt b/compiler/testData/codegen/dataClasses/copy/valInConstructorParams.kt new file mode 100644 index 00000000000..a340685014e --- /dev/null +++ b/compiler/testData/codegen/dataClasses/copy/valInConstructorParams.kt @@ -0,0 +1,29 @@ +data class A(val a: Int, val b: String) {} + +fun box() : String { + var result = "" + val a = A(1, "a") + val b = a.copy() + if (b.a == 1 && b.b == "a") { + result += "1" + } + + val c = a.copy(a = 2) + if (c.a == 2 && c.b == "a") { + result += "2" + } + + val d = a.copy(b = "b") + if (d.a == 1 && d.b == "b") { + result += "3" + } + + val e = a.copy(a = 2, b = "b") + if (e.a == 2 && e.b == "b") { + result += "4" + } + if (result == "1234") { + return "OK" + } + return "fail" +} diff --git a/compiler/testData/codegen/dataClasses/copy/varInConstructorParams.kt b/compiler/testData/codegen/dataClasses/copy/varInConstructorParams.kt new file mode 100644 index 00000000000..6cd1a25ae2c --- /dev/null +++ b/compiler/testData/codegen/dataClasses/copy/varInConstructorParams.kt @@ -0,0 +1,29 @@ +data class A(var a: Int, var b: String) {} + +fun box() : String { + var result = "" + val a = A(1, "a") + val b = a.copy() + if (b.a == 1 && b.b == "a") { + result += "1" + } + + val c = a.copy(a = 2) + if (c.a == 2 && c.b == "a") { + result += "2" + } + + val d = a.copy(b = "b") + if (d.a == 1 && d.b == "b") { + result += "3" + } + + val e = a.copy(a = 2, b = "b") + if (e.a == 2 && e.b == "b") { + result += "4" + } + if (result == "1234") { + return "OK" + } + return "fail" +} diff --git a/compiler/testData/codegen/dataClasses/copy/withGenericParameter.kt b/compiler/testData/codegen/dataClasses/copy/withGenericParameter.kt new file mode 100644 index 00000000000..83a4d82db02 --- /dev/null +++ b/compiler/testData/codegen/dataClasses/copy/withGenericParameter.kt @@ -0,0 +1,14 @@ +data class A(val a: Foo) {} + +class Foo(val a: T) { } + +fun box() : String { + val f1 = Foo("a") + val f2 = Foo("b") + val a = A(f1) + val b = a.copy(f2) + if (b.a.a == "b") { + return "OK" + } + return "fail" +} diff --git a/compiler/testData/loadKotlin/dataClass/MixedComponents.txt b/compiler/testData/loadKotlin/dataClass/MixedComponents.txt index 1258180379f..69667207f68 100644 --- a/compiler/testData/loadKotlin/dataClass/MixedComponents.txt +++ b/compiler/testData/loadKotlin/dataClass/MixedComponents.txt @@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String, /*1*/ y: jet.Int, /*2*/ z: jet.Double): test.DataClass internal final /*synthesized*/ fun component1(): jet.String internal final /*synthesized*/ fun component2(): jet.Double + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?, /*1*/ y: jet.Int, /*2*/ z: jet.Double = ?): test.DataClass internal final var x: jet.String internal final val z: jet.Double } diff --git a/compiler/testData/loadKotlin/dataClass/OneVal.txt b/compiler/testData/loadKotlin/dataClass/OneVal.txt index fe84f481aa3..50b0c6dfaa2 100644 --- a/compiler/testData/loadKotlin/dataClass/OneVal.txt +++ b/compiler/testData/loadKotlin/dataClass/OneVal.txt @@ -3,5 +3,6 @@ namespace test jet.data() internal final class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String): test.DataClass internal final /*synthesized*/ fun component1(): jet.String + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?): test.DataClass internal final val x: jet.String } diff --git a/compiler/testData/loadKotlin/dataClass/OpenDataClass.txt b/compiler/testData/loadKotlin/dataClass/OpenDataClass.txt index 921b9b529fa..839483b622f 100644 --- a/compiler/testData/loadKotlin/dataClass/OpenDataClass.txt +++ b/compiler/testData/loadKotlin/dataClass/OpenDataClass.txt @@ -3,5 +3,6 @@ namespace test jet.data() internal open class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String): test.DataClass internal final /*synthesized*/ fun component1(): jet.String + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?): test.DataClass internal final val x: jet.String } diff --git a/compiler/testData/loadKotlin/dataClass/OpenPropertyFinalComponent.txt b/compiler/testData/loadKotlin/dataClass/OpenPropertyFinalComponent.txt index bdb7e1d3b74..1f223a87eff 100644 --- a/compiler/testData/loadKotlin/dataClass/OpenPropertyFinalComponent.txt +++ b/compiler/testData/loadKotlin/dataClass/OpenPropertyFinalComponent.txt @@ -3,5 +3,6 @@ namespace test jet.data() internal open class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String): test.DataClass internal final /*synthesized*/ fun component1(): jet.String + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?): test.DataClass internal open val x: jet.String } diff --git a/compiler/testData/loadKotlin/dataClass/TwoVals.txt b/compiler/testData/loadKotlin/dataClass/TwoVals.txt index b9fdf7e89f6..683ed09ecfb 100644 --- a/compiler/testData/loadKotlin/dataClass/TwoVals.txt +++ b/compiler/testData/loadKotlin/dataClass/TwoVals.txt @@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String, /*1*/ y: jet.Int): test.DataClass internal final /*synthesized*/ fun component1(): jet.String internal final /*synthesized*/ fun component2(): jet.Int + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?, /*1*/ y: jet.Int = ?): test.DataClass internal final val x: jet.String internal final val y: jet.Int } diff --git a/compiler/testData/loadKotlin/dataClass/TwoVars.txt b/compiler/testData/loadKotlin/dataClass/TwoVars.txt index f7a1f6f6e02..a3531d1ddaa 100644 --- a/compiler/testData/loadKotlin/dataClass/TwoVars.txt +++ b/compiler/testData/loadKotlin/dataClass/TwoVars.txt @@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any { public final /*constructor*/ fun (/*0*/ x: jet.String, /*1*/ y: jet.Int): test.DataClass internal final /*synthesized*/ fun component1(): jet.String internal final /*synthesized*/ fun component2(): jet.Int + internal final /*synthesized*/ fun copy(/*0*/ x: jet.String = ?, /*1*/ y: jet.Int = ?): test.DataClass internal final var x: jet.String internal final var y: jet.Int } diff --git a/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java index d52fc48795a..927a0a02be3 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/DataClassCodegenTestGenerated.java @@ -25,7 +25,7 @@ import java.io.File; /** This class is generated by {@link org.jetbrains.jet.codegen.AbstractDataClassCodegenTest}. DO NOT MODIFY MANUALLY */ @TestMetadata("compiler/testData/codegen/dataClasses") -@InnerTestClasses({DataClassCodegenTestGenerated.Equals.class, DataClassCodegenTestGenerated.Hashcode.class, DataClassCodegenTestGenerated.Tostring.class}) +@InnerTestClasses({DataClassCodegenTestGenerated.Copy.class, DataClassCodegenTestGenerated.Equals.class, DataClassCodegenTestGenerated.Hashcode.class, DataClassCodegenTestGenerated.Tostring.class}) public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest { public void testAllFilesPresentInDataClasses() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses"), "kt", true); @@ -81,6 +81,40 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/unitComponent.kt"); } + @TestMetadata("compiler/testData/codegen/dataClasses/copy") + public static class Copy extends AbstractDataClassCodegenTest { + public void testAllFilesPresentInCopy() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", + new File("compiler/testData/codegen/dataClasses/copy"), "kt", true); + } + + @TestMetadata("constructorWithDefaultParam.kt") + public void testConstructorWithDefaultParam() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/copy/constructorWithDefaultParam.kt"); + } + + @TestMetadata("paramWithoutProperty.kt") + public void testParamWithoutProperty() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/copy/paramWithoutProperty.kt"); + } + + @TestMetadata("valInConstructorParams.kt") + public void testValInConstructorParams() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/copy/valInConstructorParams.kt"); + } + + @TestMetadata("varInConstructorParams.kt") + public void testVarInConstructorParams() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/copy/varInConstructorParams.kt"); + } + + @TestMetadata("withGenericParameter.kt") + public void testWithGenericParameter() throws Exception { + blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/copy/withGenericParameter.kt"); + } + + } + @TestMetadata("compiler/testData/codegen/dataClasses/equals") public static class Equals extends AbstractDataClassCodegenTest { public void testAllFilesPresentInEquals() throws Exception { @@ -243,6 +277,7 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest public static Test suite() { TestSuite suite = new TestSuite("DataClassCodegenTestGenerated"); suite.addTestSuite(DataClassCodegenTestGenerated.class); + suite.addTestSuite(Copy.class); suite.addTestSuite(Equals.class); suite.addTestSuite(Hashcode.class); suite.addTestSuite(Tostring.class);