I used classloader and Unsafe::definedAnonymous() to load generated bytecode byte[]. The usage of Class returned by classLoader.loadClass() succeeds while it fails with c.getMethod()
in which c=Unsafe.defineAnonymousClass()
API. So is the generated bytecode WRONG?
My code:
MainInliner loader = new MainInliner();
Class<?> c =null;
byte[] bytes = ...
if(args[0].equals("0")){
c = loader.loadClass(name, bytes, 0, bytes.length); // Classloader.loadClass.
}else{
c = Unsafe.defineAnonymousClass(Hoster, bytes, null);
}
Method m = c.getMethod("main", new Class<?>[] { String[].class });
The error is:
Exception Details:
Location:
code/sxu/asm/example/Caller.test(II)V @98: lload_3
Reason:
Type integer (current frame, locals[3]) is not assignable to long
Current Frame:
bci: @98
flags: { }
locals: { 'code/sxu/asm/example/Caller', integer, integer, integer, integer, integer, integer, integer, integer, integer }
stack: { 'java/io/PrintStream' }
Bytecode:
0000000: 1b85 2ab4 000e 1b1c 3e36 043a 0537 0619
0000010: 05b4 002a b600 3019 05b4 0033 b600 3060
0000020: 3608 1508 3609 1606 1509 a700 0385 6542
0000030: 1b2a b400 0e1c 033e 3604 3a05 3606 1905
0000040: b400 2ab6 0030 1905 b400 33b6 0030 6036
0000050: 0715 0736 0815 0615 08a7 0003 6036 05b2
0000060: 003d 21b6 0043 b1
Stackmap Table:
full_frame(@45,{Object[#2],Integer,Integer,Integer,Integer,Object[#21],Long,Integer,Integer},{Long,Integer})
full_frame(@92,{Object[#2],Integer,Integer,Integer,Integer,Object[#21],Integer,Integer,Integer,Integer},{Integer,Integer})
and the generated bytecode is:
public void test(int, int);
flags: ACC_PUBLIC
Code:
stack=5, locals=10, args_size=3
0: iload_1
1: i2l
2: aload_0
3: getfield #14 // Field _callee:Lcode/sxu/asm/example/Callee;
6: iload_1
7: iload_2
8: istore_3
9: istore 4
11: astore 5
13: lstore 6
15: aload 5
17: getfield #42 // Field code/sxu/asm/example/Callee._a:Ljava/lang/String;
20: invokevirtual #48 // Method java/lang/String.length:()I
23: aload 5
25: getfield #51 // Field code/sxu/asm/example/Callee._b:Ljava/lang/String;
28: invokevirtual #48 // Method java/lang/String.length:()I
31: iadd
32: istore 8
34: iload 8
36: istore 9
38: lload 6
40: iload 9
42: goto 45
45: i2l
46: lsub
47: lstore_3 //r..
48: iload_1
49: aload_0
50: getfield #14 // Field _callee:Lcode/sxu/asm/example/Callee;
53: iload_2
54: iconst_0
55: istore_3
56: istore 4
58: astore 5
60: istore 6 //The backup stack bottom variable.
62: aload 5
64: getfield #42 // Field code/sxu/asm/example/Callee._a:Ljava/lang/String;
67: invokevirtual #48 // Method java/lang/String.length:()I
70: aload 5
72: getfield #51 // Field code/sxu/asm/example/Callee._b:Ljava/lang/String;
75: invokevirtual #48 // Method java/lang/String.length:()I
78: iadd
79: istore 7
81: iload 7
83: istore 8
85: iload 6 //Push back 6
87: iload 8 //Repush 8
89: goto 92
//return calculate()
92: iadd
93: istore 5
95: getstatic #61 // Field java/lang/System.out:Ljava/io/PrintStream;
98: lload_3
99: invokevirtual #67 // Method java/io/PrintStream.println:(J)V
102: return
LocalVariableTable:
Start Length Slot Name Signature
15 30 5 this Lcode/sxu/asm/example/Callee;
15 30 4 t I
15 30 3 p I
34 11 8 tmp I
62 30 5 this Lcode/sxu/asm/example/Callee;
62 30 4 t I
62 30 3 p I
81 11 7 tmp I
0 103 0 this Lcode/sxu/asm/example/Caller;
0 103 1 a I
0 103 2 b I
48 55 3 r J
95 8 5 c I
LineNumberTable:
line 19: 0
line 16: 15
line 23: 34
line 21: 48
line 16: 62
line 23: 81
line 23: 95
line 30: 102
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 45
locals = [ class code/sxu/asm/example/Caller, int, int, int, int, class code/sxu/asm/example/Callee, long, int, int ]
stack = [ long, int ]
frame_type = 255 /* full_frame */
offset_delta = 46
locals = [ class code/sxu/asm/example/Caller, int, int, int, int, class code/sxu/asm/example/Callee, int, int, int, int ]
stack = [ int, int ]
}
This bytecode corresponding to inline a Callee method.
I checked this bytecode sequence, and it seems there is no error it. The variable 3 in the error message occurs 4 times, in which:
8: istore_3 //Popo up existing parameters for inlining.
47: lstore_3 //r. The local variable in the Caller method
55: istore_3 // Similiar to the label 8, pops stack paras to local variable.
98: lload_3 //Println parameter
The corresponding method is:
public class Caller{
public void test(int a, int b){
long r = (long)a- _callee.calculate(a, b);
int c = a +_callee.calculate(b, 0);
System.out.println(r);
}
}
public class Callee{
public int calculate(int t, int p){
int tmp=_a.length()+_b.length();
return tmp;
}}
loader.loadClass(bytes)
mean? There is no suchClassLoader.loadClass(byte[])
method. How did you generate the bytecode? Did you start with an existing method and modify it? – Antietamloader.loadClass(name, bytes, 0, bytes.length)
mean? There is no suchClassLoader.loadClass(String, byte[], int, int)
method. <- you didn't answer the first part of @bkail's ocmment. – Beltane55: istore_3
– Leidint
into variable #3 @55, you can’t read along
from the same variable @98. It might be that the verification has been deferred when the class has been defined via an ordinary class loader. Besides that, I recommend not to use Reflection on classes created viaUnsafe.defineAnonymousClass
, unless you want to play Russian roulette. UseMethodHandle
s to access the generated code… – RuphinaReflection
is a good suggestion for performance. – LeidMethod
will try to optimize itself by compiling an accessor class, but since anonymous classes can’t be accessed by other classes, you suddenly receiveError
s when callinginvoke
. That might happen after twenty invocations or so. As said, that’s dependent on the implementation, hence, jre version. That’s why I said Russian roulette. – Ruphina