Sander_M Sander_M - 28 days ago 11
Java Question

java.lang.VerifyError thrown when creating a method using JavaAssist

I am trying to use JavaAssist version 3.12.1.GA to do some runtime code generation of a Pojo implementing an interface using Java 8. I ran into an error when trying to create a method that has a return type of Object.

The error:

Caused by: java.lang.VerifyError: (class: person, method: getColumnByIndex signature: (I)Ljava/lang/Object;) Wrong return type in function


gets thrown when adding the
getColumnByIndex
method.

Here is the full example class:

public class Example {

public interface Domain {
public int getIdentifier();
public Object getColumnByIndex(int i);
}

public static void main(final String[] args) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {

final ClassPool pool = ClassPool.getDefault();
final CtClass cc = pool.makeClass("Person");
cc.addInterface(resolveCtClass(Domain.class));

final CtField idField = new CtField(CtClass.intType, "id", cc);
final CtMethod idGetter = CtNewMethod.getter("getId", idField);
final CtMethod idSetter = CtNewMethod.setter("setId", idField);
cc.addField(idField);
cc.addMethod(idGetter);
cc.addMethod(idSetter);

final CtField firstNameField = new CtField(resolveCtClass(String.class), "firstName", cc);
final CtMethod firstNameGetter = CtNewMethod.getter("getFirstName", firstNameField);
final CtMethod firstNameSetter = CtNewMethod.setter("setFirstName", firstNameField);
cc.addField(firstNameField);
cc.addMethod(firstNameSetter);
cc.addMethod(firstNameGetter);

final CtMethod getIdentifier = CtNewMethod.make("public int getIdentifier () { return id; }", cc);
cc.addMethod(getIdentifier);

final CtMethod getColumnByIndex = CtNewMethod.make(
"public Object getColumnByIndex(int i) {"
+ "switch (i) {"
+ "case 0:"
+ "return id;"
+ "case 1:"
+ "return firstName;"
+ "default: "
+ "throw new IllegalArgumentException(\"Tried getting column index i, but this column index does not exist\");"
+ "}"
+ "}", cc);
cc.addMethod(getColumnByIndex);
final Class<?> dynamicClass = cc.toClass();

final Domain domainImpl = (Domain) dynamicClass.newInstance();
System.out.println(domainImpl.getIdentifier());
System.out.println(domainImpl.getColumnByIndex(0));
}

private static CtClass resolveCtClass(final Class<?> clazz) throws NotFoundException {
final ClassPool pool = ClassPool.getDefault();
return pool.get(clazz.getName());
}


How can I resolve the java.lang.VeryifyError?

Answer

Apparently the id field of type int is not automatically boxed into Integer, so you either do it by hand:

switch (i) {
  case 0: return Integer.valueOf(id);
  case 1: return firstName;
  default: throw new IllegalArgumentException("...");
}

or you convert everything to Integer, including the return type of Domain.getIdentifier(). Primitive types in Java are not Object's! Wrapper types exist to fake a single root hierarchy in the JVM, and the compiler silently inserts calls like Integer.valueOf(int) and Integer.intValue() when appropriate, but sometimes the abstraction leaks up to the user.

Since the code you are writing seems a database access thing, I'd prefer the second option so that everything can be nullable - otherwise to represent a record yet-to-be-inserted you'd have to rely on some canary value like zero, -1 or whatever.

The runnable gist