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) {
|
||||
|
||||
Reference in New Issue
Block a user