-
Notifications
You must be signed in to change notification settings - Fork 2
Coding errors
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.