Sander_M
Sander_M

Reputation: 1119

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?

Upvotes: 0

Views: 270

Answers (1)

Raffaele
Raffaele

Reputation: 20885

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

Upvotes: 1

Related Questions