Skip to content

Coding errors

Brian S. O'Neill edited this page May 4, 2024 · 15 revisions

It's possible to make classes which the virtual machine rejects, resulting in a LinkageError of some kind:

  • BootstrapMethodError — caused by an indy/condy bootstrap method which fails to produce a valid result
  • ClassFormatError — caused by an illegal name, too many method parameters, or an invalid exception handler range
  • ExceptionInInitializerError — caused by an exception throwing out from a static initializer
  • IncompatibleClassChangeError — caused by failing to implement an abstract method, or by trying to access an inaccessible class member
  • VerifyError — the code in a method body is broken

With MethodMaker it's almost impossible to define code which can throw a VerifyError. One notable exception is when attempting to access a variable which hasn't been assigned yet:

    ClassMaker cm = ClassMaker.begin();
    MethodMaker mm = cm.addMethod(int.class, "test").public_().static_();

    var v1 = mm.var(int.class);

    Label start = mm.label().here();
    //v1.set(1);
    Label done = mm.label().goto_();
    mm.catch_(start, Throwable.class, ex -> {
        v1.set(2);
    });

    done.here();
    mm.return_(v1); // v1 isn't guaranteed to have been assigned

    var clazz = cm.finish();
    clazz.getMethod("test").invoke(null);

When running the above example:

Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    org/cojen/maker/ClassMaker-9.test()I @6: iload_0
  Reason:
    Type top (current frame, locals[0]) is not assignable to integer
  Current Frame:
    bci: @6
    flags: { }
    locals: { }
    stack: { }
  Bytecode:
    0000000: a700 0657 053b 1aac
  Exception Handler Table:
    bci [0, 3] => handler: 3
  Stackmap Table:
    same_locals_1_stack_item_frame(@3,Object[#6])
    same_frame(@6)

The VerifyError tends to produce a ton of extra information, and this makes it look much scarier than it actually is. Try to find an execution path in the code which accesses a local variable that hasn't been definitely assigned. One way to help eliminate the error is to call the clear method when defining variables, to ensure that they're definitely assigned to something.

Another option is to inspect the generated class by running Java with -Dorg.cojen.maker.ClassMaker.DEBUG=true. With this option, all generated classes are written to the temp directory. A tool like javap or any other Java disassembler can then be used, although some understanding of JVM instruction format is necessary to make sense of the results.

Java front-end compilers perform definite assignment analysis, and so a VerifyError isn't possible unless the compiler is defective. Although Cojen could perform definite assignment analysis too, it's redundant and adds overhead when making classes at runtime. A front-end compiler which uses Cojen should perform it's own definite assignment analysis, or else prevent assignment problems in the first place.

Another type of VerifyError can be caused by a constructor which doesn't call a super or this method. Note that if the constructor has no parameters and has no method body, the MethodMaker automatically calls the super constructor.

Clone this wiki locally