I am working on a small java bytecode instrumentation tool.
The general idea is to have all of a class methods renamed with a _CONGU
suffix, creating then proxy methods with the original method names that will call their _CONGU
counterparts.
For instance, if a class C
contains a int m() { return 1; }
method, the instrumented C
class will have a int m_CONGU() { return 1; }
method and a int m() { return m_CONGU(); }
method.
Later on I'll add some extra logic on int m()
that will do some checks before calling m_CONGU()
.
At the moment, I am in the process of instrumenting all constructor invocation calls to a method of mine with the _CONGU
suffix.
Bellow you can see both the instrumented and non-instrumented versions of the inverse()
method of a Fraction
class.
When trying to run this code
Fraction fraction = new Fraction(4, 1);
I get the following exception which is puzzling me for the last couple of hours:
Exception in thread "main" java.lang.VerifyError: (class: jorgeTestes/system/fraction/Fraction, method: inverse_CONGU signature: ()LjorgeTestes/system/fraction/Fraction;) Expecting to find object/array on stack at jorgeTestes.system.fraction.XyzTest.main(XyzTest.java:9)
I guess this must be a dead obvious mistake, but I can't get what the problem might be. It looks to be in some way related to having the wrong number of data in the stack, but it looks to me that the number of elements in the stack is the same both on the original and instrumented code (at least that's what should be happening). Any ideas?
1) here are the descriptors of both <init>
and Fraction_CONGU
(which are the same, as one would expect!):
2) I'm wondering if the color of [0] Code
being different in Bytecode Viewer could mean there's some other problem with my instrumented code? Maybe there is some metadata that's being broken in the process, so that could be the reason the code looks alright and there's still a problem when trying to run the code?
It looks like the code on the first screenshot is fundamentally broken. Object construction in JVM bytecode can be split into two phass: allocating memory on the heap and calling a constructor (with optional parameters) against that allocated memory:
new #1 <Fraction> //allocate memory
dup //duplicate to not loose the object after calling constructor
//push c-tor args onto the stack here
invokespecial <Fraction.<init>> //constructor, second `this` is lost
areturn //returns first `this`
What you are basically doing is: allocate some raw (probably zeroed) memory on the heap and invoke virtual method Fraction_congu
against that memory block. You haven't yet called the constructor!
Also there should be invokevirtual
, I guess this generated methods are private
.
UPDATE: I assume you want to transform the following class:
class Fraction {
public Fraction(float den, float num) {
//original constructor code here
}
public int m() {
return 1;
}
}
Into:
class Fraction {
public Fraction(float den, float num) {
//proxy method
//place for extra logic
Fraction_CONGU(den, num);
}
private Fraction_CONGU(float den, float num) {
//original constructor code here
}
public int m() {
//proxy method
//place for extra logic
return m_CONGU
}
private int m_CONGU() {
return 1;
}
}
As you can see it is perfectly possible (if I get your idea correctly). Just compile this Java code and see how compiler implemented this.
Which begs a question: can't you just use AspectJ with compile time-weaving?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With