Pop from empty stack after MethodCall.replace
dschulten Aug 21, 2009 2:27 AMHi,
I need to replace the following call in legacy bytecode:
dataInputStream.read(buf, 0, len);
by
dataInputStream.readFully(buf, 0, len);
The original read() has the obvious flaw that it does not check for the return value of dataInputStream.read, which leads to problems if the implementation chooses to fill the buffer only partially. I want to solve this bug using Javassist.
I do the following:
ExprEditor exprEditor = new ExprEditor() {
@Override
public void edit(MethodCall methodCall)
throws CannotCompileException {
String methodName = methodCall.getMethodName();
if (methodName.equals("read")) {
methodCall.replace("$_ = $0.readFully($$);");
didChangesToClassfile = true;
}
}
};
But the created class is broken, it is rejected by the classloader and jode says "Pop from empty stack".
Obviously I want to replace a method call with a return value which is being ignored by a void method call. I guess this is what causes the problem, but maybe I am wrong.
The offending bytecode is below, look around readFully in line 114:
public void (String arg1)
Code(max_stack = 4, max_locals = 9, code_length = 139)
0: aload_0
1: invokespecial java.lang.Object. ()V (23)
4: new <java.io.DataInputStream> (2)
7: dup
8: aload_0
9: invokevirtual java.lang.Object.getClass ()Ljava/lang/Class; (24)
12: aload_1
13: invokevirtual java.lang.Class.getResourceAsStream (Ljava/lang/String;)Ljava/io/InputStream; (22)
16: invokespecial java.io.DataInputStream. (Ljava/io/InputStream;)V (16)
19: dup
20: astore_1
21: invokevirtual java.io.DataInputStream.readUnsignedByte ()I (21)
24: i2c
25: bipush 70
27: if_icmpeq #31
30: return
31: aload_1
32: invokevirtual java.io.DataInputStream.readUnsignedByte ()I (21)
35: i2c
36: bipush 49
38: if_icmpeq #42
41: return
42: aload_0
43: aload_1
44: invokevirtual java.io.DataInputStream.readByte ()B (19)
47: putfield ak.h I (15)
50: aload_0
51: aload_1
52: invokevirtual java.io.DataInputStream.readByte ()B (19)
55: putfield ak.d I (11)
58: aload_0
59: aload_1
60: invokevirtual java.io.DataInputStream.readByte ()B (19)
63: putfield ak.e I (12)
66: aload_0
67: aload_1
68: invokevirtual java.io.DataInputStream.readByte ()B (19)
71: putfield ak.f I (13)
74: aload_0
75: aload_1
76: invokevirtual java.io.DataInputStream.readByte ()B (19)
79: putfield ak.g I (14)
82: aload_1
83: invokevirtual java.io.DataInputStream.readShort ()S (20)
86: dup
87: istore_2
88: newarray
90: astore_3
91: aload_1
92: aload_3
93: iconst_0
94: iload_2
95: istore %7
97: istore %6
99: astore %5
101: astore %4
103: iconst_0
104: istore %8
106: aload %4
108: aload %5
110: iload %6
112: iload %7
114: invokevirtual java.io.DataInputStream.readFully ([BII)V (116)
117: istore %8
119: iload %8
121: pop
122: aload_0
123: aload_3
124: iconst_0
125: iload_2
126: invokestatic javax.microedition.lcdui.Image.createImage ([BII)Ljavax/microedition/lcdui/Image; (34)
129: putfield ak.G Ljavax/microedition/lcdui/Image; (10)
132: aload_1
133: invokevirtual java.io.DataInputStream.close ()V (17)
136: return
137: pop
138: return
The original bytecode looks like this:
public void (String arg1)
Code(max_stack = 4, max_locals = 4, code_length = 116)
0: aload_0
1: invokespecial java.lang.Object. ()V (23)
4: new <java.io.DataInputStream> (2)
7: dup
8: aload_0
9: invokevirtual java.lang.Object.getClass ()Ljava/lang/Class; (24)
12: aload_1
13: invokevirtual java.lang.Class.getResourceAsStream (Ljava/lang/String;)Ljava/io/InputStream; (22)
16: invokespecial java.io.DataInputStream. (Ljava/io/InputStream;)V (16)
19: dup
20: astore_1
21: invokevirtual java.io.DataInputStream.readUnsignedByte ()I (21)
24: i2c
25: bipush 70
27: if_icmpeq #31
30: return
31: aload_1
32: invokevirtual java.io.DataInputStream.readUnsignedByte ()I (21)
35: i2c
36: bipush 49
38: if_icmpeq #42
41: return
42: aload_0
43: aload_1
44: invokevirtual java.io.DataInputStream.readByte ()B (19)
47: putfield ak.h I (15)
50: aload_0
51: aload_1
52: invokevirtual java.io.DataInputStream.readByte ()B (19)
55: putfield ak.d I (11)
58: aload_0
59: aload_1
60: invokevirtual java.io.DataInputStream.readByte ()B (19)
63: putfield ak.e I (12)
66: aload_0
67: aload_1
68: invokevirtual java.io.DataInputStream.readByte ()B (19)
71: putfield ak.f I (13)
74: aload_0
75: aload_1
76: invokevirtual java.io.DataInputStream.readByte ()B (19)
79: putfield ak.g I (14)
82: aload_1
83: invokevirtual java.io.DataInputStream.readShort ()S (20)
86: dup
87: istore_2
88: newarray
90: astore_3
91: aload_1
92: aload_3
93: iconst_0
94: iload_2
95: invokevirtual java.io.DataInputStream.read ([BII)I (18)
98: pop
99: aload_0
100: aload_3
101: iconst_0
102: iload_2
103: invokestatic javax.microedition.lcdui.Image.createImage ([BII)Ljavax/microedition/lcdui/Image; (34)
106: putfield ak.G Ljavax/microedition/lcdui/Image; (10)
109: aload_1
110: invokevirtual java.io.DataInputStream.close ()V (17)
113: return
114: pop
115: return