package me.tomassetti.turin.compiler;
import me.tomassetti.bytecode_generation.*;
import me.tomassetti.bytecode_generation.logicalop.CastBS;
import me.tomassetti.bytecode_generation.logicalop.LogicalNotBS;
import me.tomassetti.bytecode_generation.pushop.*;
import me.tomassetti.bytecode_generation.returnop.ReturnFalseBS;
import me.tomassetti.bytecode_generation.returnop.ReturnTrueBS;
import me.tomassetti.jvm.*;
import me.tomassetti.turin.definitions.TypeDefinition;
import me.tomassetti.turin.definitions.InternalConstructorDefinition;
import me.tomassetti.turin.parser.analysis.Property;
import me.tomassetti.turin.resolvers.SymbolResolver;
import me.tomassetti.turin.parser.ast.FormalParameterNode;
import me.tomassetti.turin.parser.ast.properties.PropertyConstraint;
import me.tomassetti.turin.parser.ast.TurinTypeDefinition;
import me.tomassetti.turin.parser.ast.typeusage.TypeUsageNode;
import me.tomassetti.turin.symbols.FormalParameter;
import me.tomassetti.turin.typesystem.UnsignedPrimitiveTypeUsage;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.List;
import java.util.stream.Collectors;
public class CompilationOfGeneratedMethods {
private final Compilation compilation;
private ClassWriter cw;
public CompilationOfGeneratedMethods(Compilation compilation, ClassWriter cw) {
this.compilation = compilation;
this.cw = cw;
}
private void enforceConstraint(Property property, MethodVisitor mv, BytecodeSequence getValue) {
if (property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.UINT)
|| property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.UBYTE)
|| property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.USHORT)) {
getValue.operate(mv);
Label label = new Label();
// if the value is >= 0 we jump and skip the throw exception
mv.visitJumpInsn(Opcodes.IFGE, label);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, new PushStringConst(property.getName() + " should be positive"));
new ThrowBS(instantiateException).operate(mv);
mv.visitLabel(label);
} else if (property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.ULONG)) {
getValue.operate(mv);
Label label = new Label();
// the value is already pushed: push also the value to be compared
new PushLongConst(0).operate(mv);
mv.visitInsn(Opcodes.LCMP);
// if the value is >= 0 we jump and skip the throw exception
mv.visitJumpInsn(Opcodes.IFGE, label);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, new PushStringConst(property.getName() + " should be positive"));
new ThrowBS(instantiateException).operate(mv);
mv.visitLabel(label);
} else if (property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.UFLOAT)) {
getValue.operate(mv);
Label label = new Label();
// the value is already pushed: push also the value to be compared
new PushFloatConst(0).operate(mv);
// NaN does not cause an error here
mv.visitInsn(Opcodes.FCMPG);
// if the value is >= 0 we jump and skip the throw exception
mv.visitJumpInsn(Opcodes.IFGE, label);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, new PushStringConst(property.getName() + " should be positive"));
new ThrowBS(instantiateException).operate(mv);
mv.visitLabel(label);
} else if (property.getTypeUsage().sameType(UnsignedPrimitiveTypeUsage.UDOUBLE)) {
getValue.operate(mv);
Label label = new Label();
// the value is already pushed: push also the value to be compared
new PushDoubleConst(0).operate(mv);
// NaN does not cause an error here
mv.visitInsn(Opcodes.DCMPG);
// if the value is >= 0 we jump and skip the throw exception
mv.visitJumpInsn(Opcodes.IFGE, label);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, new PushStringConst(property.getName() + " should be positive"));
new ThrowBS(instantiateException).operate(mv);
mv.visitLabel(label);
} else if (property.getTypeUsage().isReferenceTypeUsage()) {
getValue.operate(mv);
Label label = new Label();
// if not null skip the throw
mv.visitJumpInsn(Opcodes.IFNONNULL, label);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, new PushStringConst(property.getName() + " cannot be null"));
new ThrowBS(instantiateException).operate(mv);
mv.visitLabel(label);
}
for (PropertyConstraint constraint : property.getConstraints()) {
compilation.getLocalVarsSymbolTable().recordAlias("placeholder", getValue);
compilation.getPushUtils().pushExpression(constraint.getCondition()).operate(mv);
JvmConstructorDefinition constructor = new JvmConstructorDefinition("java/lang/IllegalArgumentException", "(Ljava/lang/String;)V");
BytecodeSequence instantiateException = new NewInvocationBS(constructor, compilation.getPushUtils().pushExpression(constraint.getMessage()));
new IfBS(new LogicalNotBS(compilation.getPushUtils().pushExpression(constraint.getCondition())), new ThrowBS(instantiateException)).operate(mv);
}
}
private void enforceConstraint(Property property, MethodVisitor mv, JvmType jvmType, int varIndex) {
enforceConstraint(property, mv, new BytecodeSequence() {
@Override
public void operate(MethodVisitor mv) {
mv.visitVarInsn(OpcodesUtils.loadTypeFor(jvmType), varIndex + 1);
}
});
}
private void addLocalVarForFormalParameter(FormalParameterNode formalParameter, Label start, Label end, MethodVisitor mv) {
if (!formalParameter.hasDefaultValue()) {
int index = compilation.getLocalVarsSymbolTable().add(formalParameter.getName(), formalParameter);
mv.visitLocalVariable(formalParameter.getName(),
formalParameter.getType().jvmType().getDescriptor(),
formalParameter.getType().jvmType().getSignature(),
start,
end,
index);
}
}
void generateSetter(Property property, String internalClassName) {
compilation.setLocalVarsSymbolTable(LocalVarsSymbolTable.forInstanceMethod());
String setterName = property.setterName();
JvmType jvmType = property.getTypeUsage().jvmType();
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, setterName, "(" + jvmType.getDescriptor() + ")V", "(" + jvmType.getSignature() + ")V", null);
Label start = new Label();
Label end = new Label();
mv.visitLabel(start);
TypeUsageNode typeUsageCopy = property.getTypeUsage().copy();
FormalParameterNode formalParameter = new FormalParameterNode(typeUsageCopy, property.getName());
formalParameter.setParent(property.getParent());
addLocalVarForFormalParameter(formalParameter, start, end, mv);
mv.visitCode();
compilation.getLocalVarsSymbolTable().add(property.getName(), new FormalParameterNode(property.getTypeUsage().copy(), property.getName()));
compilation.getLocalVarsSymbolTable().recordAlias("placeholder",
new PushLocalVar(OpcodesUtils.loadTypeFor(jvmType), 1));
enforceConstraint(property, mv, jvmType, 0);
// Assignment
PushThis.getInstance().operate(mv);
mv.visitVarInsn(OpcodesUtils.loadTypeFor(jvmType), 1);
mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, property.getName(), jvmType.getDescriptor());
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(end);
// calculated for us
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void generateConstructor(TurinTypeDefinition typeDefinition, String className) {
InternalConstructorDefinition superConstructor = null;
if (typeDefinition.getBaseType().isPresent()) {
TypeUsageNode baseType = typeDefinition.getBaseType().get();
if (!baseType.isReferenceTypeUsage()) {
throw new IllegalStateException();
}
TypeDefinition baseTypeDefinition = baseType.asReferenceTypeUsage().getTypeDefinition();
if (baseTypeDefinition.hasManyConstructors()) {
throw new IllegalStateException();
}
superConstructor = baseTypeDefinition.getConstructors().get(0);
}
SymbolResolver resolver = compilation.getResolver();
//
// Define the constructor prototype
//
List<? extends FormalParameter> params = typeDefinition.getOnlyConstructor(resolver).getFormalParameters();
List<FormalParameter> formalParametersWithoutDefaults = params.stream().filter((p)->!p.hasDefaultValue()).collect(Collectors.<FormalParameter>toList());
String paramsDescriptor = String.join("", formalParametersWithoutDefaults.stream().map((p) -> p.getType().jvmType().getDescriptor()).collect(Collectors.toList()));
String paramsSignature = String.join("", formalParametersWithoutDefaults.stream().map((p) -> p.getType().jvmType().getSignature()).collect(Collectors.toList()));
if (typeDefinition.hasDefaultProperties(resolver) ||
(superConstructor!=null && superConstructor.hasDefaultParams())) {
paramsDescriptor += "Ljava/util/Map;";
paramsSignature += "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;";
}
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" + paramsDescriptor + ")V", "(" + paramsSignature + ")V", null);
compilation.addDefaultParamAnnotations(mv, typeDefinition.getOnlyConstructor(resolver).getFormalParameters());
mv.visitCode();
compilation.setLocalVarsSymbolTable(LocalVarsSymbolTable.forInstanceMethod());
Label start = new Label();
Label end = new Label();
mv.visitLabel(start);
for (FormalParameter formalParameter : typeDefinition.getOnlyConstructor(resolver).getFormalParameters()) {
if (!formalParameter.hasDefaultValue()) {
int index = compilation.getLocalVarsSymbolTable().add(formalParameter.getName(), formalParameter);
mv.visitLocalVariable(formalParameter.getName(),
formalParameter.getType().jvmType().getDescriptor(),
formalParameter.getType().jvmType().getSignature(),
start,
end,
index);
}
}
//
// Invoke super constructor
//
PushThis.getInstance().operate(mv);
if (superConstructor == null) {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Compilation.OBJECT_INTERNAL_NAME, "<init>", "()V", false);
} else {
// push all explicitly passed parameters
int index = 1;
for (FormalParameter formalParameter : superConstructor.getFormalParameters()){
if (!formalParameter.hasDefaultValue()) {
JvmType jvmType = formalParameter.getType().jvmType();
new PushLocalVar(OpcodesUtils.loadTypeFor(jvmType), index).operate(mv);
index++;
}
}
// push the map if it has default values
if (superConstructor.hasDefaultParams()) {
// push the map which is in the parameters after all the formal parameters
index = formalParametersWithoutDefaults.size() + 1;
new PushLocalVar(Opcodes.ALOAD, index).operate(mv);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superConstructor.getJvmConstructorDefinition().getOwnerInternalName(), "<init>",
superConstructor.getJvmConstructorDefinition().getDescriptor(), false);
}
//
// Assign the properties passed explicitly
//
List<Property> directProperties = typeDefinition.getDirectProperties(resolver);
List<Property> directPropertiesAsParameters = directProperties.stream()
.filter((p)->!p.hasDefaultValue() && !p.hasInitialValue())
.collect(Collectors.toList());
int startIndex = 0;
if (superConstructor != null) {
startIndex = (int)superConstructor.getFormalParameters().stream().filter((p)->!p.hasDefaultValue()).count();
}
assignPropertiesPassedExplicitely(typeDefinition, className, resolver, directPropertiesAsParameters, mv, startIndex);
//
// Assign the properties with initial value
//
assignPropertiesWithInitialValue(className, resolver, directProperties, mv);
// now we should get values from the defaultParamsMap and assign them
// to fields
if (typeDefinition.hasDefaultProperties(resolver)) {
int indexOfMapOfDefaults = formalParametersWithoutDefaults.size() + 1;
assignDefaultPropertiesFromMapParam(typeDefinition, className, resolver, mv, indexOfMapOfDefaults);
}
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(end);
// calculated for us
mv.visitMaxs(0, 0);
mv.visitEnd();
compilation.setLocalVarsSymbolTable(null);
}
private void assignDefaultPropertiesFromMapParam(TurinTypeDefinition typeDefinition, final String className, SymbolResolver resolver, MethodVisitor mv, int indexOfMapOfDefaults) {
int localVarIndex = indexOfMapOfDefaults;
for (Property property : typeDefinition.defaultPropeties(resolver)) {
JvmType jvmType = property.getTypeUsage().jvmType();
BytecodeSequence isPropertyInMap = new ComposedBytecodeSequence(
// we push the map
new PushLocalVar(Opcodes.ALOAD, localVarIndex),
new PushStringConst(property.getName()),
new MethodInvocationBS(new JvmMethodDefinition("java/util/Map", "containsKey", "(Ljava/lang/Object;)Z", false, true)));
BytecodeSequence getSequence = new ComposedBytecodeSequence(
// we push the map
new PushLocalVar(Opcodes.ALOAD, localVarIndex),
new PushStringConst(property.getName()),
new MethodInvocationBS(new JvmMethodDefinition("java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false, true))
);
String propertyBoxedType = property.getTypeUsage().isPrimitive() ?
property.getTypeUsage().asPrimitiveTypeUsage().getBoxType(compilation.getResolver()).jvmType().getInternalName()
: property.getTypeUsage().jvmType().getInternalName();
BytecodeSequence assignPropertyFromMap = new ComposedBytecodeSequence(
PushThis.getInstance(),
getSequence,
new CastBS(propertyBoxedType),
property.getTypeUsage().isPrimitive() ?
new UnboxBS(property.getTypeUsage().jvmType())
: NoOp.getInstance(),
new BytecodeSequence() {
@Override
public void operate(MethodVisitor mv) {
mv.visitFieldInsn(Opcodes.PUTFIELD, className, property.getName(), jvmType.getDescriptor());
}
});
BytecodeSequence assignPropertyFromDefaultValue = new ComposedBytecodeSequence(
PushThis.getInstance(),
compilation.getPushUtils().pushExpression(property.getDefaultValue().get()),
new BytecodeSequence() {
@Override
public void operate(MethodVisitor mv) {
mv.visitFieldInsn(Opcodes.PUTFIELD, className, property.getName(), jvmType.getDescriptor());
}
});
new IfBS(isPropertyInMap, assignPropertyFromMap, assignPropertyFromDefaultValue).operate(mv);
JvmFieldDefinition jvmFieldDefinition = new JvmFieldDefinition(className, property.getName(), property.getTypeUsage().jvmType().getDescriptor(),false);
enforceConstraint(property, mv, new PushInstanceField(jvmFieldDefinition));
}
}
private void assignPropertiesWithInitialValue(String className, SymbolResolver resolver, List<Property> directProperties, MethodVisitor mv) {
List<Property> directPropertiesWithInitialValue = directProperties.stream()
.filter((p) -> p.hasInitialValue())
.collect(Collectors.toList());
for (Property property : directPropertiesWithInitialValue) {
JvmType jvmType = property.getTypeUsage().jvmType();
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_THIS_IN_METHOD);
compilation.getPushUtils().pushExpression(property.getInitialValue().get()).operate(mv);
mv.visitFieldInsn(Opcodes.PUTFIELD, className, property.getName(), jvmType.getDescriptor());
JvmFieldDefinition jvmFieldDefinition = new JvmFieldDefinition(className, property.getName(), property.getTypeUsage().jvmType().getDescriptor(),false);
enforceConstraint(property, mv, new PushInstanceField(jvmFieldDefinition));
}
}
private void assignPropertiesPassedExplicitely(TurinTypeDefinition typeDefinition, String className, SymbolResolver resolver,
List<Property> directPropertiesAsParameters, MethodVisitor mv, int startIndex) {
int propIndex = startIndex;
for (Property property : directPropertiesAsParameters) {
enforceConstraint(property, mv, property.getTypeUsage().jvmType(), propIndex);
propIndex++;
}
propIndex = startIndex;
for (Property property : typeDefinition.propertiesAppearingInDefaultConstructor(resolver)) {
JvmType jvmType = property.getTypeUsage().jvmType();
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_THIS_IN_METHOD);
mv.visitVarInsn(OpcodesUtils.loadTypeFor(jvmType), propIndex + 1);
mv.visitFieldInsn(Opcodes.PUTFIELD, className, property.getName(), jvmType.getDescriptor());
propIndex++;
}
}
void generateEqualsMethod(TurinTypeDefinition typeDefinition, String internalClassName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", "(" + Compilation.OBJECT_DESCRIPTOR + ")Z", "(" + Compilation.OBJECT_DESCRIPTOR + ")Z", null);
mv.visitCode();
// if (this == o) return true;
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_THIS_IN_METHOD);
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_PARAM_0);
Label paramAndThisAreNotTheSame = new Label();
mv.visitJumpInsn(Opcodes.IF_ACMPNE, paramAndThisAreNotTheSame);
new ReturnTrueBS().operate(mv);
mv.visitLabel(paramAndThisAreNotTheSame);
// if (o == null || getClass() != o.getClass()) return false;
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_PARAM_0);
Label paramIsNull = new Label();
mv.visitJumpInsn(Opcodes.IFNULL, paramIsNull);
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_THIS_IN_METHOD);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_PARAM_0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
Label paramHasSameClassAsThis = new Label();
mv.visitJumpInsn(Opcodes.IF_ACMPEQ, paramHasSameClassAsThis);
mv.visitLabel(paramIsNull);
new ReturnFalseBS().operate(mv);
mv.visitLabel(paramHasSameClassAsThis);
// MyType other = (MyType) o;
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_PARAM_0);
mv.visitTypeInsn(Opcodes.CHECKCAST, internalClassName);
final int localvar_index_for_other = 2;
mv.visitVarInsn(Opcodes.ASTORE, localvar_index_for_other);
// if (!this.aField.equals(other.aField)) return false;
for (Property property : typeDefinition.getAllProperties(compilation.getResolver())) {
TypeUsageNode propertyTypeUsage = property.getTypeUsage();
String fieldTypeDescriptor = propertyTypeUsage.jvmType().getDescriptor();
mv.visitVarInsn(Opcodes.ALOAD, Compilation.LOCALVAR_INDEX_FOR_THIS_IN_METHOD);
mv.visitFieldInsn(Opcodes.GETFIELD, internalClassName, property.getName(), fieldTypeDescriptor);
mv.visitVarInsn(Opcodes.ALOAD, localvar_index_for_other);
mv.visitFieldInsn(Opcodes.GETFIELD, internalClassName, property.getName(), fieldTypeDescriptor);
Label propertyIsEqual = new Label();
if (propertyTypeUsage.isPrimitive()) {
if (propertyTypeUsage.asPrimitiveTypeUsage().isLong()) {
mv.visitInsn(Opcodes.LCMP);
mv.visitJumpInsn(Opcodes.IFEQ, propertyIsEqual);
} else if (propertyTypeUsage.asPrimitiveTypeUsage().isFloat()) {
mv.visitInsn(Opcodes.FCMPL);
mv.visitJumpInsn(Opcodes.IFEQ, propertyIsEqual);
} else if (propertyTypeUsage.asPrimitiveTypeUsage().isDouble()) {
mv.visitInsn(Opcodes.DCMPL);
mv.visitJumpInsn(Opcodes.IFEQ, propertyIsEqual);
} else {
mv.visitJumpInsn(Opcodes.IF_ICMPEQ, propertyIsEqual);
}
} else {
boolean isInterface = propertyTypeUsage.asReferenceTypeUsage().isInterface(compilation.getResolver());
if (isInterface) {
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, propertyTypeUsage.jvmType().getInternalName(), "equals", "(Ljava/lang/Object;)Z", true);
} else {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, propertyTypeUsage.jvmType().getInternalName(), "equals", "(Ljava/lang/Object;)Z", false);
}
mv.visitJumpInsn(Opcodes.IFNE, propertyIsEqual);
}
new ReturnFalseBS().operate(mv);
mv.visitLabel(propertyIsEqual);
}
new ReturnTrueBS().operate(mv);
// calculated for us
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void generateHashCodeMethod(TurinTypeDefinition typeDefinition, String internalClassName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "hashCode", "()I", "()I", null);
mv.visitCode();
final int localvar_index_of_result = 1;
// int result = 1;
mv.visitInsn(Opcodes.ICONST_1);
mv.visitVarInsn(Opcodes.ISTORE, localvar_index_of_result);
for (Property property : typeDefinition.getAllProperties(compilation.getResolver())) {
// result = 31 * result + this.aField.hashCode();
TypeUsageNode propertyTypeUsage = property.getTypeUsage();
String fieldTypeDescriptor = propertyTypeUsage.jvmType().getDescriptor();
// 31 is just a prime number by which we multiply the current value of result
mv.visitIntInsn(Opcodes.BIPUSH, 31);
mv.visitVarInsn(Opcodes.ILOAD, localvar_index_of_result);
mv.visitInsn(Opcodes.IMUL);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, internalClassName, property.getName(), fieldTypeDescriptor);
if (propertyTypeUsage.isPrimitive()) {
if (propertyTypeUsage.asPrimitiveTypeUsage().isLong()) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "hashCode", "(J)I", false);
} else if (propertyTypeUsage.asPrimitiveTypeUsage().isFloat()) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "hashCode", "(F)I", false);
} else if (propertyTypeUsage.asPrimitiveTypeUsage().isDouble()) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "hashCode", "(D)I", false);
} else {
// nothing to do, the value is already on the stack and we can sum it directly
}
} else {
boolean isInterface = propertyTypeUsage.asReferenceTypeUsage().isInterface(compilation.getResolver());
if (isInterface) {
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, propertyTypeUsage.jvmType().getInternalName(), "hashCode", "()I", true);
} else {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, propertyTypeUsage.jvmType().getInternalName(), "hashCode", "()I", false);
}
}
mv.visitInsn(Opcodes.IADD);
mv.visitVarInsn(Opcodes.ISTORE, localvar_index_of_result);
}
// return result;
mv.visitVarInsn(Opcodes.ILOAD, localvar_index_of_result);
mv.visitInsn(Opcodes.IRETURN);
// calculated for us
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}