Generate copy function for data classes

#KT-2779 Fixed
This commit is contained in:
Natalia.Ukhorskaya
2012-10-31 15:34:46 +04:00
parent 6009038342
commit 3b58eb4b60
20 changed files with 388 additions and 33 deletions
@@ -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) {