Generate copy function for data classes
#KT-2779 Fixed
This commit is contained in:
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<String> names = new HashSet<String>();
|
||||
public static class LocalVariablesInfo {
|
||||
@NotNull public final Collection<String> names = new HashSet<String>();
|
||||
|
||||
@NotNull private final Map<Name, Label> labelsForSharedVars = new HashMap<Name, Label>();
|
||||
@NotNull public final Map<Name, Label> labelsForSharedVars = new HashMap<Name, Label>();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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<PropertyDescriptor> 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<PropertyDescriptor> 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, "<init>", 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) {
|
||||
|
||||
@@ -238,6 +238,8 @@ public interface BindingContext {
|
||||
|
||||
WritableSlice<ValueParameterDescriptor, FunctionDescriptor> DATA_CLASS_COMPONENT_FUNCTION =
|
||||
Slices.<ValueParameterDescriptor, FunctionDescriptor>sliceBuilder().build();
|
||||
WritableSlice<ClassDescriptor, FunctionDescriptor> DATA_CLASS_COPY_FUNCTION =
|
||||
Slices.<ClassDescriptor, FunctionDescriptor>sliceBuilder().build();
|
||||
|
||||
WritableSlice<FqName, ClassDescriptor> FQNAME_TO_CLASS_DESCRIPTOR = new BasicWritableSlice<FqName, ClassDescriptor>(DO_NOTHING, true);
|
||||
WritableSlice<FqName, NamespaceDescriptor> FQNAME_TO_NAMESPACE_DESCRIPTOR =
|
||||
|
||||
@@ -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<JetClass, MutableClassDescriptor> 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<ConstructorDescriptor> 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 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ValueParameterDescriptor> constructorParameters,
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
@NotNull BindingTrace trace
|
||||
) {
|
||||
JetType returnType = classDescriptor.getDefaultType();
|
||||
|
||||
SimpleFunctionDescriptorImpl functionDescriptor = new SimpleFunctionDescriptorImpl(
|
||||
classDescriptor,
|
||||
Collections.<AnnotationDescriptor>emptyList(),
|
||||
COPY_METHOD_NAME,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
);
|
||||
|
||||
List<ValueParameterDescriptor> 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.<TypeParameterDescriptor>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) {
|
||||
|
||||
@@ -365,4 +365,10 @@ public class DescriptorUtils {
|
||||
+ (classifier == null ? "null" : classifier.getClass());
|
||||
return (ClassDescriptor) classifier;
|
||||
}
|
||||
|
||||
public static ConstructorDescriptor getConstructorOfDataClass(ClassDescriptor classDescriptor) {
|
||||
Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
|
||||
assert constructors.size() == 1 : "Data class must have only one constructor: " + constructors;
|
||||
return constructors.iterator().next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,12 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescr
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!constructor.getValueParameters().isEmpty() && name.equals(DescriptorResolver.COPY_METHOD_NAME)) {
|
||||
SimpleFunctionDescriptor copyFunctionDescriptor = DescriptorResolver.createCopyFunctionDescriptor(
|
||||
constructor.getValueParameters(),
|
||||
thisDescriptor, resolveSession.getTrace());
|
||||
result.add(copyFunctionDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateEnumClassObjectMethods(@NotNull Collection<? super FunctionDescriptor> result, @NotNull Name name) {
|
||||
@@ -307,6 +313,7 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescr
|
||||
if (functions.isEmpty()) break;
|
||||
n++;
|
||||
}
|
||||
getFunctions(Name.identifier("copy"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
data class A(val a: Int = 1, val b: String = "$a") {}
|
||||
|
||||
fun box() : String {
|
||||
var result = ""
|
||||
val a = A()
|
||||
val b = a.copy()
|
||||
if (b.a == 1 && b.b == "1") {
|
||||
result += "1"
|
||||
}
|
||||
|
||||
val c = a.copy(a = 2)
|
||||
if (c.a == 2 && c.b == "1") {
|
||||
result += "2"
|
||||
}
|
||||
|
||||
val d = a.copy(b = "2")
|
||||
if (d.a == 1 && d.b == "2") {
|
||||
result += "3"
|
||||
}
|
||||
|
||||
val e = a.copy(a = 2, b = "2")
|
||||
if (e.a == 2 && e.b == "2") {
|
||||
result += "4"
|
||||
}
|
||||
if (result == "1234") {
|
||||
return "OK"
|
||||
}
|
||||
return "fail"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
data class A(a: Int, b: String) {}
|
||||
|
||||
fun box() : String {
|
||||
for (method in javaClass<A>().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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
data class A(val a: Foo<String>) {}
|
||||
|
||||
class Foo<T>(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"
|
||||
}
|
||||
@@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ namespace test
|
||||
jet.data() internal final class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ namespace test
|
||||
jet.data() internal open class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ namespace test
|
||||
jet.data() internal open class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ jet.data() internal final class test.DataClass : jet.Any {
|
||||
public final /*constructor*/ fun <init>(/*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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user