1 /*
2 * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 package gc.stress.gcbasher;
26
27 class Decompiler {
28 private ByteCursor cursor;
29 private ClassInfo ci;
30
31 public Decompiler(Byte[] classData) {
32 cursor = new ByteCursor(classData);
33
34 int magicNumber = cursor.readInt();
35 if (magicNumber != 0xCAFEBABE) {
36 throw new IllegalArgumentException("Bad magic number " + magicNumber);
37 }
38
39 cursor.readUnsignedShort(); // Minor version
40 cursor.readUnsignedShort(); // Major version
41
42 ConstantPoolEntry[] constantPool = decodeConstantPool();
43
44 cursor.readUnsignedShort(); // Access flags
45
46 // this class index in constant pool;
47 int classInfo = cursor.readUnsignedShort();
48 int classInfoNameIndex = constantPool[classInfo].getNameIndex();
49 ci = new ClassInfo(constantPool[classInfoNameIndex].getValue());
50
51 cursor.readUnsignedShort(); // superclass
52
53 int numInterfaces = cursor.readUnsignedShort();
54 for (int i = 0; i < numInterfaces; i++) {
55 cursor.readUnsignedShort(); // interface
56 }
57
58 decodeFields();
59 MethodInfo[] methods = decodeMethods(constantPool);
60 decodeMethodDependencies(methods, constantPool);
61 }
62
63 public ClassInfo getClassInfo() {
64 return ci;
65 }
66
67 private boolean isDependency(String name, String className) {
68 return !name.equals(className) && !name.startsWith("[");
69 }
70
71 private void addDependency(MethodInfo m, String name) {
72 Dependency d = new Dependency(m.name(), m.descriptor(), name);
73 ci.addResolutionDep(d);
74 }
75
76 private String resolveName(ConstantPoolEntry[] constantPool, int cpi) {
77 int nameIndex = constantPool[cpi].getNameIndex();
78 return constantPool[nameIndex].getValue();
79 }
80
81 private void decodeMethodDependencies(MethodInfo[] methods, ConstantPoolEntry[] constantPool) {
82 for (int i = 0; i < methods.length; i++) {
83 MethodInfo m = methods[i];
84 final int stopCheck = m.codeStart() + m.codeLength();
85
86 int byteCodeIndex = m.codeStart();
87 while (byteCodeIndex < stopCheck) {
88 int bc = cursor.readUnsignedByteAt(byteCodeIndex);
89
90 switch (bc) {
91 // These opcodes cause name resolution or initialization
92 // Their index bytes all point to a CONSTANT_Class (4.4.1)
93 case Bytecode.ANEWARRAY:
94 case Bytecode.CHECKCAST:
95 case Bytecode.INSTANCEOF:
96 case Bytecode.MULTIANEWARRAY:
97 case Bytecode.NEW: {
98 int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1);
99 String name = resolveName(constantPool, cpi);
100
101 if (isDependency(name, ci.getName())) {
102 addDependency(m, name);
103 }
104 break;
105 }
106
107 // These opcodes cause name resolution or initialization
108 // Their index bytes all point to a CONSTANT_Field/Methodref (4.4.2)
109 case Bytecode.GETFIELD:
110 case Bytecode.INVOKEINTERFACE:
111 case Bytecode.INVOKESPECIAL:
112 case Bytecode.INVOKEVIRTUAL:
113 case Bytecode.PUTFIELD:
114 case Bytecode.PUTSTATIC:
115 case Bytecode.GETSTATIC:
116 case Bytecode.INVOKESTATIC: {
117 int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1);
118 int classIndex = constantPool[cpi].getClassIndex();
119 String name = resolveName(constantPool, classIndex);
120
121 if (isDependency(name, ci.getName())) {
122 addDependency(m, name);
123 }
124 break;
125 }
126
127 case Bytecode.LOOKUPSWITCH: {
128 byteCodeIndex++;
129 int offset = byteCodeIndex - m.codeStart();
130 while (offset % 4 != 0) {
131 offset++;
132 byteCodeIndex++;
133 }
134
135 cursor.readIntAt(byteCodeIndex); // def
136 byteCodeIndex +=4;
137
138 int npairs = cursor.readIntAt(byteCodeIndex);
139 byteCodeIndex +=4;
140 byteCodeIndex += (8 * npairs);
141 continue;
142 }
143
144 case Bytecode.TABLESWITCH: {
145 byteCodeIndex++;
146 int offset = byteCodeIndex - m.codeStart();
147 while (offset % 4 != 0) {
148 offset++;
149 byteCodeIndex++;
150 }
151
152 cursor.readIntAt(byteCodeIndex); // def
153 byteCodeIndex +=4;
154
155 int low = cursor.readIntAt(byteCodeIndex);
156 byteCodeIndex +=4;
157 int high = cursor.readIntAt(byteCodeIndex);
158 byteCodeIndex +=4;
159 byteCodeIndex += (4 * (high - low + 1));
160 continue;
161 }
162
163 case Bytecode.WIDE: {
164 bc = cursor.readUnsignedByteAt(++byteCodeIndex);
165 if (bc == Bytecode.IINC) {
166 byteCodeIndex += 5;
167 } else {
168 byteCodeIndex += 3;
169 }
170 continue;
171 }
172 }
173
174 byteCodeIndex += Bytecode.getLength(bc);
175 }
176
177 if (byteCodeIndex - stopCheck > 1) {
178 String err = "bad finish for method " + m.name() +
179 "End + " + (byteCodeIndex - stopCheck);
180 throw new IllegalArgumentException(err);
181 }
182 }
183 }
184
185 private MethodInfo[] decodeMethods(ConstantPoolEntry[] constantPool) {
186 MethodInfo[] methods = new MethodInfo[cursor.readUnsignedShort()];
187
188 for (int i = 0; i < methods.length; i++) {
189 cursor.readUnsignedShort(); // access flags
190
191 String name = constantPool[cursor.readUnsignedShort()].getValue();
192 String descriptor = constantPool[cursor.readUnsignedShort()].getValue();
193
194 Integer codeLength = 0;
195 Integer codeStart = 0;
196
197 int numAttributes = cursor.readUnsignedShort(); // attributes count
198 for (int j = 0; j < numAttributes; j++) {
199 int type = cursor.readUnsignedShort(); // attrib nameIndex
200 int aLen = cursor.readInt(); // attrib length
201
202 if (constantPool[type].getValue().equals("Code")) {
203 cursor.readUnsignedShort(); // Max stack
204 cursor.readUnsignedShort(); // Max locals
205
206 codeLength = cursor.readInt();
207 codeStart = cursor.getOffset();
208
209 cursor.skipBytes(codeLength); // Need to skip the code bytes
210 cursor.skipBytes(cursor.readUnsignedShort() * 8); // Skip exception table
211
212 int numSubAttributes = cursor.readUnsignedShort();
213 for (int k = 0; k < numSubAttributes; k++) {
214 cursor.readUnsignedShort(); // sub name
215 cursor.skipBytes(cursor.readInt()); // sub attrib data
216 }
217 } else {
218 cursor.skipBytes(aLen); // unknown attrib data
219 }
220 }
221
222 methods[i] = new MethodInfo(name, descriptor, codeLength, codeStart);
223 }
224
225 return methods;
226 }
227
228 private void decodeFields() {
229 // Looks like we dont need any field info, throw it away!
230 int numFields = cursor.readUnsignedShort();
231
232 for (int i = 0; i < numFields; i++) {
233 cursor.readUnsignedShort(); // access flags
234 cursor.readUnsignedShort(); // nameIndex
235 cursor.readUnsignedShort(); // descriptorIndex
236
237 int numAttributes = cursor.readUnsignedShort();
238 for (int j = 0; j < numAttributes; j++) {
239 cursor.readUnsignedShort(); // nameIndex
240 int length = cursor.readInt();
241 cursor.skipBytes(length); // data
242 }
243 }
244 }
245
246 private ConstantPoolEntry[] decodeConstantPool() {
247 final int CONSTANT_Utf8 = 1;
248 // final int CONSTANT_Unicode = 2;
249 final int CONSTANT_Integer = 3;
250 final int CONSTANT_Float = 4;
251 final int CONSTANT_Long = 5;
252 final int CONSTANT_Double = 6;
253 final int CONSTANT_Class = 7;
254 final int CONSTANT_String = 8;
255 final int CONSTANT_Fieldref = 9;
256 final int CONSTANT_Methodref = 10;
257 final int CONSTANT_InterfaceMethodref = 11;
258 final int CONSTANT_NameAndType = 12;
259 final int CONSTANT_MethodHandle = 15;
260 final int CONSTANT_MethodType = 16;
261 final int CONSTANT_Dynamic = 17;
262 final int CONSTANT_InvokeDynamic = 18;
263
264 ConstantPoolEntry[] constantPool = new ConstantPoolEntry[cursor.readUnsignedShort()];
265
266 // The constant pool starts at index 1
267 for (int i = 1; i < constantPool.length; i++) {
268 int type = cursor.readUnsignedByte();
269
270 switch (type) {
271 case CONSTANT_Class:
272 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index
273 break;
274
275 case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref:
276 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // class_index
277 cursor.readUnsignedShort(); // name_and_type_index
278 break;
279
280 case CONSTANT_String:
281 cursor.readUnsignedShort(); // string_index
282 break;
283
284 case CONSTANT_Integer:
285 cursor.readInt(); // bytes
286 break;
287
288 case CONSTANT_Float:
289 cursor.readInt(); // bytes
290 break;
291
292 case CONSTANT_Long:
293 cursor.readInt(); // high_bytes
294 cursor.readInt(); // low_bytes
295 i++; // 8 byte constants use 2 constant pool slots.
296 break;
297
298 case CONSTANT_Double:
299 cursor.readInt(); // high_bytes
300 cursor.readInt(); // low_bytes
301 i++; // 8 byte constants use 2 constant pool slots.
302 break;
303
304 case CONSTANT_NameAndType:
305 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index
306 cursor.readUnsignedShort(); // descriptor_index
307 break;
308
309 case CONSTANT_Utf8:
310 int length = cursor.readUnsignedShort(); // length
311 constantPool[i] = new ConstantPoolEntry(cursor.readUtf8(length)); // bytes[length]
312 break;
313
314 case CONSTANT_MethodHandle:
315 cursor.readUnsignedByte(); // reference_kind
316 cursor.readUnsignedShort(); // reference_index
317 break;
318
319 case CONSTANT_MethodType:
320 cursor.readUnsignedShort(); // descriptor_index
321 break;
322
323 case CONSTANT_Dynamic:
324 cursor.readUnsignedShort(); // bootstrap_method_attr_index
325 cursor.readUnsignedShort(); // name_and_type_index
326 break;
327
328 case CONSTANT_InvokeDynamic:
329 cursor.readUnsignedShort(); // bootstrap_method_attr_index
330 cursor.readUnsignedShort(); // name_and_type_index
331 break;
332
333 default:
334 String err = "Unknown constant pool type " + String.valueOf(type) + "\n" +
335 "CPE " + i + " of " + constantPool.length + "\n" +
336 "Byte offset " + Integer.toHexString(cursor.getOffset());
337 throw new IllegalArgumentException(err);
338 }
339 }
340 return constantPool;
341 }
342 }