toString() method for data classes and fake implementation for hashCode/equals

This commit is contained in:
Alex Tkachman
2012-09-16 15:56:48 +03:00
parent e317764a88
commit 58af365eb2
13 changed files with 236 additions and 40 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
@@ -116,7 +117,7 @@ public abstract class ClassBodyCodegen extends GenerationStateAware {
}
}
protected List<JetParameter> getPrimaryConstructorParameters() {
protected @NotNull List<JetParameter> getPrimaryConstructorParameters() {
if (myClass instanceof JetClass) {
return ((JetClass) myClass).getPrimaryConstructorParameters();
}
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.asm4.commons.Method;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
@@ -47,7 +48,7 @@ import java.util.*;
import static org.jetbrains.asm4.Opcodes.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.*;
/**
* @author abreslav
@@ -369,4 +370,16 @@ public class CodegenUtil {
iv.invokespecial(classAsmType.getInternalName(), "<init>", "()V");
iv.putstatic(classAsmType.getInternalName(), JvmAbi.INSTANCE_FIELD, classAsmType.getDescriptor());
}
public static void generateStringBuilderConstructor(InstructionAdapter v) {
v.visitTypeInsn(NEW, "java/lang/StringBuilder");
v.dup();
v.invokespecial("java/lang/StringBuilder", "<init>", "()V");
}
public static void invokeAppendMethod(InstructionAdapter v, Type exprType) {
Method appendDescriptor = new Method("append", getType(StringBuilder.class),
new Type[] {exprType.getSort() == Type.OBJECT ? (exprType.equals(JAVA_STRING_TYPE) ? JAVA_STRING_TYPE : OBJECT_TYPE) : exprType});
v.invokevirtual("java/lang/StringBuilder", "append", appendDescriptor.getDescriptor());
}
}
@@ -984,7 +984,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
return StackValue.constant(constantValue.toString(), type);
}
else {
generateStringBuilderConstructor();
generateStringBuilderConstructor(v);
for (JetStringTemplateEntry entry : expression.getEntries()) {
if (entry instanceof JetStringTemplateEntryWithExpression) {
invokeAppend(entry.getExpression());
@@ -994,7 +994,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
? ((JetEscapeStringTemplateEntry) entry).getUnescapedValue()
: entry.getText();
v.aconst(text);
invokeAppendMethod(JAVA_STRING_TYPE);
invokeAppendMethod(v, JAVA_STRING_TYPE);
}
}
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
@@ -2568,14 +2568,6 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
}
public void generateStringBuilderConstructor() {
Type type = getType(StringBuilder.class);
v.anew(type);
v.dup();
Method method = new Method("<init>", Type.VOID_TYPE, new Type[0]);
v.invokespecial("java/lang/StringBuilder", method.getName(), method.getDescriptor());
}
public void invokeAppend(final JetExpression expr) {
if (expr instanceof JetBinaryExpression) {
final JetBinaryExpression binaryExpression = (JetBinaryExpression) expr;
@@ -2594,13 +2586,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
Type exprType = expressionType(expr);
gen(expr, exprType);
invokeAppendMethod(exprType.getSort() == Type.ARRAY ? OBJECT_TYPE : exprType);
}
public void invokeAppendMethod(Type exprType) {
Method appendDescriptor = new Method("append", getType(StringBuilder.class),
new Type[] {exprType.getSort() == Type.OBJECT ? OBJECT_TYPE : exprType});
v.invokevirtual("java/lang/StringBuilder", "append", appendDescriptor.getDescriptor());
invokeAppendMethod(v, exprType.getSort() == Type.ARRAY ? OBJECT_TYPE : exprType);
}
@Nullable
@@ -322,10 +322,14 @@ public class FunctionCodegen extends GenerationStateAware {
public static void genJetAnnotations(
@NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull JvmMethodSignature jvmSignature,
@Nullable JvmMethodSignature jvmSignature,
@Nullable String propertyTypeSignature,
MethodVisitor mv
) {
if (jvmSignature == null) {
jvmSignature = state.getTypeMapper().mapToCallableMethod(functionDescriptor, false, OwnerKind.IMPLEMENTATION).getSignature();
}
List<ValueParameterDescriptor> paramDescrs = functionDescriptor.getValueParameters();
Modality modality = functionDescriptor.getModality();
ReceiverDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
@@ -59,6 +59,7 @@ import static org.jetbrains.asm4.Opcodes.*;
import static org.jetbrains.jet.codegen.CodegenUtil.*;
import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.callableDescriptorToDeclaration;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.JAVA_STRING_TYPE;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
/**
@@ -352,11 +353,103 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateEnumMethods();
generateComponentFunctionsForDataClasses();
generateFunctionsForDataClasses();
generateClosureFields(context.closure, v, state.getTypeMapper());
}
private void generateFunctionsForDataClasses() {
if (!JetStandardLibrary.isData(descriptor)) return;
generateComponentFunctionsForDataClasses();
generateDataClassToStringMethod();
generateDataClassHashCodeMethod();
generateDataClassEqualsMethod();
}
private void generateDataClassEqualsMethod() {
// todo: this is fake implementation
final MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, superClassAsmType.getInternalName(), "equals", "(Ljava/lang/Object;)Z");
mv.visitInsn(IRETURN);
mv.visitMaxs(-1,-1);
mv.visitEnd();
}
private void generateDataClassHashCodeMethod() {
// todo: this is fake implementation
final MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superClassAsmType.getInternalName(), "hashCode", "()I");
mv.visitInsn(IRETURN);
mv.visitMaxs(-1,-1);
mv.visitEnd();
}
private void generateDataClassToStringMethod() {
final MethodVisitor mv = v.getVisitor().visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
final InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitVarInsn(ALOAD, 0);
generateStringBuilderConstructor(iv);
boolean first = true;
for (JetParameter parameter : getPrimaryConstructorParameters()) {
if (parameter.getValOrVarNode() == null) continue;
PropertyDescriptor propertyDescriptor = state.getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
assert propertyDescriptor != null;
if(first) {
iv.aconst(descriptor.getName() + "{" + propertyDescriptor.getName().getName()+"=");
first = false;
}
else {
iv.aconst(", " + propertyDescriptor.getName().getName()+"=");
}
CodegenUtil.invokeAppendMethod(iv, JAVA_STRING_TYPE);
iv.load(0, classAsmType);
// todo: seems to be more correct - need to be changed in sync with 'componentX' generation and related tests
// final Method
// method = typeMapper.mapGetterSignature(propertyDescriptor, OwnerKind.IMPLEMENTATION).getJvmMethodSignature().getAsmMethod();
//
// iv.invokevirtual(classAsmType.getInternalName(), method.getName(), method.getDescriptor());
// final Type type = method.getReturnType();
Type type = typeMapper.mapType(propertyDescriptor);
iv.getfield(classAsmType.getInternalName(), parameter.getName(), type.getDescriptor());
if (type.getSort() == Type.ARRAY) {
final Type elementType = correctElementType(type);
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;");
type = JAVA_STRING_TYPE;
}
else {
if (elementType.getSort() != Type.CHAR) {
iv.invokestatic("java/util/Arrays", "toString", "(" + type.getDescriptor() + ")Ljava/lang/String;");
type = JAVA_STRING_TYPE;
}
}
}
CodegenUtil.invokeAppendMethod(iv, type);
}
iv.aconst("}");
CodegenUtil.invokeAppendMethod(iv, JAVA_STRING_TYPE);
iv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
iv.areturn(JAVA_STRING_TYPE);
FunctionCodegen.endVisit(mv, "toString", myClass);
}
private void generateComponentFunctionsForDataClasses() {
if (!myClass.hasPrimaryConstructor() || !JetStandardLibrary.isData(descriptor)) return;
@@ -376,19 +469,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
Type componentType = typeMapper.mapReturnType(returnType);
MethodVisitor mv = v.newMethod(myClass,
ACC_FINAL | getVisibilityAccessFlag(function),
FunctionCodegen.getMethodAsmFlags(function, OwnerKind.IMPLEMENTATION),
function.getName().getName(),
"()" + componentType.getDescriptor(),
null, null);
JetMethodAnnotationWriter aw = JetMethodAnnotationWriter.visitAnnotation(mv);
BitSet kotlinFlags = getFlagsForVisibility(function.getVisibility());
aw.writeFlags(kotlinFlags);
aw.writeKind(DescriptorKindUtils.kindToInt(function.getKind()));
aw.writeNullableReturnType(returnType.isNullable());
JvmMethodSignature jvmMethodSignature = typeMapper.mapToCallableMethod(function, false, kind).getSignature();
aw.writeReturnType(jvmMethodSignature.getKotlinReturnType());
aw.visitEnd();
FunctionCodegen.genJetAnnotations(state, function, null, null, mv);
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
@@ -754,7 +840,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void genSimpleSuperCall(InstructionAdapter iv) {
iv.load(0, superClassAsmType);
if (descriptor.getKind() == ClassKind.ENUM_CLASS || descriptor.getKind() == ClassKind.ENUM_ENTRY) {
iv.load(1, AsmTypeConstants.JAVA_STRING_TYPE);
iv.load(1, JAVA_STRING_TYPE);
iv.load(2, Type.INT_TYPE);
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "(Ljava/lang/String;I)V");
}
@@ -20,11 +20,12 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Type;
import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import org.jetbrains.jet.codegen.CodegenUtil;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import java.util.List;
@@ -43,15 +44,15 @@ public class Concat implements IntrinsicMethod {
@NotNull GenerationState state
) {
if (receiver == null || receiver == StackValue.none()) { // LHS + RHS
codegen.generateStringBuilderConstructor();
CodegenUtil.generateStringBuilderConstructor(v);
codegen.invokeAppend(arguments.get(0)); // StringBuilder(LHS)
codegen.invokeAppend(arguments.get(1));
}
else { // LHS.plus(RHS)
receiver.put(AsmTypeConstants.OBJECT_TYPE, v);
codegen.generateStringBuilderConstructor();
CodegenUtil.generateStringBuilderConstructor(v);
v.swap(); // StringBuilder LHS
codegen.invokeAppendMethod(expectedType); // StringBuilder(LHS)
CodegenUtil.invokeAppendMethod(v, expectedType); // StringBuilder(LHS)
codegen.invokeAppend(arguments.get(0));
}