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