Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/mlvm/tools/Indify.java
41155 views
1
/*
2
* Copyright (c) 2010, 2021, 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
package vm.mlvm.tools;
25
26
import java.util.*;
27
import java.io.*;
28
import java.lang.reflect.Modifier;
29
import java.util.regex.*;
30
31
/**
32
* Transform one or more class files to incorporate JSR 292 features,
33
* such as {@code invokedynamic}.
34
* <p>
35
* This is a standalone program in a single source file.
36
* In this form, it may be useful for test harnesses, small experiments, and javadoc examples.
37
* Copies of this file may show up in multiple locations for standalone usage.
38
* <p>
39
* Static private methods named MH_x and MT_x (where x is arbitrary)
40
* must be stereotyped generators of MethodHandle and MethodType
41
* constants. All calls to them are transformed to {@code CONSTANT_MethodHandle}
42
* and {@code CONSTANT_MethodType} "ldc" instructions.
43
* The stereotyped code must create method types by calls to {@code methodType} or
44
* {@code fromMethodDescriptorString}. The "lookup" argument must be created
45
* by calls to {@code java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
46
* The class and string arguments must be constant.
47
* The following methods of {@code java.lang.invoke.MethodHandle.Lookup Lookup} are
48
* allowed for method handle creation: {@code findStatic}, {@code findVirtual},
49
* {@code findConstructor}, {@code findSpecial},
50
* {@code findGetter}, {@code findSetter},
51
* {@code findStaticGetter}, or {@code findStaticSetter}.
52
* The call to one of these methods must be followed immediately
53
* by an {@code areturn} instruction.
54
* The net result of the call to the MH_x or MT_x method must be
55
* the creation of a constant method handle. Thus, replacing calls
56
* to MH_x or MT_x methods by {@code ldc} instructions should leave
57
* the meaning of the program unchanged.
58
* <p>
59
* Static private methods named INDY_x must be stereotyped generators
60
* of {@code invokedynamic} call sites.
61
* All calls to them must be immediately followed by
62
* {@code invokeExact} calls.
63
* All such pairs of calls are transformed to {@code invokedynamic}
64
* instructions. Each INDY_x method must begin with a call to a
65
* MH_x method, which is taken to be its bootstrap method.
66
* The method must be immediately invoked (via {@code invokeGeneric}
67
* on constant lookup, name, and type arguments. An object array of
68
* constants may also be appended to the {@code invokeGeneric call}.
69
* This call must be cast to {@code CallSite}, and the result must be
70
* immediately followed by a call to {@code dynamicInvoker}, with the
71
* resulting method handle returned.
72
* <p>
73
* The net result of all of these actions is equivalent to the JVM's
74
* execution of an {@code invokedynamic} instruction in the unlinked state.
75
* Running this code once should produce the same results as running
76
* the corresponding {@code invokedynamic} instruction.
77
* In order to model the caching behavior, the code of an INDY_x
78
* method is allowed to begin with getstatic, aaload, and if_acmpne
79
* instructions which load a static method handle value and return it
80
* if the value is non-null.
81
* <p>
82
* Example usage:
83
* <blockquote><pre>
84
$ JAVA_HOME=(some recent OpenJDK 7 build)
85
$ ant
86
$ $JAVA_HOME/bin/java -cp build/classes indify.Indify --overwrite --dest build/testout build/classes/indify/Example.class
87
$ $JAVA_HOME/bin/java -cp build/classes indify.Example
88
MT = (java.lang.Object)java.lang.Object
89
MH = adder(int,int)java.lang.Integer
90
adder(1,2) = 3
91
calling indy: 42
92
$ $JAVA_HOME/bin/java -cp build/testout indify.Example
93
(same output as above)
94
* </pre></blockquote>
95
* <p>
96
* Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized,
97
* the {@code --transitionalJSR292} switch is recommended (and turned on by default).
98
* <p>
99
* A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome.
100
*/
101
@SuppressWarnings("unchecked")
102
public class Indify {
103
public static void main(String... av) throws IOException {
104
new Indify().run(av);
105
}
106
107
public File dest;
108
public String[] classpath = {"."};
109
public boolean keepgoing = false;
110
public boolean expandProperties = false;
111
public boolean overwrite = false;
112
public boolean quiet = true;
113
public boolean verbose = false;
114
public boolean transitionalJSR292 = true; // default to false later
115
public boolean all = false;
116
public int verifySpecifierCount = -1;
117
118
public void run(String... av) throws IOException {
119
List<String> avl = new ArrayList<>(Arrays.asList(av));
120
parseOptions(avl);
121
if (avl.isEmpty())
122
throw new IllegalArgumentException("Usage: indify [--dest dir] [option...] file...");
123
if ("--java".equals(avl.get(0))) {
124
avl.remove(0);
125
try {
126
runApplication(avl.toArray(new String[0]));
127
} catch (Exception ex) {
128
if (ex instanceof RuntimeException) throw (RuntimeException) ex;
129
throw new RuntimeException(ex);
130
}
131
return;
132
}
133
Exception err = null;
134
for (String a : avl) {
135
try {
136
indify(a);
137
} catch (Exception ex) {
138
if (err == null) err = ex;
139
System.err.println("failure on "+a);
140
if (!keepgoing) break;
141
}
142
}
143
if (err != null) {
144
if (err instanceof IOException) throw (IOException) err;
145
throw (RuntimeException) err;
146
}
147
}
148
149
/** Execute the given application under a class loader which indifies all application classes. */
150
public void runApplication(String... av) throws Exception {
151
List<String> avl = new ArrayList<>(Arrays.asList(av));
152
String mainClassName = avl.remove(0);
153
av = avl.toArray(new String[0]);
154
Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader());
155
java.lang.reflect.Method main = mainClass.getMethod("main", String[].class);
156
main.invoke(null, (Object) av);
157
}
158
159
public void parseOptions(List<String> av) throws IOException {
160
for (; !av.isEmpty(); av.remove(0)) {
161
String a = av.get(0);
162
if (a.startsWith("-")) {
163
String a2 = null;
164
int eq = a.indexOf('=');
165
if (eq > 0) {
166
a2 = maybeExpandProperties(a.substring(eq+1));
167
a = a.substring(0, eq+1);
168
}
169
switch (a) {
170
case "--java":
171
return; // keep this argument
172
case "-d": case "--dest": case "-d=": case "--dest=":
173
dest = new File(a2 != null ? a2 : maybeExpandProperties(av.remove(1)));
174
break;
175
case "-cp": case "--classpath":
176
classpath = maybeExpandProperties(av.remove(1)).split("["+File.pathSeparatorChar+"]");
177
break;
178
case "-k": case "--keepgoing": case "--keepgoing=":
179
keepgoing = booleanOption(a2); // print errors but keep going
180
break;
181
case "--expand-properties": case "--expand-properties=":
182
expandProperties = booleanOption(a2); // expand property references in subsequent arguments
183
break;
184
case "--verify-specifier-count": case "--verify-specifier-count=":
185
verifySpecifierCount = Integer.valueOf(a2);
186
break;
187
case "--overwrite": case "--overwrite=":
188
overwrite = booleanOption(a2); // overwrite output files
189
break;
190
case "--all": case "--all=":
191
all = booleanOption(a2); // copy all classes, even if no patterns
192
break;
193
case "-q": case "--quiet": case "--quiet=":
194
quiet = booleanOption(a2); // less output
195
break;
196
case "-v": case "--verbose": case "--verbose=":
197
verbose = booleanOption(a2); // more output
198
break;
199
case "--transitionalJSR292": case "--transitionalJSR292=":
200
transitionalJSR292 = booleanOption(a2); // use older invokedynamic format
201
break;
202
default:
203
throw new IllegalArgumentException("unrecognized flag: "+a);
204
}
205
continue;
206
} else {
207
break;
208
}
209
}
210
if (dest == null && !overwrite)
211
throw new RuntimeException("no output specified; need --dest d or --overwrite");
212
if (expandProperties) {
213
for (int i = 0; i < av.size(); i++)
214
av.set(i, maybeExpandProperties(av.get(i)));
215
}
216
}
217
218
private boolean booleanOption(String s) {
219
if (s == null) return true;
220
switch (s) {
221
case "true": case "yes": case "1": return true;
222
case "false": case "no": case "0": return false;
223
}
224
throw new IllegalArgumentException("unrecognized boolean flag="+s);
225
}
226
227
private String maybeExpandProperties(String s) {
228
if (!expandProperties) return s;
229
Set<String> propsDone = new HashSet<>();
230
while (s.contains("${")) {
231
int lbrk = s.indexOf("${");
232
int rbrk = s.indexOf('}', lbrk);
233
if (rbrk < 0) break;
234
String prop = s.substring(lbrk+2, rbrk);
235
if (!propsDone.add(prop)) break;
236
String value = System.getProperty(prop);
237
if (verbose) System.err.println("expanding ${"+prop+"} => "+value);
238
if (value == null) break;
239
s = s.substring(0, lbrk) + value + s.substring(rbrk+1);
240
}
241
return s;
242
}
243
244
public void indify(String a) throws IOException {
245
File f = new File(a);
246
String fn = f.getName();
247
if (fn.endsWith(".class") && f.isFile())
248
indifyFile(f, dest);
249
else if (fn.endsWith(".jar") && f.isFile())
250
indifyJar(f, dest);
251
else if (f.isDirectory())
252
indifyTree(f, dest);
253
else if (!keepgoing)
254
throw new RuntimeException("unrecognized file: "+a);
255
}
256
257
private void ensureDirectory(File dir) {
258
if (dir.mkdirs() && !quiet)
259
System.err.println("created "+dir);
260
}
261
262
public void indifyFile(File f, File dest) throws IOException {
263
if (verbose) System.err.println("reading "+f);
264
ClassFile cf = new ClassFile(f);
265
Logic logic = new Logic(cf);
266
boolean changed = logic.transform();
267
logic.reportPatternMethods(quiet, keepgoing);
268
if (changed || all) {
269
File outfile;
270
if (dest != null) {
271
ensureDirectory(dest);
272
outfile = classPathFile(dest, cf.nameString());
273
} else {
274
outfile = f; // overwrite input file, no matter where it is
275
}
276
cf.writeTo(outfile);
277
if (!quiet) System.err.println("wrote "+outfile);
278
}
279
}
280
281
File classPathFile(File pathDir, String className) {
282
String qualname = className+".class";
283
qualname = qualname.replace('/', File.separatorChar);
284
return new File(pathDir, qualname);
285
}
286
287
public void indifyJar(File f, Object dest) throws IOException {
288
throw new UnsupportedOperationException("Not yet implemented");
289
}
290
291
public void indifyTree(File f, File dest) throws IOException {
292
if (verbose) System.err.println("reading directory: "+f);
293
for (File f2 : f.listFiles(new FilenameFilter() {
294
public boolean accept(File dir, String name) {
295
if (name.endsWith(".class")) return true;
296
if (name.contains(".")) return false;
297
// return true if it might be a package name:
298
return Character.isJavaIdentifierStart(name.charAt(0));
299
}})) {
300
if (f2.getName().endsWith(".class"))
301
indifyFile(f2, dest);
302
else if (f2.isDirectory())
303
indifyTree(f2, dest);
304
}
305
}
306
307
public ClassLoader makeClassLoader() {
308
return new Loader();
309
}
310
private class Loader extends ClassLoader {
311
Loader() {
312
this(Indify.class.getClassLoader());
313
}
314
Loader(ClassLoader parent) {
315
super(parent);
316
}
317
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
318
File f = findClassInPath(name);
319
if (f != null) {
320
try {
321
Class<?> c = transformAndLoadClass(f);
322
if (c != null) {
323
if (resolve) resolveClass(c);
324
return c;
325
}
326
} catch (Exception ex) {
327
if (ex instanceof IllegalArgumentException)
328
// pass error from reportPatternMethods
329
throw (IllegalArgumentException) ex;
330
}
331
}
332
return super.loadClass(name, resolve);
333
}
334
private File findClassInPath(String name) {
335
for (String s : classpath) {
336
File f = classPathFile(new File(s), name);
337
if (f.exists() && f.canRead()) {
338
return f;
339
}
340
}
341
return null;
342
}
343
protected Class<?> findClass(String name) throws ClassNotFoundException {
344
try {
345
return transformAndLoadClass(findClassInPath(name));
346
} catch (IOException ex) {
347
throw new ClassNotFoundException("IO error", ex);
348
}
349
}
350
private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException {
351
if (verbose) System.out.println("Loading class from "+f);
352
ClassFile cf = new ClassFile(f);
353
Logic logic = new Logic(cf);
354
boolean changed = logic.transform();
355
if (verbose && !changed) System.out.println("(no change)");
356
logic.reportPatternMethods(!verbose, keepgoing);
357
byte[] bytes = cf.toByteArray();
358
return defineClass(null, bytes, 0, bytes.length);
359
}
360
}
361
362
private class Logic {
363
// Indify logic, per se.
364
ClassFile cf;
365
final char[] poolMarks;
366
final Map<Method,Constant> constants = new HashMap<>();
367
final Map<Method,String> indySignatures = new HashMap<>();
368
Logic(ClassFile cf) {
369
this.cf = cf;
370
poolMarks = new char[cf.pool.size()];
371
}
372
boolean transform() {
373
if (!initializeMarks()) return false;
374
if (!findPatternMethods()) return false;
375
Pool pool = cf.pool;
376
//for (Constant c : cp) System.out.println(" # "+c);
377
for (Method m : cf.methods) {
378
if (constants.containsKey(m)) continue; // don't bother
379
// Transform references.
380
int blab = 0;
381
for (Instruction i = m.instructions(); i != null; i = i.next()) {
382
if (i.bc != opc_invokestatic) continue;
383
int methi = i.u2At(1);
384
if (poolMarks[methi] == 0) continue;
385
Short[] ref = pool.getMemberRef((short)methi);
386
Method conm = findMember(cf.methods, ref[1], ref[2]);
387
if (conm == null) continue;
388
Constant con = constants.get(conm);
389
if (con == null) continue;
390
if (blab++ == 0 && !quiet)
391
System.err.println("patching "+cf.nameString()+"."+m);
392
//if (blab == 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); }
393
if (con.tag == CONSTANT_InvokeDynamic ||
394
con.tag == CONSTANT_InvokeDynamic_17) {
395
// need to patch the following instruction too,
396
// but there are usually intervening argument pushes too
397
Instruction i2 = findPop(i);
398
Short[] ref2 = null;
399
short ref2i = 0;
400
if (i2 != null && i2.bc == opc_invokevirtual &&
401
poolMarks[(char)(ref2i = (short) i2.u2At(1))] == 'D')
402
ref2 = pool.getMemberRef(ref2i);
403
if (ref2 == null || !"invokeExact".equals(pool.getString(ref2[1]))) {
404
System.err.println(m+": failed to create invokedynamic at "+i.pc);
405
continue;
406
}
407
String invType = pool.getString(ref2[2]);
408
String bsmType = indySignatures.get(conm);
409
if (!invType.equals(bsmType)) {
410
System.err.println(m+": warning: "+conm+" call type and local invoke type differ: "
411
+bsmType+", "+invType);
412
}
413
assert(i.len == 3 || i2.len == 3);
414
if (!quiet) System.err.println(i+" "+conm+";...; "+i2+" => invokedynamic "+con);
415
int start = i.pc + 3, end = i2.pc;
416
System.arraycopy(i.codeBase, start, i.codeBase, i.pc, end-start);
417
i.forceNext(0); // force revisit of new instruction
418
i2.u1AtPut(-3, opc_invokedynamic);
419
i2.u2AtPut(-2, con.index);
420
i2.u2AtPut(0, (short)0);
421
i2.u1AtPut(2, opc_nop);
422
//System.out.println(new Instruction(i.codeBase, i2.pc-3));
423
} else {
424
if (!quiet) System.err.println(i+" "+conm+" => ldc "+con);
425
assert(i.len == 3);
426
i.u1AtPut(0, opc_ldc_w);
427
i.u2AtPut(1, con.index);
428
}
429
}
430
//if (blab >= 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); }
431
}
432
cf.methods.removeAll(constants.keySet());
433
return true;
434
}
435
436
// Scan forward from the instruction to find where the stack p
437
// below the current sp at the instruction.
438
Instruction findPop(Instruction i) {
439
//System.out.println("findPop from "+i);
440
Pool pool = cf.pool;
441
JVMState jvm = new JVMState();
442
decode:
443
for (i = i.clone().next(); i != null; i = i.next()) {
444
String pops = INSTRUCTION_POPS[i.bc];
445
//System.out.println(" "+i+" "+jvm.stack+" : "+pops.replace("$", " => "));
446
if (pops == null) break;
447
if (jvm.stackMotion(i.bc)) continue decode;
448
if (pops.indexOf('Q') >= 0) {
449
Short[] ref = pool.getMemberRef((short) i.u2At(1));
450
String type = simplifyType(pool.getString(CONSTANT_Utf8, ref[2]));
451
switch (i.bc) {
452
case opc_getstatic:
453
case opc_getfield:
454
case opc_putstatic:
455
case opc_putfield:
456
pops = pops.replace("Q", type);
457
break;
458
default:
459
if (!type.startsWith("("))
460
throw new InternalError(i.toString());
461
pops = pops.replace("Q$Q", type.substring(1).replace(")","$"));
462
break;
463
}
464
//System.out.println("special type: "+type+" => "+pops);
465
}
466
int npops = pops.indexOf('$');
467
if (npops < 0) throw new InternalError();
468
if (npops > jvm.sp()) return i;
469
List<Object> args = jvm.args(npops);
470
int k = 0;
471
for (Object x : args) {
472
char have = (Character) x;
473
char want = pops.charAt(k++);
474
if (have == 'X' || want == 'X') continue;
475
if (have != want) break decode;
476
}
477
if (pops.charAt(k++) != '$') break decode;
478
args.clear();
479
while (k < pops.length())
480
args.add(pops.charAt(k++));
481
}
482
System.err.println("*** bailout on jvm: "+jvm.stack+" "+i);
483
return null;
484
}
485
486
boolean findPatternMethods() {
487
boolean found = false;
488
for (char mark : "THI".toCharArray()) {
489
for (Method m : cf.methods) {
490
if (!Modifier.isPrivate(m.access)) continue;
491
if (!Modifier.isStatic(m.access)) continue;
492
if (nameAndTypeMark(m.name, m.type) == mark) {
493
Constant con = scanPattern(m, mark);
494
if (con == null) continue;
495
constants.put(m, con);
496
found = true;
497
}
498
}
499
}
500
return found;
501
}
502
503
void reportPatternMethods(boolean quietly, boolean allowMatchFailure) {
504
if (!quietly && !constants.keySet().isEmpty())
505
System.err.println("pattern methods removed: "+constants.keySet());
506
for (Method m : cf.methods) {
507
if (nameMark(cf.pool.getString(m.name)) != 0 &&
508
constants.get(m) == null) {
509
String failure = "method has special name but fails to match pattern: "+m;
510
if (!allowMatchFailure)
511
throw new IllegalArgumentException(failure);
512
else if (!quietly)
513
System.err.println("warning: "+failure);
514
}
515
}
516
if (verifySpecifierCount >= 0) {
517
List<Object[]> specs = bootstrapMethodSpecifiers(false);
518
int specsLen = (specs == null ? 0 : specs.size());
519
if (specsLen != verifySpecifierCount) {
520
throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
521
}
522
}
523
}
524
525
// mark constant pool entries according to participation in patterns
526
boolean initializeMarks() {
527
boolean changed = false;
528
for (;;) {
529
boolean changed1 = false;
530
int cpindex = -1;
531
for (Constant e : cf.pool) {
532
++cpindex;
533
if (e == null) continue;
534
char mark = poolMarks[cpindex];
535
if (mark != 0) continue;
536
switch (e.tag) {
537
case CONSTANT_Utf8:
538
mark = nameMark(e.itemString()); break;
539
case CONSTANT_NameAndType:
540
mark = nameAndTypeMark(e.itemIndexes()); break;
541
case CONSTANT_Class: {
542
int n1 = e.itemIndex();
543
char nmark = poolMarks[(char)n1];
544
if ("DJ".indexOf(nmark) >= 0)
545
mark = nmark;
546
break;
547
}
548
case CONSTANT_Field:
549
case CONSTANT_Method: {
550
Short[] n12 = e.itemIndexes();
551
short cl = n12[0];
552
short nt = n12[1];
553
char cmark = poolMarks[(char)cl];
554
if (cmark != 0) {
555
mark = cmark; // it is a java.lang.invoke.* or java.lang.* method
556
break;
557
}
558
String cls = cf.pool.getString(CONSTANT_Class, cl);
559
if (cls.equals(cf.nameString())) {
560
switch (poolMarks[(char)nt]) {
561
// it is a private MH/MT/INDY method
562
case 'T': case 'H': case 'I':
563
mark = poolMarks[(char)nt];
564
break;
565
}
566
}
567
break;
568
}
569
default: break;
570
}
571
if (mark != 0) {
572
poolMarks[cpindex] = mark;
573
changed1 = true;
574
}
575
}
576
if (!changed1)
577
break;
578
changed = true;
579
}
580
return changed;
581
}
582
char nameMark(String s) {
583
if (s.startsWith("MT_")) return 'T';
584
else if (s.startsWith("MH_")) return 'H';
585
else if (s.startsWith("INDY_")) return 'I';
586
else if (s.startsWith("java/lang/invoke/")) return 'D';
587
else if (s.startsWith("java/lang/")) return 'J';
588
return 0;
589
}
590
char nameAndTypeMark(Short[] n12) {
591
return nameAndTypeMark(n12[0], n12[1]);
592
}
593
char nameAndTypeMark(short n1, short n2) {
594
char mark = poolMarks[(char)n1];
595
if (mark == 0) return 0;
596
String descr = cf.pool.getString(CONSTANT_Utf8, n2);
597
String requiredType;
598
switch (poolMarks[(char)n1]) {
599
case 'H': requiredType = "()Ljava/lang/invoke/MethodHandle;"; break;
600
case 'T': requiredType = "()Ljava/lang/invoke/MethodType;"; break;
601
case 'I': requiredType = "()Ljava/lang/invoke/MethodHandle;"; break;
602
default: return 0;
603
}
604
if (descr.equals(requiredType)) return mark;
605
return 0;
606
}
607
608
private class JVMState {
609
final List<Object> stack = new ArrayList<>();
610
int sp() { return stack.size(); }
611
void push(Object x) { stack.add(x); }
612
void push2(Object x) { stack.add(EMPTY_SLOT); stack.add(x); }
613
void pushAt(int pos, Object x) { stack.add(stack.size()+pos, x); }
614
Object pop() { return stack.remove(sp()-1); }
615
Object top() { return stack.get(sp()-1); }
616
List<Object> args(boolean hasRecv, String type) {
617
return args(argsize(type) + (hasRecv ? 1 : 0));
618
}
619
List<Object> args(int argsize) {
620
return stack.subList(sp()-argsize, sp());
621
}
622
boolean stackMotion(int bc) {
623
switch (bc) {
624
case opc_pop: pop(); break;
625
case opc_pop2: pop(); pop(); break;
626
case opc_swap: pushAt(-1, pop()); break;
627
case opc_dup: push(top()); break;
628
case opc_dup_x1: pushAt(-2, top()); break;
629
case opc_dup_x2: pushAt(-3, top()); break;
630
// ? also: dup2{,_x1,_x2}
631
default: return false;
632
}
633
return true;
634
}
635
}
636
private final String EMPTY_SLOT = "_";
637
private void removeEmptyJVMSlots(List<Object> args) {
638
for (;;) {
639
int i = args.indexOf(EMPTY_SLOT);
640
if (i >= 0 && i+1 < args.size()
641
&& (isConstant(args.get(i+1), CONSTANT_Long) ||
642
isConstant(args.get(i+1), CONSTANT_Double)))
643
args.remove(i);
644
else break;
645
}
646
}
647
648
private Constant scanPattern(Method m, char patternMark) {
649
if (verbose) System.err.println("scan "+m+" for pattern="+patternMark);
650
int wantTag;
651
switch (patternMark) {
652
case 'T': wantTag = CONSTANT_MethodType; break;
653
case 'H': wantTag = CONSTANT_MethodHandle; break;
654
case 'I': wantTag = CONSTANT_InvokeDynamic; break;
655
default: throw new InternalError();
656
}
657
Instruction i = m.instructions();
658
JVMState jvm = new JVMState();
659
Pool pool = cf.pool;
660
int branchCount = 0;
661
Object arg;
662
List<Object> args;
663
List<Object> bsmArgs = null; // args to invokeGeneric
664
decode:
665
for (; i != null; i = i.next()) {
666
//System.out.println(jvm.stack+" "+i);
667
int bc = i.bc;
668
switch (bc) {
669
case opc_ldc: jvm.push(pool.get(i.u1At(1))); break;
670
case opc_ldc_w: jvm.push(pool.get(i.u2At(1))); break;
671
case opc_ldc2_w: jvm.push2(pool.get(i.u2At(1))); break;
672
case opc_aconst_null: jvm.push(null); break;
673
case opc_bipush: jvm.push((int)(byte) i.u1At(1)); break;
674
case opc_sipush: jvm.push((int)(short)i.u2At(1)); break;
675
676
// these support creation of a restarg array
677
case opc_anewarray:
678
arg = jvm.pop();
679
if (!(arg instanceof Integer)) break decode;
680
arg = Arrays.asList(new Object[(Integer)arg]);
681
jvm.push(arg);
682
break;
683
case opc_dup:
684
jvm.push(jvm.top()); break;
685
case opc_aastore:
686
args = jvm.args(3); // array, index, value
687
if (args.get(0) instanceof List &&
688
args.get(1) instanceof Integer) {
689
((List<Object>)args.get(0)).set( (Integer)args.get(1), args.get(2) );
690
}
691
args.clear();
692
break;
693
694
case opc_getstatic:
695
{
696
// int.class compiles to getstatic Integer.TYPE
697
int fieldi = i.u2At(1);
698
char mark = poolMarks[fieldi];
699
//System.err.println("getstatic "+fieldi+Arrays.asList(pool.getStrings(pool.getMemberRef((short)fieldi)))+mark);
700
if (mark == 'J') {
701
Short[] ref = pool.getMemberRef((short) fieldi);
702
String name = pool.getString(CONSTANT_Utf8, ref[1]);
703
if ("TYPE".equals(name)) {
704
String wrapperName = pool.getString(CONSTANT_Class, ref[0]).replace('/', '.');
705
// a primitive type descriptor
706
Class<?> primClass;
707
try {
708
primClass = (Class<?>) Class.forName(wrapperName).getField(name).get(null);
709
} catch (Exception ex) {
710
throw new InternalError("cannot load "+wrapperName+"."+name);
711
}
712
jvm.push(primClass);
713
break;
714
}
715
}
716
// unknown field; keep going...
717
jvm.push(UNKNOWN_CON);
718
break;
719
}
720
case opc_putstatic:
721
{
722
if (patternMark != 'I') break decode;
723
jvm.pop();
724
// unknown field; keep going...
725
break;
726
}
727
728
case opc_invokestatic:
729
case opc_invokevirtual:
730
{
731
boolean hasRecv = (bc == opc_invokevirtual);
732
int methi = i.u2At(1);
733
char mark = poolMarks[methi];
734
Short[] ref = pool.getMemberRef((short)methi);
735
String type = pool.getString(CONSTANT_Utf8, ref[2]);
736
//System.out.println("invoke "+pool.getString(CONSTANT_Utf8, ref[1])+" "+Arrays.asList(ref)+" : "+type);
737
args = jvm.args(hasRecv, type);
738
String intrinsic = null;
739
Constant con;
740
if (mark == 'D' || mark == 'J') {
741
intrinsic = pool.getString(CONSTANT_Utf8, ref[1]);
742
if (mark == 'J') {
743
String cls = pool.getString(CONSTANT_Class, ref[0]);
744
cls = cls.substring(1+cls.lastIndexOf('/'));
745
intrinsic = cls+"."+intrinsic;
746
}
747
//System.out.println("recognized intrinsic "+intrinsic);
748
byte refKind = -1;
749
switch (intrinsic) {
750
case "findGetter": refKind = REF_getField; break;
751
case "findStaticGetter": refKind = REF_getStatic; break;
752
case "findSetter": refKind = REF_putField; break;
753
case "findStaticSetter": refKind = REF_putStatic; break;
754
case "findVirtual": refKind = REF_invokeVirtual; break;
755
case "findStatic": refKind = REF_invokeStatic; break;
756
case "findSpecial": refKind = REF_invokeSpecial; break;
757
case "findConstructor": refKind = REF_newInvokeSpecial; break;
758
}
759
if (refKind >= 0 && (con = parseMemberLookup(refKind, args)) != null) {
760
args.clear(); args.add(con);
761
continue;
762
}
763
}
764
Method ownMethod = null;
765
if (mark == 'T' || mark == 'H' || mark == 'I') {
766
ownMethod = findMember(cf.methods, ref[1], ref[2]);
767
}
768
switch (intrinsic == null ? "" : intrinsic) {
769
case "fromMethodDescriptorString":
770
con = makeMethodTypeCon(args.get(0));
771
args.clear(); args.add(con);
772
continue;
773
case "methodType": {
774
flattenVarargs(args); // there are several overloadings, some with varargs
775
StringBuilder buf = new StringBuilder();
776
String rtype = null;
777
for (Object typeArg : args) {
778
if (typeArg instanceof Class) {
779
Class<?> argClass = (Class<?>) typeArg;
780
if (argClass.isPrimitive()) {
781
char tchar;
782
switch (argClass.getName()) {
783
case "void": tchar = 'V'; break;
784
case "boolean": tchar = 'Z'; break;
785
case "byte": tchar = 'B'; break;
786
case "char": tchar = 'C'; break;
787
case "short": tchar = 'S'; break;
788
case "int": tchar = 'I'; break;
789
case "long": tchar = 'J'; break;
790
case "float": tchar = 'F'; break;
791
case "double": tchar = 'D'; break;
792
default: throw new InternalError(argClass.toString());
793
}
794
buf.append(tchar);
795
} else {
796
// should not happen, but...
797
buf.append('L').append(argClass.getName().replace('.','/')).append(';');
798
}
799
} else if (typeArg instanceof Constant) {
800
Constant argCon = (Constant) typeArg;
801
if (argCon.tag == CONSTANT_Class) {
802
String cn = pool.get(argCon.itemIndex()).itemString();
803
if (cn.endsWith(";"))
804
buf.append(cn);
805
else
806
buf.append('L').append(cn).append(';');
807
} else {
808
break decode;
809
}
810
} else {
811
break decode;
812
}
813
if (rtype == null) {
814
// first arg is treated differently
815
rtype = buf.toString();
816
buf.setLength(0);
817
buf.append('(');
818
}
819
}
820
buf.append(')').append(rtype);
821
con = con = makeMethodTypeCon(buf.toString());
822
args.clear(); args.add(con);
823
continue;
824
}
825
case "lookup":
826
case "dynamicInvoker":
827
args.clear(); args.add(intrinsic);
828
continue;
829
case "lookupClass":
830
if (args.equals(Arrays.asList("lookup"))) {
831
// fold lookup().lookupClass() to the enclosing class
832
args.clear(); args.add(pool.get(cf.thisc));
833
continue;
834
}
835
break;
836
case "invokeGeneric":
837
case "invokeWithArguments":
838
if (patternMark != 'I') break decode;
839
if ("invokeWithArguments".equals(intrinsic))
840
flattenVarargs(args);
841
bsmArgs = new ArrayList(args);
842
args.clear(); args.add("invokeGeneric");
843
continue;
844
case "Integer.valueOf":
845
case "Float.valueOf":
846
case "Long.valueOf":
847
case "Double.valueOf":
848
removeEmptyJVMSlots(args);
849
if (args.size() == 1) {
850
arg = args.remove(0);
851
assert(3456 == (CONSTANT_Integer*1000 + CONSTANT_Float*100 + CONSTANT_Long*10 + CONSTANT_Double));
852
if (isConstant(arg, CONSTANT_Integer + "IFLD".indexOf(intrinsic.charAt(0)))
853
|| arg instanceof Number) {
854
args.add(arg); continue;
855
}
856
}
857
break decode;
858
}
859
if (!hasRecv && ownMethod != null && patternMark != 0) {
860
con = constants.get(ownMethod);
861
if (con == null) break decode;
862
args.clear(); args.add(con);
863
continue;
864
} else if (type.endsWith(")V")) {
865
// allow calls like println("reached the pattern method")
866
args.clear();
867
continue;
868
}
869
break decode; // bail out for most calls
870
}
871
case opc_areturn:
872
{
873
++branchCount;
874
if (bsmArgs != null) {
875
// parse bsmArgs as (MH, lookup, String, MT, [extra])
876
Constant indyCon = makeInvokeDynamicCon(bsmArgs);
877
if (indyCon != null) {
878
Constant typeCon = (Constant) bsmArgs.get(3);
879
indySignatures.put(m, pool.getString(typeCon.itemIndex()));
880
return indyCon;
881
}
882
System.err.println(m+": inscrutable bsm arguments: "+bsmArgs);
883
break decode; // bail out
884
}
885
arg = jvm.pop();
886
if (branchCount == 2 && UNKNOWN_CON.equals(arg))
887
break; // merge to next path
888
if (isConstant(arg, wantTag))
889
return (Constant) arg;
890
break decode; // bail out
891
}
892
default:
893
if (jvm.stackMotion(i.bc)) break;
894
if (bc >= opc_nconst_MIN && bc <= opc_nconst_MAX)
895
{ jvm.push(INSTRUCTION_CONSTANTS[bc - opc_nconst_MIN]); break; }
896
if (patternMark == 'I') {
897
// these support caching paths in INDY_x methods
898
if (bc == opc_aload || bc >= opc_aload_0 && bc <= opc_aload_MAX)
899
{ jvm.push(UNKNOWN_CON); break; }
900
if (bc == opc_astore || bc >= opc_astore_0 && bc <= opc_astore_MAX)
901
{ jvm.pop(); break; }
902
switch (bc) {
903
case opc_getfield:
904
case opc_aaload:
905
jvm.push(UNKNOWN_CON); break;
906
case opc_ifnull:
907
case opc_ifnonnull:
908
// ignore branch target
909
if (++branchCount != 1) break decode;
910
jvm.pop();
911
break;
912
case opc_checkcast:
913
arg = jvm.top();
914
if ("invokeWithArguments".equals(arg) ||
915
"invokeGeneric".equals(arg))
916
break; // assume it is a helpful cast
917
break decode;
918
default:
919
break decode; // bail out
920
}
921
continue decode; // go to next instruction
922
}
923
break decode; // bail out
924
} //end switch
925
}
926
System.err.println(m+": bailout on "+i+" jvm stack: "+jvm.stack);
927
return null;
928
}
929
private final String UNKNOWN_CON = "<unknown>";
930
931
private void flattenVarargs(List<Object> args) {
932
int size = args.size();
933
if (size > 0 && args.get(size-1) instanceof List)
934
args.addAll((List<Object>) args.remove(size-1));
935
}
936
937
private boolean isConstant(Object x, int tag) {
938
return x instanceof Constant && ((Constant)x).tag == tag;
939
}
940
private Constant makeMethodTypeCon(Object x) {
941
short utfIndex;
942
if (x instanceof String)
943
utfIndex = (short) cf.pool.addConstant(CONSTANT_Utf8, x).index;
944
else if (isConstant(x, CONSTANT_String))
945
utfIndex = ((Constant)x).itemIndex();
946
else return null;
947
return cf.pool.addConstant(CONSTANT_MethodType, utfIndex);
948
}
949
private Constant parseMemberLookup(byte refKind, List<Object> args) {
950
// E.g.: lookup().findStatic(Foo.class, "name", MethodType)
951
if (args.size() != 4) return null;
952
int argi = 0;
953
if (!"lookup".equals(args.get(argi++))) return null;
954
short refindex, cindex, ntindex, nindex, tindex;
955
Object con;
956
if (!isConstant(con = args.get(argi++), CONSTANT_Class)) return null;
957
cindex = (short)((Constant)con).index;
958
if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null;
959
nindex = ((Constant)con).itemIndex();
960
if (isConstant(con = args.get(argi++), CONSTANT_MethodType) ||
961
isConstant(con, CONSTANT_Class)) {
962
tindex = ((Constant)con).itemIndex();
963
} else return null;
964
ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType,
965
new Short[]{ nindex, tindex }).index;
966
byte reftag = CONSTANT_Method;
967
if (refKind <= REF_putStatic)
968
reftag = CONSTANT_Field;
969
else if (refKind == REF_invokeInterface)
970
reftag = CONSTANT_InterfaceMethod;
971
Constant ref = cf.pool.addConstant(reftag, new Short[]{ cindex, ntindex });
972
return cf.pool.addConstant(CONSTANT_MethodHandle, new Object[]{ refKind, (short)ref.index });
973
}
974
private Constant makeInvokeDynamicCon(List<Object> args) {
975
// E.g.: MH_bsm.invokeGeneric(lookup(), "name", MethodType, "extraArg")
976
removeEmptyJVMSlots(args);
977
if (args.size() != 4 && args.size() != 5) return null;
978
int argi = 0;
979
short nindex, tindex, ntindex, bsmindex;
980
Object con;
981
if (!isConstant(con = args.get(argi++), CONSTANT_MethodHandle)) return null;
982
bsmindex = (short) ((Constant)con).index;
983
if (!"lookup".equals(args.get(argi++))) return null;
984
if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null;
985
nindex = ((Constant)con).itemIndex();
986
if (!isConstant(con = args.get(argi++), CONSTANT_MethodType)) return null;
987
tindex = ((Constant)con).itemIndex();
988
ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType,
989
new Short[]{ nindex, tindex }).index;
990
if (transitionalJSR292) {
991
if (argi != args.size()) {
992
System.err.println("BSM specifier has extra arguments but transitionalJSR292=1");
993
return null;
994
}
995
return cf.pool.addConstant(CONSTANT_InvokeDynamic_17,
996
new Short[]{ bsmindex, ntindex });
997
}
998
List<Object> extraArgs = Collections.emptyList();
999
if (argi < args.size()) {
1000
Object arg = args.get(argi);
1001
if (arg instanceof List)
1002
extraArgs = (List<Object>) arg;
1003
else
1004
extraArgs = Arrays.asList(arg);
1005
removeEmptyJVMSlots(args);
1006
}
1007
List<Short> extraArgIndexes = new CountedList<>(Short.class);
1008
for (Object x : extraArgs) {
1009
if (x instanceof Number) {
1010
Object num = null; byte numTag = 0;
1011
if (x instanceof Integer) { num = x; numTag = CONSTANT_Integer; }
1012
if (x instanceof Float) { num = Float.floatToRawIntBits((Float)x); numTag = CONSTANT_Float; }
1013
if (x instanceof Long) { num = x; numTag = CONSTANT_Long; }
1014
if (x instanceof Double) { num = Double.doubleToRawLongBits((Double)x); numTag = CONSTANT_Double; }
1015
if (num != null) x = cf.pool.addConstant(numTag, x);
1016
}
1017
if (!(x instanceof Constant)) return null;
1018
extraArgIndexes.add((short) ((Constant)x).index);
1019
}
1020
List<Object[]> specs = bootstrapMethodSpecifiers(true);
1021
int specindex = -1;
1022
Object[] spec = new Object[]{ bsmindex, extraArgIndexes };
1023
for (Object[] spec1 : specs) {
1024
if (Arrays.equals(spec1, spec)) {
1025
specindex = specs.indexOf(spec1);
1026
if (verbose) System.err.println("reusing BSM specifier: "+spec1[0]+spec1[1]);
1027
break;
1028
}
1029
}
1030
if (specindex == -1) {
1031
specindex = (short) specs.size();
1032
specs.add(spec);
1033
if (verbose) System.err.println("adding BSM specifier: "+spec[0]+spec[1]);
1034
}
1035
return cf.pool.addConstant(CONSTANT_InvokeDynamic,
1036
new Short[]{ (short)specindex, ntindex });
1037
}
1038
1039
List<Object[]> bootstrapMethodSpecifiers(boolean createIfNotFound) {
1040
Attr bsms = cf.findAttr("BootstrapMethods");
1041
if (bsms == null) {
1042
if (!createIfNotFound) return null;
1043
bsms = new Attr(cf, "BootstrapMethods", new byte[]{0,0});
1044
assert(bsms == cf.findAttr("BootstrapMethods"));
1045
}
1046
if (bsms.item instanceof byte[]) {
1047
// unflatten
1048
List<Object[]> specs = new CountedList<>(Object[].class);
1049
DataInputStream in = new DataInputStream(new ByteArrayInputStream((byte[]) bsms.item));
1050
try {
1051
int len = (char) in.readShort();
1052
for (int i = 0; i < len; i++) {
1053
short bsm = in.readShort();
1054
int argc = (char) in.readShort();
1055
List<Short> argv = new CountedList<>(Short.class);
1056
for (int j = 0; j < argc; j++)
1057
argv.add(in.readShort());
1058
specs.add(new Object[]{ bsm, argv });
1059
}
1060
} catch (IOException ex) { throw new InternalError(); }
1061
bsms.item = specs;
1062
}
1063
return (List<Object[]>) bsms.item;
1064
}
1065
}
1066
1067
private DataInputStream openInput(File f) throws IOException {
1068
return new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
1069
}
1070
1071
private DataOutputStream openOutput(File f) throws IOException {
1072
if (!overwrite && f.exists())
1073
throw new IOException("file already exists: "+f);
1074
ensureDirectory(f.getParentFile());
1075
return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
1076
}
1077
1078
static byte[] readRawBytes(DataInputStream in, int size) throws IOException {
1079
byte[] bytes = new byte[size];
1080
int nr = in.read(bytes);
1081
if (nr != size)
1082
throw new InternalError("wrong size: "+nr);
1083
return bytes;
1084
}
1085
1086
private interface Chunk {
1087
void readFrom(DataInputStream in) throws IOException;
1088
void writeTo(DataOutputStream out) throws IOException;
1089
}
1090
1091
private static class CountedList<T> extends ArrayList<T> implements Chunk {
1092
final Class<? extends T> itemClass;
1093
final int rowlen;
1094
CountedList(Class<? extends T> itemClass, int rowlen) {
1095
this.itemClass = itemClass;
1096
this.rowlen = rowlen;
1097
}
1098
CountedList(Class<? extends T> itemClass) { this(itemClass, -1); }
1099
public void readFrom(DataInputStream in) throws IOException {
1100
int count = in.readUnsignedShort();
1101
while (size() < count) {
1102
if (rowlen < 0) {
1103
add(readInput(in, itemClass));
1104
} else {
1105
Class<?> elemClass = itemClass.getComponentType();
1106
Object[] row = (Object[]) java.lang.reflect.Array.newInstance(elemClass, rowlen);
1107
for (int i = 0; i < rowlen; i++)
1108
row[i] = readInput(in, elemClass);
1109
add(itemClass.cast(row));
1110
}
1111
}
1112
}
1113
public void writeTo(DataOutputStream out) throws IOException {
1114
out.writeShort((short)size());
1115
for (T item : this) {
1116
writeOutput(out, item);
1117
}
1118
}
1119
}
1120
1121
private static <T> T readInput(DataInputStream in, Class<T> dataClass) throws IOException {
1122
Object data;
1123
if (dataClass == Integer.class) {
1124
data = in.readInt();
1125
} else if (dataClass == Short.class) {
1126
data = in.readShort();
1127
} else if (dataClass == Byte.class) {
1128
data = in.readByte();
1129
} else if (dataClass == String.class) {
1130
data = in.readUTF();
1131
} else if (Chunk.class.isAssignableFrom(dataClass)) {
1132
T obj;
1133
try { obj = dataClass.newInstance(); }
1134
catch (Exception ex) { throw new RuntimeException(ex); }
1135
((Chunk)obj).readFrom(in);
1136
data = obj;
1137
} else {
1138
throw new InternalError("bad input datum: "+dataClass);
1139
}
1140
return dataClass.cast(data);
1141
}
1142
private static <T> T readInput(byte[] bytes, Class<T> dataClass) {
1143
try {
1144
return readInput(new DataInputStream(new ByteArrayInputStream(bytes)), dataClass);
1145
} catch (IOException ex) {
1146
throw new InternalError();
1147
}
1148
}
1149
private static void readInputs(DataInputStream in, Object... data) throws IOException {
1150
for (Object x : data) ((Chunk)x).readFrom(in);
1151
}
1152
1153
private static void writeOutput(DataOutputStream out, Object data) throws IOException {
1154
if (data == null) {
1155
return;
1156
} if (data instanceof Integer) {
1157
out.writeInt((Integer)data);
1158
} else if (data instanceof Long) {
1159
out.writeLong((Long)data);
1160
} else if (data instanceof Short) {
1161
out.writeShort((Short)data);
1162
} else if (data instanceof Byte) {
1163
out.writeByte((Byte)data);
1164
} else if (data instanceof String) {
1165
out.writeUTF((String)data);
1166
} else if (data instanceof byte[]) {
1167
out.write((byte[])data);
1168
} else if (data instanceof Object[]) {
1169
for (Object x : (Object[]) data)
1170
writeOutput(out, x);
1171
} else if (data instanceof Chunk) {
1172
Chunk x = (Chunk) data;
1173
x.writeTo(out);
1174
} else if (data instanceof List) {
1175
for (Object x : (List<?>) data)
1176
writeOutput(out, x);
1177
} else {
1178
throw new InternalError("bad output datum: "+data+" : "+data.getClass().getName());
1179
}
1180
}
1181
private static void writeOutputs(DataOutputStream out, Object... data) throws IOException {
1182
for (Object x : data) writeOutput(out, x);
1183
}
1184
1185
public static abstract class Outer {
1186
public abstract List<? extends Inner> inners();
1187
protected void linkInners() {
1188
for (Inner i : inners()) {
1189
i.linkOuter(this);
1190
if (i instanceof Outer)
1191
((Outer)i).linkInners();
1192
}
1193
}
1194
public <T extends Outer> T outer(Class<T> c) {
1195
for (Outer walk = this;; walk = ((Inner)walk).outer()) {
1196
if (c.isInstance(walk))
1197
return c.cast(walk);
1198
//if (!(walk instanceof Inner)) return null;
1199
}
1200
}
1201
1202
public abstract List<Attr> attrs();
1203
public Attr findAttr(String name) {
1204
return findAttr(outer(ClassFile.class).pool.stringIndex(name, false));
1205
}
1206
public Attr findAttr(int name) {
1207
if (name == 0) return null;
1208
for (Attr a : attrs()) {
1209
if (a.name == name) return a;
1210
}
1211
return null;
1212
}
1213
}
1214
public interface Inner { Outer outer(); void linkOuter(Outer o); }
1215
public static abstract class InnerOuter extends Outer implements Inner {
1216
public Outer outer;
1217
public Outer outer() { return outer; }
1218
public void linkOuter(Outer o) { assert(outer == null); outer = o; }
1219
}
1220
public static class Constant<T> implements Chunk {
1221
public final byte tag;
1222
public final T item;
1223
public final int index;
1224
public Constant(int index, byte tag, T item) {
1225
this.index = index;
1226
this.tag = tag;
1227
this.item = item;
1228
}
1229
public Constant checkTag(byte tag) {
1230
if (this.tag != tag) throw new InternalError(this.toString());
1231
return this;
1232
}
1233
public String itemString() { return (String)item; }
1234
public Short itemIndex() { return (Short)item; }
1235
public Short[] itemIndexes() { return (Short[])item; }
1236
public void readFrom(DataInputStream in) throws IOException {
1237
throw new InternalError("do not call");
1238
}
1239
public void writeTo(DataOutputStream out) throws IOException {
1240
writeOutputs(out, tag, item);
1241
}
1242
public boolean equals(Object x) { return (x instanceof Constant && equals((Constant)x)); }
1243
public boolean equals(Constant that) {
1244
return (this.tag == that.tag && this.itemAsComparable().equals(that.itemAsComparable()));
1245
}
1246
public int hashCode() { return (tag * 31) + this.itemAsComparable().hashCode(); }
1247
public Object itemAsComparable() {
1248
switch (tag) {
1249
case CONSTANT_Double: return Double.longBitsToDouble((Long)item);
1250
case CONSTANT_Float: return Float.intBitsToFloat((Integer)item);
1251
}
1252
return (item instanceof Object[] ? Arrays.asList((Object[])item) : item);
1253
}
1254
public String toString() {
1255
String itstr = String.valueOf(itemAsComparable());
1256
return (index + ":" + tagName(tag) + (itstr.startsWith("[")?"":"=") + itstr);
1257
}
1258
private static String[] TAG_NAMES;
1259
public static String tagName(byte tag) { // used for error messages
1260
if (TAG_NAMES == null)
1261
TAG_NAMES = ("None Utf8 Unicode Integer Float Long Double Class String"
1262
+" Fieldref Methodref InterfaceMethodref NameAndType #13 #14"
1263
+" MethodHandle MethodType InvokeDynamic#17 InvokeDynamic").split(" ");
1264
if ((tag & 0xFF) >= TAG_NAMES.length) return "#"+(tag & 0xFF);
1265
return TAG_NAMES[tag & 0xFF];
1266
}
1267
}
1268
1269
public static class Pool extends CountedList<Constant> implements Chunk {
1270
private Map<String,Short> strings = new TreeMap<>();
1271
1272
public Pool() {
1273
super(Constant.class);
1274
}
1275
public void readFrom(DataInputStream in) throws IOException {
1276
int count = in.readUnsignedShort();
1277
add(null); // always ignore first item
1278
while (size() < count) {
1279
readConstant(in);
1280
}
1281
}
1282
public <T> Constant<T> addConstant(byte tag, T item) {
1283
Constant<T> con = new Constant<>(size(), tag, item);
1284
int idx = indexOf(con);
1285
if (idx >= 0) return get(idx);
1286
add(con);
1287
if (tag == CONSTANT_Utf8) strings.put((String)item, (short) con.index);
1288
return con;
1289
}
1290
private void readConstant(DataInputStream in) throws IOException {
1291
byte tag = in.readByte();
1292
int index = size();
1293
Object arg;
1294
switch (tag) {
1295
case CONSTANT_Utf8:
1296
arg = in.readUTF();
1297
strings.put((String) arg, (short) size());
1298
break;
1299
case CONSTANT_Integer:
1300
case CONSTANT_Float:
1301
arg = in.readInt(); break;
1302
case CONSTANT_Long:
1303
case CONSTANT_Double:
1304
add(new Constant(index, tag, in.readLong()));
1305
add(null);
1306
return;
1307
case CONSTANT_Class:
1308
case CONSTANT_String:
1309
arg = in.readShort(); break;
1310
case CONSTANT_Field:
1311
case CONSTANT_Method:
1312
case CONSTANT_InterfaceMethod:
1313
case CONSTANT_NameAndType:
1314
case CONSTANT_InvokeDynamic_17:
1315
case CONSTANT_InvokeDynamic:
1316
// read an ordered pair
1317
arg = new Short[] { in.readShort(), in.readShort() };
1318
break;
1319
case CONSTANT_MethodHandle:
1320
// read an ordered pair; first part is a u1 (not u2)
1321
arg = new Object[] { in.readByte(), in.readShort() };
1322
break;
1323
case CONSTANT_MethodType:
1324
arg = in.readShort(); break;
1325
default:
1326
throw new InternalError("bad CP tag "+tag);
1327
}
1328
add(new Constant(index, tag, arg));
1329
}
1330
1331
// Access:
1332
public Constant get(int index) {
1333
// extra 1-bits get into the shorts
1334
return super.get((char) index);
1335
}
1336
String getString(byte tag, short index) {
1337
get(index).checkTag(tag);
1338
return getString(index);
1339
}
1340
String getString(short index) {
1341
Object v = get(index).item;
1342
if (v instanceof Short)
1343
v = get((Short)v).checkTag(CONSTANT_Utf8).item;
1344
return (String) v;
1345
}
1346
String[] getStrings(Short[] indexes) {
1347
String[] res = new String[indexes.length];
1348
for (int i = 0; i < indexes.length; i++)
1349
res[i] = getString(indexes[i]);
1350
return res;
1351
}
1352
int stringIndex(String name, boolean createIfNotFound) {
1353
Short x = strings.get(name);
1354
if (x != null) return (char)(int) x;
1355
if (!createIfNotFound) return 0;
1356
return addConstant(CONSTANT_Utf8, name).index;
1357
}
1358
Short[] getMemberRef(short index) {
1359
Short[] cls_nnt = get(index).itemIndexes();
1360
Short[] name_type = get(cls_nnt[1]).itemIndexes();
1361
return new Short[]{ cls_nnt[0], name_type[0], name_type[1] };
1362
}
1363
}
1364
1365
public class ClassFile extends Outer implements Chunk {
1366
ClassFile(File f) throws IOException {
1367
DataInputStream in = openInput(f);
1368
try {
1369
readFrom(in);
1370
} finally {
1371
if (in != null) in.close();
1372
}
1373
}
1374
1375
public int magic, version; // <min:maj>
1376
public final Pool pool = new Pool();
1377
public short access, thisc, superc;
1378
public final List<Short> interfaces = new CountedList<>(Short.class);
1379
public final List<Field> fields = new CountedList<>(Field.class);
1380
public final List<Method> methods = new CountedList<>(Method.class);
1381
public final List<Attr> attrs = new CountedList<>(Attr.class);
1382
1383
public final void readFrom(DataInputStream in) throws IOException {
1384
magic = in.readInt(); version = in.readInt();
1385
if (magic != 0xCAFEBABE) throw new IOException("bad magic number");
1386
pool.readFrom(in);
1387
Code_index = pool.stringIndex("Code", false);
1388
access = in.readShort(); thisc = in.readShort(); superc = in.readShort();
1389
readInputs(in, interfaces, fields, methods, attrs);
1390
if (in.read() >= 0) throw new IOException("junk after end of file");
1391
linkInners();
1392
}
1393
1394
void writeTo(File f) throws IOException {
1395
DataOutputStream out = openOutput(f);
1396
try {
1397
writeTo(out);
1398
} finally {
1399
out.close();
1400
}
1401
}
1402
1403
public void writeTo(DataOutputStream out) throws IOException {
1404
writeOutputs(out, magic, version, pool,
1405
access, thisc, superc, interfaces,
1406
fields, methods, attrs);
1407
}
1408
1409
public byte[] toByteArray() {
1410
try {
1411
ByteArrayOutputStream buf = new ByteArrayOutputStream();
1412
writeTo(new DataOutputStream(buf));
1413
return buf.toByteArray();
1414
} catch (IOException ex) {
1415
throw new InternalError();
1416
}
1417
}
1418
1419
public List<Inner> inners() {
1420
List<Inner> inns = new ArrayList<>();
1421
inns.addAll(fields); inns.addAll(methods); inns.addAll(attrs);
1422
return inns;
1423
}
1424
public List<Attr> attrs() { return attrs; }
1425
1426
// derived stuff:
1427
public String nameString() { return pool.getString(CONSTANT_Class, thisc); }
1428
int Code_index;
1429
}
1430
1431
private static <T extends Member> T findMember(List<T> mems, int name, int type) {
1432
if (name == 0 || type == 0) return null;
1433
for (T m : mems) {
1434
if (m.name == name && m.type == type) return m;
1435
}
1436
return null;
1437
}
1438
1439
public static class Member extends InnerOuter implements Chunk {
1440
public short access, name, type;
1441
public final List<Attr> attrs = new CountedList<>(Attr.class);
1442
public void readFrom(DataInputStream in) throws IOException {
1443
access = in.readShort(); name = in.readShort(); type = in.readShort();
1444
readInputs(in, attrs);
1445
}
1446
public void writeTo(DataOutputStream out) throws IOException {
1447
writeOutputs(out, access, name, type, attrs);
1448
}
1449
public List<Attr> inners() { return attrs; }
1450
public List<Attr> attrs() { return attrs; }
1451
public ClassFile outer() { return (ClassFile) outer; }
1452
public String nameString() { return outer().pool.getString(CONSTANT_Utf8, name); }
1453
public String typeString() { return outer().pool.getString(CONSTANT_Utf8, type); }
1454
public String toString() {
1455
if (outer == null) return super.toString();
1456
return nameString() + (this instanceof Method ? "" : ":")
1457
+ simplifyType(typeString());
1458
}
1459
}
1460
public static class Field extends Member {
1461
}
1462
public static class Method extends Member {
1463
public Code code() {
1464
Attr a = findAttr("Code");
1465
if (a == null) return null;
1466
return (Code) a.item;
1467
}
1468
public Instruction instructions() {
1469
Code code = code();
1470
if (code == null) return null;
1471
return code.instructions();
1472
}
1473
}
1474
1475
public static class Attr extends InnerOuter implements Chunk {
1476
public short name;
1477
public int size = -1; // no pre-declared size
1478
public Object item;
1479
1480
public Attr() {}
1481
public Attr(Outer outer, String name, Object item) {
1482
ClassFile cf = outer.outer(ClassFile.class);
1483
linkOuter(outer);
1484
this.name = (short) cf.pool.stringIndex(name, true);
1485
this.item = item;
1486
outer.attrs().add(this);
1487
}
1488
public void readFrom(DataInputStream in) throws IOException {
1489
name = in.readShort();
1490
size = in.readInt();
1491
item = readRawBytes(in, size);
1492
}
1493
public void writeTo(DataOutputStream out) throws IOException {
1494
out.writeShort(name);
1495
// write the 4-byte size header and then the contents:
1496
byte[] bytes;
1497
int trueSize;
1498
if (item instanceof byte[]) {
1499
bytes = (byte[]) item;
1500
out.writeInt(trueSize = bytes.length);
1501
out.write(bytes);
1502
} else {
1503
trueSize = flatten(out);
1504
}
1505
if (trueSize != size && size >= 0)
1506
System.err.println("warning: attribute size changed "+size+" to "+trueSize);
1507
}
1508
public void linkOuter(Outer o) {
1509
super.linkOuter(o);
1510
if (item instanceof byte[] &&
1511
outer instanceof Method &&
1512
((Method)outer).outer().Code_index == name) {
1513
item = readInput((byte[])item, Code.class);
1514
}
1515
}
1516
public List<Inner> inners() {
1517
if (item instanceof Inner)
1518
return Collections.nCopies(1, (Inner)item);
1519
return Collections.emptyList();
1520
}
1521
public List<Attr> attrs() { return null; } // Code overrides this
1522
public byte[] flatten() {
1523
ByteArrayOutputStream buf = new ByteArrayOutputStream(size);
1524
flatten(buf);
1525
return buf.toByteArray();
1526
}
1527
public int flatten(DataOutputStream out) throws IOException {
1528
ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
1529
int trueSize = flatten(buf);
1530
out.writeInt(trueSize);
1531
buf.writeTo(out);
1532
return trueSize;
1533
}
1534
private int flatten(ByteArrayOutputStream buf) {
1535
try {
1536
writeOutput(new DataOutputStream(buf), item);
1537
return buf.size();
1538
} catch (IOException ex) {
1539
throw new InternalError();
1540
}
1541
}
1542
public String nameString() {
1543
ClassFile cf = outer(ClassFile.class);
1544
if (cf == null) return "#"+name;
1545
return cf.pool.getString(name);
1546
}
1547
public String toString() {
1548
return nameString()+(size < 0 ? "=" : "["+size+"]=")+item;
1549
}
1550
}
1551
1552
public static class Code extends InnerOuter implements Chunk {
1553
public short stacks, locals;
1554
public byte[] bytes;
1555
public final List<Short[]> etable = new CountedList<>(Short[].class, 4);
1556
public final List<Attr> attrs = new CountedList<>(Attr.class);
1557
// etable[N] = (N)*{ startpc, endpc, handlerpc, catchtype }
1558
public void readFrom(DataInputStream in) throws IOException {
1559
stacks = in.readShort(); locals = in.readShort();
1560
bytes = readRawBytes(in, in.readInt());
1561
readInputs(in, etable, attrs);
1562
}
1563
public void writeTo(DataOutputStream out) throws IOException {
1564
writeOutputs(out, stacks, locals, bytes.length, bytes, etable, attrs);
1565
}
1566
public List<Attr> inners() { return attrs; }
1567
public List<Attr> attrs() { return attrs; }
1568
public Instruction instructions() {
1569
return new Instruction(bytes, 0);
1570
}
1571
}
1572
1573
// lots of constants
1574
private static final byte
1575
CONSTANT_Utf8 = 1,
1576
CONSTANT_Integer = 3,
1577
CONSTANT_Float = 4,
1578
CONSTANT_Long = 5,
1579
CONSTANT_Double = 6,
1580
CONSTANT_Class = 7,
1581
CONSTANT_String = 8,
1582
CONSTANT_Field = 9,
1583
CONSTANT_Method = 10,
1584
CONSTANT_InterfaceMethod = 11,
1585
CONSTANT_NameAndType = 12,
1586
CONSTANT_MethodHandle = 15, // JSR 292
1587
CONSTANT_MethodType = 16, // JSR 292
1588
CONSTANT_InvokeDynamic_17 = 17, // JSR 292, only occurs in old class files
1589
CONSTANT_InvokeDynamic = 18; // JSR 292
1590
private static final byte
1591
REF_getField = 1,
1592
REF_getStatic = 2,
1593
REF_putField = 3,
1594
REF_putStatic = 4,
1595
REF_invokeVirtual = 5,
1596
REF_invokeStatic = 6,
1597
REF_invokeSpecial = 7,
1598
REF_newInvokeSpecial = 8,
1599
REF_invokeInterface = 9;
1600
1601
private static final int
1602
opc_nop = 0,
1603
opc_aconst_null = 1,
1604
opc_nconst_MIN = 2, // iconst_m1
1605
opc_nconst_MAX = 15, // dconst_1
1606
opc_bipush = 16,
1607
opc_sipush = 17,
1608
opc_ldc = 18,
1609
opc_ldc_w = 19,
1610
opc_ldc2_w = 20,
1611
opc_aload = 25,
1612
opc_aload_0 = 42,
1613
opc_aload_MAX = 45,
1614
opc_aaload = 50,
1615
opc_astore = 58,
1616
opc_astore_0 = 75,
1617
opc_astore_MAX = 78,
1618
opc_aastore = 83,
1619
opc_pop = 87,
1620
opc_pop2 = 88,
1621
opc_dup = 89,
1622
opc_dup_x1 = 90,
1623
opc_dup_x2 = 91,
1624
opc_dup2 = 92,
1625
opc_dup2_x1 = 93,
1626
opc_dup2_x2 = 94,
1627
opc_swap = 95,
1628
opc_tableswitch = 170,
1629
opc_lookupswitch = 171,
1630
opc_areturn = 176,
1631
opc_getstatic = 178,
1632
opc_putstatic = 179,
1633
opc_getfield = 180,
1634
opc_putfield = 181,
1635
opc_invokevirtual = 182,
1636
opc_invokespecial = 183,
1637
opc_invokestatic = 184,
1638
opc_invokeinterface = 185,
1639
opc_invokedynamic = 186,
1640
opc_anewarray = 189,
1641
opc_checkcast = 192,
1642
opc_ifnull = 198,
1643
opc_ifnonnull = 199,
1644
opc_wide = 196;
1645
1646
private static final Object[] INSTRUCTION_CONSTANTS = {
1647
-1, 0, 1, 2, 3, 4, 5, 0L, 1L, 0.0F, 1.0F, 2.0F, 0.0D, 1.0D
1648
};
1649
1650
private static final String INSTRUCTION_FORMATS =
1651
"nop$ aconst_null$L iconst_m1$I iconst_0$I iconst_1$I "+
1652
"iconst_2$I iconst_3$I iconst_4$I iconst_5$I lconst_0$J_ "+
1653
"lconst_1$J_ fconst_0$F fconst_1$F fconst_2$F dconst_0$D_ "+
1654
"dconst_1$D_ bipush=bx$I sipush=bxx$I ldc=bk$X ldc_w=bkk$X "+
1655
"ldc2_w=bkk$X_ iload=bl/wbll$I lload=bl/wbll$J_ fload=bl/wbll$F "+
1656
"dload=bl/wbll$D_ aload=bl/wbll$L iload_0$I iload_1$I "+
1657
"iload_2$I iload_3$I lload_0$J_ lload_1$J_ lload_2$J_ "+
1658
"lload_3$J_ fload_0$F fload_1$F fload_2$F fload_3$F dload_0$D_ "+
1659
"dload_1$D_ dload_2$D_ dload_3$D_ aload_0$L aload_1$L "+
1660
"aload_2$L aload_3$L iaload$LI$I laload$LI$J_ faload$LI$F "+
1661
"daload$LI$D_ aaload$LI$L baload$LI$I caload$LI$I saload$LI$I "+
1662
"istore=bl/wbll$I$ lstore=bl/wbll$J_$ fstore=bl/wbll$F$ "+
1663
"dstore=bl/wbll$D_$ astore=bl/wbll$L$ istore_0$I$ istore_1$I$ "+
1664
"istore_2$I$ istore_3$I$ lstore_0$J_$ lstore_1$J_$ "+
1665
"lstore_2$J_$ lstore_3$J_$ fstore_0$F$ fstore_1$F$ fstore_2$F$ "+
1666
"fstore_3$F$ dstore_0$D_$ dstore_1$D_$ dstore_2$D_$ "+
1667
"dstore_3$D_$ astore_0$L$ astore_1$L$ astore_2$L$ astore_3$L$ "+
1668
"iastore$LII$ lastore$LIJ_$ fastore$LIF$ dastore$LID_$ "+
1669
"aastore$LIL$ bastore$LII$ castore$LII$ sastore$LII$ pop$X$ "+
1670
"pop2$XX$ dup$X$XX dup_x1$XX$XXX dup_x2$XXX$XXXX dup2$XX$XXXX "+
1671
"dup2_x1$XXX$XXXXX dup2_x2$XXXX$XXXXXX swap$XX$XX "+
1672
"iadd$II$I ladd$J_J_$J_ fadd$FF$F dadd$D_D_$D_ isub$II$I "+
1673
"lsub$J_J_$J_ fsub$FF$F dsub$D_D_$D_ imul$II$I lmul$J_J_$J_ "+
1674
"fmul$FF$F dmul$D_D_$D_ idiv$II$I ldiv$J_J_$J_ fdiv$FF$F "+
1675
"ddiv$D_D_$D_ irem$II$I lrem$J_J_$J_ frem$FF$F drem$D_D_$D_ "+
1676
"ineg$I$I lneg$J_$J_ fneg$F$F dneg$D_$D_ ishl$II$I lshl$J_I$J_ "+
1677
"ishr$II$I lshr$J_I$J_ iushr$II$I lushr$J_I$J_ iand$II$I "+
1678
"land$J_J_$J_ ior$II$I lor$J_J_$J_ ixor$II$I lxor$J_J_$J_ "+
1679
"iinc=blx/wbllxx$ i2l$I$J_ i2f$I$F i2d$I$D_ l2i$J_$I l2f$J_$F "+
1680
"l2d$J_$D_ f2i$F$I f2l$F$J_ f2d$F$D_ d2i$D_$I d2l$D_$J_ "+
1681
"d2f$D_$F i2b$I$I i2c$I$I i2s$I$I lcmp fcmpl fcmpg dcmpl dcmpg "+
1682
"ifeq=boo ifne=boo iflt=boo ifge=boo ifgt=boo ifle=boo "+
1683
"if_icmpeq=boo if_icmpne=boo if_icmplt=boo if_icmpge=boo "+
1684
"if_icmpgt=boo if_icmple=boo if_acmpeq=boo if_acmpne=boo "+
1685
"goto=boo jsr=boo ret=bl/wbll tableswitch=* lookupswitch=* "+
1686
"ireturn lreturn freturn dreturn areturn return "+
1687
"getstatic=bkf$Q putstatic=bkf$Q$ getfield=bkf$L$Q "+
1688
"putfield=bkf$LQ$ invokevirtual=bkm$LQ$Q "+
1689
"invokespecial=bkm$LQ$Q invokestatic=bkm$Q$Q "+
1690
"invokeinterface=bkixx$LQ$Q invokedynamic=bkd__$Q$Q new=bkc$L "+
1691
"newarray=bx$I$L anewarray=bkc$I$L arraylength$L$I athrow "+
1692
"checkcast=bkc$L$L instanceof=bkc$L$I monitorenter$L "+
1693
"monitorexit$L wide=* multianewarray=bkcx ifnull=boo "+
1694
"ifnonnull=boo goto_w=boooo jsr_w=boooo ";
1695
private static final String[] INSTRUCTION_NAMES;
1696
private static final String[] INSTRUCTION_POPS;
1697
private static final int[] INSTRUCTION_INFO;
1698
static {
1699
String[] insns = INSTRUCTION_FORMATS.split(" ");
1700
assert(insns[opc_lookupswitch].startsWith("lookupswitch"));
1701
assert(insns[opc_tableswitch].startsWith("tableswitch"));
1702
assert(insns[opc_wide].startsWith("wide"));
1703
assert(insns[opc_invokedynamic].startsWith("invokedynamic"));
1704
int[] info = new int[256];
1705
String[] names = new String[256];
1706
String[] pops = new String[256];
1707
for (int i = 0; i < insns.length; i++) {
1708
String insn = insns[i];
1709
int dl = insn.indexOf('$');
1710
if (dl > 0) {
1711
String p = insn.substring(dl+1);
1712
if (p.indexOf('$') < 0) p = "$" + p;
1713
pops[i] = p;
1714
insn = insn.substring(0, dl);
1715
}
1716
int eq = insn.indexOf('=');
1717
if (eq < 0) {
1718
info[i] = 1;
1719
names[i] = insn;
1720
continue;
1721
}
1722
names[i] = insn.substring(0, eq);
1723
String fmt = insn.substring(eq+1);
1724
if (fmt.equals("*")) {
1725
info[i] = 0;
1726
continue;
1727
}
1728
int sl = fmt.indexOf('/');
1729
if (sl < 0) {
1730
info[i] = (char) fmt.length();
1731
} else {
1732
String wfmt = fmt.substring(sl+1);
1733
fmt = fmt.substring(0, sl);
1734
info[i] = (char)( fmt.length() + (wfmt.length() * 16) );
1735
}
1736
}
1737
INSTRUCTION_INFO = info;
1738
INSTRUCTION_NAMES = names;
1739
INSTRUCTION_POPS = pops;
1740
}
1741
1742
public static class Instruction implements Cloneable {
1743
byte[] codeBase;
1744
int pc;
1745
int bc;
1746
int info;
1747
int wide;
1748
int len;
1749
Instruction(byte[] codeBase, int pc) {
1750
this.codeBase = codeBase;
1751
init(pc);
1752
}
1753
public Instruction clone() {
1754
try {
1755
return (Instruction) super.clone();
1756
} catch (CloneNotSupportedException ex) {
1757
throw new InternalError();
1758
}
1759
}
1760
private Instruction init(int pc) {
1761
this.pc = pc;
1762
this.bc = codeBase[pc] & 0xFF;
1763
this.info = INSTRUCTION_INFO[bc];
1764
this.wide = 0;
1765
this.len = (info & 0x0F);
1766
if (len == 0)
1767
computeLength();
1768
return this;
1769
}
1770
Instruction next() {
1771
if (len == 0 && bc != 0) throw new InternalError();
1772
int npc = pc + len;
1773
if (npc == codeBase.length)
1774
return null;
1775
return init(npc);
1776
}
1777
void forceNext(int newLen) {
1778
bc = opc_nop;
1779
len = newLen;
1780
}
1781
1782
public String toString() {
1783
StringBuilder buf = new StringBuilder();
1784
buf.append(pc).append(":").append(INSTRUCTION_NAMES[bc]);
1785
switch (len) {
1786
case 3: buf.append(" ").append(u2At(1)); break;
1787
case 5: buf.append(" ").append(u2At(1)).append(" ").append(u2At(3)); break;
1788
default: for (int i = 1; i < len; i++) buf.append(" ").append(u1At(1));
1789
}
1790
return buf.toString();
1791
}
1792
1793
// these are the hard parts
1794
private void computeLength() {
1795
int cases;
1796
switch (bc) {
1797
case opc_wide:
1798
bc = codeBase[pc + 1];
1799
info = INSTRUCTION_INFO[bc];
1800
len = ((info >> 4) & 0x0F);
1801
if (len == 0) throw new RuntimeException("misplaced wide bytecode: "+bc);
1802
return;
1803
1804
case opc_tableswitch:
1805
cases = (u4At(alignedIntOffset(2)) - u4At(alignedIntOffset(1)) + 1);
1806
len = alignedIntOffset(3 + cases*1);
1807
return;
1808
1809
case opc_lookupswitch:
1810
cases = u4At(alignedIntOffset(1));
1811
len = alignedIntOffset(2 + cases*2);
1812
return;
1813
1814
default:
1815
throw new RuntimeException("unknown bytecode: "+bc);
1816
}
1817
}
1818
// switch code
1819
// clget the Nth int (where 0 is the first after the opcode itself)
1820
public int alignedIntOffset(int n) {
1821
int pos = pc + 1;
1822
pos += ((-pos) & 0x03); // align it
1823
pos += (n * 4);
1824
return pos - pc;
1825
}
1826
public int u1At(int pos) {
1827
return (codeBase[pc+pos] & 0xFF);
1828
}
1829
public int u2At(int pos) {
1830
return (u1At(pos+0)<<8) + u1At(pos+1);
1831
}
1832
public int u4At(int pos) {
1833
return (u2At(pos+0)<<16) + u2At(pos+2);
1834
}
1835
public void u1AtPut(int pos, int x) {
1836
codeBase[pc+pos] = (byte)x;
1837
}
1838
public void u2AtPut(int pos, int x) {
1839
codeBase[pc+pos+0] = (byte)(x >> 8);
1840
codeBase[pc+pos+1] = (byte)(x >> 0);
1841
}
1842
}
1843
1844
static String simplifyType(String type) {
1845
String simpleType = OBJ_SIGNATURE.matcher(type).replaceAll("L");
1846
assert(simpleType.matches("^\\([A-Z]*\\)[A-Z]$"));
1847
// change (DD)D to (D_D_)D_
1848
simpleType = WIDE_SIGNATURE.matcher(simpleType).replaceAll("\\0_");
1849
return simpleType;
1850
}
1851
static int argsize(String type) {
1852
return simplifyType(type).length()-3;
1853
}
1854
private static final Pattern OBJ_SIGNATURE = Pattern.compile("\\[*L[^;]*;|\\[+[A-Z]");
1855
private static final Pattern WIDE_SIGNATURE = Pattern.compile("[JD]");
1856
}
1857
1858