3243N/A/*
3909N/A * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3243N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3243N/A *
3243N/A * This code is free software; you can redistribute it and/or modify it
3243N/A * under the terms of the GNU General Public License version 2 only, as
3243N/A * published by the Free Software Foundation. Oracle designates this
3243N/A * particular file as subject to the "Classpath" exception as provided
3243N/A * by Oracle in the LICENSE file that accompanied this code.
3243N/A *
3243N/A * This code is distributed in the hope that it will be useful, but WITHOUT
3243N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
3243N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
3243N/A * version 2 for more details (a copy is included in the LICENSE file that
3243N/A * accompanied this code).
3243N/A *
3243N/A * You should have received a copy of the GNU General Public License version
3243N/A * 2 along with this work; if not, write to the Free Software Foundation,
3243N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
3243N/A *
3243N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
3243N/A * or visit www.oracle.com if you need additional information or have any
3243N/A * questions.
3243N/A */
3243N/A
3243N/Apackage indify;
3243N/A
3243N/Aimport java.util.*;
3243N/Aimport java.io.*;
3243N/Aimport java.lang.reflect.Modifier;
3243N/Aimport java.util.regex.*;
3243N/A
3243N/A/**
3243N/A * Transform one or more class files to incorporate JSR 292 features,
3243N/A * such as {@code invokedynamic}.
3243N/A * <p>
3243N/A * This is a standalone program in a single source file.
3243N/A * In this form, it may be useful for test harnesses, small experiments, and javadoc examples.
3243N/A * Copies of this file may show up in multiple locations for standalone usage.
3243N/A * The primary maintained location of this file is as follows:
3243N/A * <a href="http://kenai.com/projects/ninja/sources/indify-repo/content/src/indify/Indify.java">
3243N/A * http://kenai.com/projects/ninja/sources/indify-repo/content/src/indify/Indify.java</a>
3243N/A * <p>
3243N/A * Static private methods named MH_x and MT_x (where x is arbitrary)
3243N/A * must be stereotyped generators of MethodHandle and MethodType
3243N/A * constants. All calls to them are transformed to {@code CONSTANT_MethodHandle}
3243N/A * and {@code CONSTANT_MethodType} "ldc" instructions.
3243N/A * The stereotyped code must create method types by calls to {@code methodType} or
3243N/A * {@code fromMethodDescriptorString}. The "lookup" argument must be created
3793N/A * by calls to {@code java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
3243N/A * The class and string arguments must be constant.
3793N/A * The following methods of {@code java.lang.invoke.MethodHandle.Lookup Lookup} are
3243N/A * allowed for method handle creation: {@code findStatic}, {@code findVirtual},
3243N/A * {@code findConstructor}, {@code findSpecial},
3243N/A * {@code findGetter}, {@code findSetter},
3243N/A * {@code findStaticGetter}, or {@code findStaticSetter}.
3243N/A * The call to one of these methods must be followed immediately
3243N/A * by an {@code areturn} instruction.
3243N/A * The net result of the call to the MH_x or MT_x method must be
3243N/A * the creation of a constant method handle. Thus, replacing calls
3243N/A * to MH_x or MT_x methods by {@code ldc} instructions should leave
3243N/A * the meaning of the program unchanged.
3243N/A * <p>
3243N/A * Static private methods named INDY_x must be stereotyped generators
3243N/A * of {@code invokedynamic} call sites.
3243N/A * All calls to them must be immediately followed by
3243N/A * {@code invokeExact} calls.
3243N/A * All such pairs of calls are transformed to {@code invokedynamic}
3243N/A * instructions. Each INDY_x method must begin with a call to a
3243N/A * MH_x method, which is taken to be its bootstrap method.
3243N/A * The method must be immediately invoked (via {@code invokeGeneric}
3243N/A * on constant lookup, name, and type arguments. An object array of
3243N/A * constants may also be appended to the {@code invokeGeneric call}.
3243N/A * This call must be cast to {@code CallSite}, and the result must be
3243N/A * immediately followed by a call to {@code dynamicInvoker}, with the
3243N/A * resulting method handle returned.
3243N/A * <p>
3243N/A * The net result of all of these actions is equivalent to the JVM's
3243N/A * execution of an {@code invokedynamic} instruction in the unlinked state.
3243N/A * Running this code once should produce the same results as running
3243N/A * the corresponding {@code invokedynamic} instruction.
3243N/A * In order to model the caching behavior, the code of an INDY_x
3243N/A * method is allowed to begin with getstatic, aaload, and if_acmpne
3243N/A * instructions which load a static method handle value and return it
3243N/A * if the value is non-null.
3243N/A * <p>
3243N/A * Example usage:
3243N/A * <blockquote><pre>
3243N/A$ JAVA_HOME=(some recent OpenJDK 7 build)
3243N/A$ ant
3243N/A$ $JAVA_HOME/bin/java -cp build/classes indify.Indify --overwrite --dest build/testout build/classes/indify/Example.class
3907N/A$ $JAVA_HOME/bin/java -cp build/classes indify.Example
3243N/AMT = (java.lang.Object)java.lang.Object
3243N/AMH = adder(int,int)java.lang.Integer
3243N/Aadder(1,2) = 3
3243N/Acalling indy: 42
3907N/A$ $JAVA_HOME/bin/java -cp build/testout indify.Example
3243N/A(same output as above)
3243N/A * </pre></blockquote>
3243N/A * <p>
3243N/A * A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome.
3243N/A * @author John Rose
3243N/A */
3243N/Apublic class Indify {
3243N/A public static void main(String... av) throws IOException {
3243N/A new Indify().run(av);
3243N/A }
3243N/A
3243N/A public File dest;
3243N/A public String[] classpath = {"."};
3243N/A public boolean keepgoing = false;
3243N/A public boolean expandProperties = false;
3243N/A public boolean overwrite = false;
3243N/A public boolean quiet = false;
3243N/A public boolean verbose = false;
3243N/A public boolean all = false;
3243N/A public int verifySpecifierCount = -1;
3243N/A
3243N/A public void run(String... av) throws IOException {
3243N/A List<String> avl = new ArrayList<>(Arrays.asList(av));
3243N/A parseOptions(avl);
3243N/A if (avl.isEmpty())
3243N/A throw new IllegalArgumentException("Usage: indify [--dest dir] [option...] file...");
3243N/A if ("--java".equals(avl.get(0))) {
3243N/A avl.remove(0);
3243N/A try {
3243N/A runApplication(avl.toArray(new String[0]));
3243N/A } catch (Exception ex) {
3243N/A if (ex instanceof RuntimeException) throw (RuntimeException) ex;
3243N/A throw new RuntimeException(ex);
3243N/A }
3243N/A return;
3243N/A }
3243N/A Exception err = null;
3243N/A for (String a : avl) {
3243N/A try {
3243N/A indify(a);
3243N/A } catch (Exception ex) {
3243N/A if (err == null) err = ex;
3243N/A System.err.println("failure on "+a);
3243N/A if (!keepgoing) break;
3243N/A }
3243N/A }
3243N/A if (err != null) {
3243N/A if (err instanceof IOException) throw (IOException) err;
3243N/A throw (RuntimeException) err;
3243N/A }
3243N/A }
3243N/A
3243N/A /** Execute the given application under a class loader which indifies all application classes. */
3243N/A public void runApplication(String... av) throws Exception {
3243N/A List<String> avl = new ArrayList<>(Arrays.asList(av));
3243N/A String mainClassName = avl.remove(0);
3243N/A av = avl.toArray(new String[0]);
3243N/A Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader());
3243N/A java.lang.reflect.Method main = mainClass.getMethod("main", String[].class);
3528N/A try { main.setAccessible(true); } catch (SecurityException ex) { }
3243N/A main.invoke(null, (Object) av);
3243N/A }
3243N/A
3243N/A public void parseOptions(List<String> av) throws IOException {
3243N/A for (; !av.isEmpty(); av.remove(0)) {
3243N/A String a = av.get(0);
3243N/A if (a.startsWith("-")) {
3243N/A String a2 = null;
3243N/A int eq = a.indexOf('=');
3243N/A if (eq > 0) {
3243N/A a2 = maybeExpandProperties(a.substring(eq+1));
3243N/A a = a.substring(0, eq+1);
3243N/A }
3243N/A switch (a) {
3243N/A case "--java":
3243N/A return; // keep this argument
3243N/A case "-d": case "--dest": case "-d=": case "--dest=":
3243N/A dest = new File(a2 != null ? a2 : maybeExpandProperties(av.remove(1)));
3243N/A break;
3243N/A case "-cp": case "--classpath":
3243N/A classpath = maybeExpandProperties(av.remove(1)).split("["+File.pathSeparatorChar+"]");
3243N/A break;
3243N/A case "-k": case "--keepgoing": case "--keepgoing=":
3243N/A keepgoing = booleanOption(a2); // print errors but keep going
3243N/A break;
3243N/A case "--expand-properties": case "--expand-properties=":
3243N/A expandProperties = booleanOption(a2); // expand property references in subsequent arguments
3243N/A break;
3243N/A case "--verify-specifier-count": case "--verify-specifier-count=":
3243N/A verifySpecifierCount = Integer.valueOf(a2);
3243N/A break;
3243N/A case "--overwrite": case "--overwrite=":
3243N/A overwrite = booleanOption(a2); // overwrite output files
3243N/A break;
3243N/A case "--all": case "--all=":
3243N/A all = booleanOption(a2); // copy all classes, even if no patterns
3243N/A break;
3243N/A case "-q": case "--quiet": case "--quiet=":
3243N/A quiet = booleanOption(a2); // less output
3243N/A break;
3243N/A case "-v": case "--verbose": case "--verbose=":
3243N/A verbose = booleanOption(a2); // more output
3243N/A break;
3243N/A default:
3243N/A throw new IllegalArgumentException("unrecognized flag: "+a);
3243N/A }
3243N/A continue;
3243N/A } else {
3243N/A break;
3243N/A }
3243N/A }
3243N/A if (dest == null && !overwrite)
3243N/A throw new RuntimeException("no output specified; need --dest d or --overwrite");
3243N/A if (expandProperties) {
3243N/A for (int i = 0; i < av.size(); i++)
3243N/A av.set(i, maybeExpandProperties(av.get(i)));
3243N/A }
3243N/A }
3243N/A
3243N/A private boolean booleanOption(String s) {
3243N/A if (s == null) return true;
3243N/A switch (s) {
3528N/A case "true": case "yes": case "on": case "1": return true;
3528N/A case "false": case "no": case "off": case "0": return false;
3243N/A }
3243N/A throw new IllegalArgumentException("unrecognized boolean flag="+s);
3243N/A }
3243N/A
3243N/A private String maybeExpandProperties(String s) {
3243N/A if (!expandProperties) return s;
3243N/A Set<String> propsDone = new HashSet<>();
3243N/A while (s.contains("${")) {
3243N/A int lbrk = s.indexOf("${");
3243N/A int rbrk = s.indexOf('}', lbrk);
3243N/A if (rbrk < 0) break;
3243N/A String prop = s.substring(lbrk+2, rbrk);
3243N/A if (!propsDone.add(prop)) break;
3243N/A String value = System.getProperty(prop);
3243N/A if (verbose) System.err.println("expanding ${"+prop+"} => "+value);
3243N/A if (value == null) break;
3243N/A s = s.substring(0, lbrk) + value + s.substring(rbrk+1);
3243N/A }
3243N/A return s;
3243N/A }
3243N/A
3243N/A public void indify(String a) throws IOException {
3243N/A File f = new File(a);
3243N/A String fn = f.getName();
3243N/A if (fn.endsWith(".class") && f.isFile())
3243N/A indifyFile(f, dest);
3243N/A else if (fn.endsWith(".jar") && f.isFile())
3243N/A indifyJar(f, dest);
3243N/A else if (f.isDirectory())
3243N/A indifyTree(f, dest);
3243N/A else if (!keepgoing)
3243N/A throw new RuntimeException("unrecognized file: "+a);
3243N/A }
3243N/A
3243N/A private void ensureDirectory(File dir) {
3243N/A if (dir.mkdirs() && !quiet)
3243N/A System.err.println("created "+dir);
3243N/A }
3243N/A
3243N/A public void indifyFile(File f, File dest) throws IOException {
3243N/A if (verbose) System.err.println("reading "+f);
3243N/A ClassFile cf = new ClassFile(f);
3243N/A Logic logic = new Logic(cf);
3243N/A boolean changed = logic.transform();
3243N/A logic.reportPatternMethods(quiet, keepgoing);
3243N/A if (changed || all) {
3243N/A File outfile;
3243N/A if (dest != null) {
3243N/A ensureDirectory(dest);
3243N/A outfile = classPathFile(dest, cf.nameString());
3243N/A } else {
3243N/A outfile = f; // overwrite input file, no matter where it is
3243N/A }
3243N/A cf.writeTo(outfile);
3243N/A if (!quiet) System.err.println("wrote "+outfile);
3243N/A }
3243N/A }
3243N/A
3243N/A File classPathFile(File pathDir, String className) {
3528N/A String qualname = className.replace('.','/')+".class";
3243N/A qualname = qualname.replace('/', File.separatorChar);
3243N/A return new File(pathDir, qualname);
3243N/A }
3243N/A
3243N/A public void indifyJar(File f, Object dest) throws IOException {
3243N/A throw new UnsupportedOperationException("Not yet implemented");
3243N/A }
3243N/A
3243N/A public void indifyTree(File f, File dest) throws IOException {
3243N/A if (verbose) System.err.println("reading directory: "+f);
3243N/A for (File f2 : f.listFiles(new FilenameFilter() {
3243N/A public boolean accept(File dir, String name) {
3243N/A if (name.endsWith(".class")) return true;
3243N/A if (name.contains(".")) return false;
3243N/A // return true if it might be a package name:
3243N/A return Character.isJavaIdentifierStart(name.charAt(0));
3243N/A }})) {
3243N/A if (f2.getName().endsWith(".class"))
3243N/A indifyFile(f2, dest);
3243N/A else if (f2.isDirectory())
3243N/A indifyTree(f2, dest);
3243N/A }
3243N/A }
3243N/A
3243N/A public ClassLoader makeClassLoader() {
3243N/A return new Loader();
3243N/A }
3243N/A private class Loader extends ClassLoader {
3243N/A Loader() {
3243N/A this(Indify.class.getClassLoader());
3243N/A }
3243N/A Loader(ClassLoader parent) {
3243N/A super(parent);
3243N/A }
3243N/A public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
3243N/A File f = findClassInPath(name);
3243N/A if (f != null) {
3243N/A try {
3243N/A Class<?> c = transformAndLoadClass(f);
3243N/A if (c != null) {
3243N/A if (resolve) resolveClass(c);
3243N/A return c;
3243N/A }
3907N/A } catch (ClassNotFoundException ex) {
3907N/A // fall through
3907N/A } catch (IOException ex) {
3907N/A // fall through
3243N/A } catch (Exception ex) {
3907N/A // pass error from reportPatternMethods, etc.
3907N/A if (ex instanceof RuntimeException) throw (RuntimeException) ex;
3907N/A throw new RuntimeException(ex);
3243N/A }
3243N/A }
3243N/A return super.loadClass(name, resolve);
3243N/A }
3243N/A private File findClassInPath(String name) {
3243N/A for (String s : classpath) {
3243N/A File f = classPathFile(new File(s), name);
3528N/A //System.out.println("Checking for "+f);
3243N/A if (f.exists() && f.canRead()) {
3243N/A return f;
3243N/A }
3243N/A }
3243N/A return null;
3243N/A }
3243N/A protected Class<?> findClass(String name) throws ClassNotFoundException {
3243N/A try {
3793N/A File f = findClassInPath(name);
3793N/A if (f != null) {
3793N/A Class<?> c = transformAndLoadClass(f);
3793N/A if (c != null) return c;
3793N/A }
3243N/A } catch (IOException ex) {
3243N/A throw new ClassNotFoundException("IO error", ex);
3243N/A }
3793N/A throw new ClassNotFoundException();
3243N/A }
3243N/A private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException {
3528N/A if (verbose) System.err.println("Loading class from "+f);
3243N/A ClassFile cf = new ClassFile(f);
3243N/A Logic logic = new Logic(cf);
3243N/A boolean changed = logic.transform();
3528N/A if (verbose && !changed) System.err.println("(no change)");
3243N/A logic.reportPatternMethods(!verbose, keepgoing);
3243N/A byte[] bytes = cf.toByteArray();
3243N/A return defineClass(null, bytes, 0, bytes.length);
3243N/A }
3243N/A }
3243N/A
3243N/A private class Logic {
3243N/A // Indify logic, per se.
3243N/A ClassFile cf;
3243N/A final char[] poolMarks;
3243N/A final Map<Method,Constant> constants = new HashMap<>();
3243N/A final Map<Method,String> indySignatures = new HashMap<>();
3243N/A Logic(ClassFile cf) {
3243N/A this.cf = cf;
3243N/A poolMarks = new char[cf.pool.size()];
3243N/A }
3243N/A boolean transform() {
3243N/A if (!initializeMarks()) return false;
3243N/A if (!findPatternMethods()) return false;
3243N/A Pool pool = cf.pool;
3243N/A //for (Constant c : cp) System.out.println(" # "+c);
3243N/A for (Method m : cf.methods) {
3243N/A if (constants.containsKey(m)) continue; // don't bother
3243N/A // Transform references.
3243N/A int blab = 0;
3243N/A for (Instruction i = m.instructions(); i != null; i = i.next()) {
3243N/A if (i.bc != opc_invokestatic) continue;
3243N/A int methi = i.u2At(1);
3243N/A if (poolMarks[methi] == 0) continue;
3243N/A Short[] ref = pool.getMemberRef((short)methi);
3243N/A Method conm = findMember(cf.methods, ref[1], ref[2]);
3243N/A if (conm == null) continue;
3243N/A Constant con = constants.get(conm);
3243N/A if (con == null) continue;
3243N/A if (blab++ == 0 && !quiet)
3243N/A System.err.println("patching "+cf.nameString()+"."+m);
3243N/A //if (blab == 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); }
3907N/A if (con.tag == CONSTANT_InvokeDynamic) {
3243N/A // need to patch the following instruction too,
3243N/A // but there are usually intervening argument pushes too
3243N/A Instruction i2 = findPop(i);
3243N/A Short[] ref2 = null;
3243N/A short ref2i = 0;
3243N/A if (i2 != null && i2.bc == opc_invokevirtual &&
3243N/A poolMarks[(char)(ref2i = (short) i2.u2At(1))] == 'D')
3243N/A ref2 = pool.getMemberRef(ref2i);
3243N/A if (ref2 == null || !"invokeExact".equals(pool.getString(ref2[1]))) {
3243N/A System.err.println(m+": failed to create invokedynamic at "+i.pc);
3243N/A continue;
3243N/A }
3243N/A String invType = pool.getString(ref2[2]);
3243N/A String bsmType = indySignatures.get(conm);
3243N/A if (!invType.equals(bsmType)) {
3243N/A System.err.println(m+": warning: "+conm+" call type and local invoke type differ: "
3243N/A +bsmType+", "+invType);
3243N/A }
3243N/A assert(i.len == 3 || i2.len == 3);
3243N/A if (!quiet) System.err.println(i+" "+conm+";...; "+i2+" => invokedynamic "+con);
3243N/A int start = i.pc + 3, end = i2.pc;
3243N/A System.arraycopy(i.codeBase, start, i.codeBase, i.pc, end-start);
3243N/A i.forceNext(0); // force revisit of new instruction
3243N/A i2.u1AtPut(-3, opc_invokedynamic);
3243N/A i2.u2AtPut(-2, con.index);
3243N/A i2.u2AtPut(0, (short)0);
3243N/A i2.u1AtPut(2, opc_nop);
3243N/A //System.out.println(new Instruction(i.codeBase, i2.pc-3));
3243N/A } else {
3243N/A if (!quiet) System.err.println(i+" "+conm+" => ldc "+con);
3243N/A assert(i.len == 3);
3243N/A i.u1AtPut(0, opc_ldc_w);
3243N/A i.u2AtPut(1, con.index);
3243N/A }
3243N/A }
3243N/A //if (blab >= 1) { for (Instruction j = m.instructions(); j != null; j = j.next()) System.out.println(" |"+j); }
3243N/A }
3243N/A cf.methods.removeAll(constants.keySet());
3243N/A return true;
3243N/A }
3243N/A
3243N/A // Scan forward from the instruction to find where the stack p
3243N/A // below the current sp at the instruction.
3243N/A Instruction findPop(Instruction i) {
3243N/A //System.out.println("findPop from "+i);
3243N/A Pool pool = cf.pool;
3243N/A JVMState jvm = new JVMState();
3243N/A decode:
3243N/A for (i = i.clone().next(); i != null; i = i.next()) {
3243N/A String pops = INSTRUCTION_POPS[i.bc];
3243N/A //System.out.println(" "+i+" "+jvm.stack+" : "+pops.replace("$", " => "));
3243N/A if (pops == null) break;
3243N/A if (jvm.stackMotion(i.bc)) continue decode;
3243N/A if (pops.indexOf('Q') >= 0) {
3243N/A Short[] ref = pool.getMemberRef((short) i.u2At(1));
3243N/A String type = simplifyType(pool.getString(CONSTANT_Utf8, ref[2]));
3243N/A switch (i.bc) {
3243N/A case opc_getstatic:
3243N/A case opc_getfield:
3243N/A case opc_putstatic:
3243N/A case opc_putfield:
3243N/A pops = pops.replace("Q", type);
3243N/A break;
3243N/A default:
3243N/A if (!type.startsWith("("))
3243N/A throw new InternalError(i.toString());
3243N/A pops = pops.replace("Q$Q", type.substring(1).replace(")","$"));
3243N/A break;
3243N/A }
3243N/A //System.out.println("special type: "+type+" => "+pops);
3243N/A }
3243N/A int npops = pops.indexOf('$');
3243N/A if (npops < 0) throw new InternalError();
3243N/A if (npops > jvm.sp()) return i;
3243N/A List<Object> args = jvm.args(npops);
3243N/A int k = 0;
3243N/A for (Object x : args) {
3243N/A char have = (Character) x;
3243N/A char want = pops.charAt(k++);
3243N/A if (have == 'X' || want == 'X') continue;
3243N/A if (have != want) break decode;
3243N/A }
3243N/A if (pops.charAt(k++) != '$') break decode;
3243N/A args.clear();
3243N/A while (k < pops.length())
3243N/A args.add(pops.charAt(k++));
3243N/A }
3243N/A System.err.println("*** bailout on jvm: "+jvm.stack+" "+i);
3243N/A return null;
3243N/A }
3243N/A
3243N/A boolean findPatternMethods() {
3243N/A boolean found = false;
3243N/A for (char mark : "THI".toCharArray()) {
3243N/A for (Method m : cf.methods) {
3243N/A if (!Modifier.isPrivate(m.access)) continue;
3243N/A if (!Modifier.isStatic(m.access)) continue;
3243N/A if (nameAndTypeMark(m.name, m.type) == mark) {
3243N/A Constant con = scanPattern(m, mark);
3243N/A if (con == null) continue;
3243N/A constants.put(m, con);
3243N/A found = true;
3243N/A }
3243N/A }
3243N/A }
3243N/A return found;
3243N/A }
3243N/A
3243N/A void reportPatternMethods(boolean quietly, boolean allowMatchFailure) {
3243N/A if (!quietly && !constants.keySet().isEmpty())
3243N/A System.err.println("pattern methods removed: "+constants.keySet());
3243N/A for (Method m : cf.methods) {
3243N/A if (nameMark(cf.pool.getString(m.name)) != 0 &&
3243N/A constants.get(m) == null) {
3243N/A String failure = "method has special name but fails to match pattern: "+m;
3243N/A if (!allowMatchFailure)
3243N/A throw new IllegalArgumentException(failure);
3243N/A else if (!quietly)
3243N/A System.err.println("warning: "+failure);
3243N/A }
3243N/A }
3243N/A if (verifySpecifierCount >= 0) {
3243N/A List<Object[]> specs = bootstrapMethodSpecifiers(false);
3243N/A int specsLen = (specs == null ? 0 : specs.size());
4250N/A // Pass by specsLen == 0, to help with associated (inner) classes.
4250N/A if (specsLen == 0) specsLen = verifySpecifierCount;
3243N/A if (specsLen != verifySpecifierCount) {
3243N/A throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount);
3243N/A }
3243N/A }
3528N/A if (!quiet) System.err.flush();
3243N/A }
3243N/A
3243N/A // mark constant pool entries according to participation in patterns
3243N/A boolean initializeMarks() {
3243N/A boolean changed = false;
3243N/A for (;;) {
3243N/A boolean changed1 = false;
3243N/A int cpindex = -1;
3243N/A for (Constant e : cf.pool) {
3243N/A ++cpindex;
3243N/A if (e == null) continue;
3243N/A char mark = poolMarks[cpindex];
3243N/A if (mark != 0) continue;
3243N/A switch (e.tag) {
3243N/A case CONSTANT_Utf8:
3243N/A mark = nameMark(e.itemString()); break;
3243N/A case CONSTANT_NameAndType:
3243N/A mark = nameAndTypeMark(e.itemIndexes()); break;
3243N/A case CONSTANT_Class: {
3243N/A int n1 = e.itemIndex();
3243N/A char nmark = poolMarks[(char)n1];
3243N/A if ("DJ".indexOf(nmark) >= 0)
3243N/A mark = nmark;
3243N/A break;
3243N/A }
3243N/A case CONSTANT_Field:
3243N/A case CONSTANT_Method: {
3243N/A Short[] n12 = e.itemIndexes();
3243N/A short cl = n12[0];
3243N/A short nt = n12[1];
3243N/A char cmark = poolMarks[(char)cl];
3243N/A if (cmark != 0) {
3907N/A mark = cmark; // it is a java.lang.invoke.* or java.lang.* method
3243N/A break;
3243N/A }
3243N/A String cls = cf.pool.getString(CONSTANT_Class, cl);
3243N/A if (cls.equals(cf.nameString())) {
3243N/A switch (poolMarks[(char)nt]) {
3243N/A // it is a private MH/MT/INDY method
3243N/A case 'T': case 'H': case 'I':
3243N/A mark = poolMarks[(char)nt];
3243N/A break;
3243N/A }
3243N/A }
3243N/A break;
3243N/A }
3243N/A default: break;
3243N/A }
3243N/A if (mark != 0) {
3243N/A poolMarks[cpindex] = mark;
3243N/A changed1 = true;
3243N/A }
3243N/A }
3243N/A if (!changed1)
3243N/A break;
3243N/A changed = true;
3243N/A }
3243N/A return changed;
3243N/A }
3243N/A char nameMark(String s) {
3243N/A if (s.startsWith("MT_")) return 'T';
3243N/A else if (s.startsWith("MH_")) return 'H';
3243N/A else if (s.startsWith("INDY_")) return 'I';
3793N/A else if (s.startsWith("java/lang/invoke/")) return 'D';
3243N/A else if (s.startsWith("java/lang/")) return 'J';
3243N/A return 0;
3243N/A }
3243N/A char nameAndTypeMark(Short[] n12) {
3243N/A return nameAndTypeMark(n12[0], n12[1]);
3243N/A }
3243N/A char nameAndTypeMark(short n1, short n2) {
3243N/A char mark = poolMarks[(char)n1];
3243N/A if (mark == 0) return 0;
3243N/A String descr = cf.pool.getString(CONSTANT_Utf8, n2);
3243N/A String requiredType;
3243N/A switch (poolMarks[(char)n1]) {
3793N/A case 'H': requiredType = "()Ljava/lang/invoke/MethodHandle;"; break;
3793N/A case 'T': requiredType = "()Ljava/lang/invoke/MethodType;"; break;
3793N/A case 'I': requiredType = "()Ljava/lang/invoke/MethodHandle;"; break;
3243N/A default: return 0;
3243N/A }
3793N/A if (matchType(descr, requiredType)) return mark;
3243N/A return 0;
3243N/A }
3243N/A
3793N/A boolean matchType(String descr, String requiredType) {
3793N/A if (descr.equals(requiredType)) return true;
3793N/A return false;
3793N/A }
3793N/A
3243N/A private class JVMState {
3243N/A final List<Object> stack = new ArrayList<>();
3243N/A int sp() { return stack.size(); }
3243N/A void push(Object x) { stack.add(x); }
3243N/A void push2(Object x) { stack.add(EMPTY_SLOT); stack.add(x); }
3243N/A void pushAt(int pos, Object x) { stack.add(stack.size()+pos, x); }
3243N/A Object pop() { return stack.remove(sp()-1); }
3243N/A Object top() { return stack.get(sp()-1); }
3243N/A List<Object> args(boolean hasRecv, String type) {
3243N/A return args(argsize(type) + (hasRecv ? 1 : 0));
3243N/A }
3243N/A List<Object> args(int argsize) {
3243N/A return stack.subList(sp()-argsize, sp());
3243N/A }
3243N/A boolean stackMotion(int bc) {
3243N/A switch (bc) {
3243N/A case opc_pop: pop(); break;
3243N/A case opc_pop2: pop(); pop(); break;
3243N/A case opc_swap: pushAt(-1, pop()); break;
3243N/A case opc_dup: push(top()); break;
3243N/A case opc_dup_x1: pushAt(-2, top()); break;
3243N/A case opc_dup_x2: pushAt(-3, top()); break;
3243N/A // ? also: dup2{,_x1,_x2}
3243N/A default: return false;
3243N/A }
3243N/A return true;
3243N/A }
3243N/A }
3243N/A private final String EMPTY_SLOT = "_";
3243N/A private void removeEmptyJVMSlots(List<Object> args) {
3243N/A for (;;) {
3243N/A int i = args.indexOf(EMPTY_SLOT);
3243N/A if (i >= 0 && i+1 < args.size()
3243N/A && (isConstant(args.get(i+1), CONSTANT_Long) ||
3243N/A isConstant(args.get(i+1), CONSTANT_Double)))
3243N/A args.remove(i);
3243N/A else break;
3243N/A }
3243N/A }
3243N/A
3243N/A private Constant scanPattern(Method m, char patternMark) {
3243N/A if (verbose) System.err.println("scan "+m+" for pattern="+patternMark);
3243N/A int wantTag;
3243N/A switch (patternMark) {
3243N/A case 'T': wantTag = CONSTANT_MethodType; break;
3243N/A case 'H': wantTag = CONSTANT_MethodHandle; break;
3243N/A case 'I': wantTag = CONSTANT_InvokeDynamic; break;
3243N/A default: throw new InternalError();
3243N/A }
3243N/A Instruction i = m.instructions();
3243N/A JVMState jvm = new JVMState();
3243N/A Pool pool = cf.pool;
3243N/A int branchCount = 0;
3243N/A Object arg;
3243N/A List<Object> args;
3243N/A List<Object> bsmArgs = null; // args to invokeGeneric
3243N/A decode:
3243N/A for (; i != null; i = i.next()) {
3243N/A //System.out.println(jvm.stack+" "+i);
3243N/A int bc = i.bc;
3243N/A switch (bc) {
3243N/A case opc_ldc: jvm.push(pool.get(i.u1At(1))); break;
3243N/A case opc_ldc_w: jvm.push(pool.get(i.u2At(1))); break;
3243N/A case opc_ldc2_w: jvm.push2(pool.get(i.u2At(1))); break;
3243N/A case opc_aconst_null: jvm.push(null); break;
3243N/A case opc_bipush: jvm.push((int)(byte) i.u1At(1)); break;
3243N/A case opc_sipush: jvm.push((int)(short)i.u2At(1)); break;
3243N/A
3243N/A // these support creation of a restarg array
3243N/A case opc_anewarray:
3243N/A arg = jvm.pop();
3243N/A if (!(arg instanceof Integer)) break decode;
3243N/A arg = Arrays.asList(new Object[(Integer)arg]);
3243N/A jvm.push(arg);
3243N/A break;
3243N/A case opc_dup:
3243N/A jvm.push(jvm.top()); break;
3243N/A case opc_aastore:
3243N/A args = jvm.args(3); // array, index, value
3243N/A if (args.get(0) instanceof List &&
3243N/A args.get(1) instanceof Integer) {
3243N/A ((List<Object>)args.get(0)).set( (Integer)args.get(1), args.get(2) );
3243N/A }
3243N/A args.clear();
3243N/A break;
3243N/A
3528N/A case opc_new:
3528N/A {
3528N/A String type = pool.getString(CONSTANT_Class, (short)i.u2At(1));
3528N/A //System.out.println("new "+type);
3528N/A switch (type) {
3528N/A case "java/lang/StringBuilder":
3528N/A jvm.push("StringBuilder");
3528N/A continue decode; // go to next instruction
3528N/A }
3528N/A break decode; // bail out
3528N/A }
3528N/A
3243N/A case opc_getstatic:
3243N/A {
3243N/A // int.class compiles to getstatic Integer.TYPE
3243N/A int fieldi = i.u2At(1);
3243N/A char mark = poolMarks[fieldi];
3243N/A //System.err.println("getstatic "+fieldi+Arrays.asList(pool.getStrings(pool.getMemberRef((short)fieldi)))+mark);
3243N/A if (mark == 'J') {
3243N/A Short[] ref = pool.getMemberRef((short) fieldi);
3243N/A String name = pool.getString(CONSTANT_Utf8, ref[1]);
3243N/A if ("TYPE".equals(name)) {
3243N/A String wrapperName = pool.getString(CONSTANT_Class, ref[0]).replace('/', '.');
3243N/A // a primitive type descriptor
3243N/A Class<?> primClass;
3243N/A try {
3243N/A primClass = (Class<?>) Class.forName(wrapperName).getField(name).get(null);
3243N/A } catch (Exception ex) {
3243N/A throw new InternalError("cannot load "+wrapperName+"."+name);
3243N/A }
3243N/A jvm.push(primClass);
3243N/A break;
3243N/A }
3243N/A }
3243N/A // unknown field; keep going...
3243N/A jvm.push(UNKNOWN_CON);
3243N/A break;
3243N/A }
3243N/A case opc_putstatic:
3243N/A {
3243N/A if (patternMark != 'I') break decode;
3243N/A jvm.pop();
3243N/A // unknown field; keep going...
3243N/A break;
3243N/A }
3243N/A
3243N/A case opc_invokestatic:
3243N/A case opc_invokevirtual:
3528N/A case opc_invokespecial:
3243N/A {
3528N/A boolean hasRecv = (bc != opc_invokestatic);
3243N/A int methi = i.u2At(1);
3243N/A char mark = poolMarks[methi];
3243N/A Short[] ref = pool.getMemberRef((short)methi);
3243N/A String type = pool.getString(CONSTANT_Utf8, ref[2]);
3243N/A //System.out.println("invoke "+pool.getString(CONSTANT_Utf8, ref[1])+" "+Arrays.asList(ref)+" : "+type);
3243N/A args = jvm.args(hasRecv, type);
3243N/A String intrinsic = null;
3243N/A Constant con;
3243N/A if (mark == 'D' || mark == 'J') {
3243N/A intrinsic = pool.getString(CONSTANT_Utf8, ref[1]);
3243N/A if (mark == 'J') {
3243N/A String cls = pool.getString(CONSTANT_Class, ref[0]);
3243N/A cls = cls.substring(1+cls.lastIndexOf('/'));
3243N/A intrinsic = cls+"."+intrinsic;
3243N/A }
3243N/A //System.out.println("recognized intrinsic "+intrinsic);
3243N/A byte refKind = -1;
3243N/A switch (intrinsic) {
3243N/A case "findGetter": refKind = REF_getField; break;
3243N/A case "findStaticGetter": refKind = REF_getStatic; break;
3243N/A case "findSetter": refKind = REF_putField; break;
3243N/A case "findStaticSetter": refKind = REF_putStatic; break;
3243N/A case "findVirtual": refKind = REF_invokeVirtual; break;
3243N/A case "findStatic": refKind = REF_invokeStatic; break;
3243N/A case "findSpecial": refKind = REF_invokeSpecial; break;
3243N/A case "findConstructor": refKind = REF_newInvokeSpecial; break;
3243N/A }
3243N/A if (refKind >= 0 && (con = parseMemberLookup(refKind, args)) != null) {
3243N/A args.clear(); args.add(con);
3243N/A continue;
3243N/A }
3243N/A }
3243N/A Method ownMethod = null;
3243N/A if (mark == 'T' || mark == 'H' || mark == 'I') {
3243N/A ownMethod = findMember(cf.methods, ref[1], ref[2]);
3243N/A }
3528N/A //if (intrinsic != null) System.out.println("intrinsic = "+intrinsic);
3243N/A switch (intrinsic == null ? "" : intrinsic) {
3243N/A case "fromMethodDescriptorString":
3243N/A con = makeMethodTypeCon(args.get(0));
3243N/A args.clear(); args.add(con);
3243N/A continue;
3243N/A case "methodType": {
3243N/A flattenVarargs(args); // there are several overloadings, some with varargs
3243N/A StringBuilder buf = new StringBuilder();
3243N/A String rtype = null;
3243N/A for (Object typeArg : args) {
3243N/A if (typeArg instanceof Class) {
3243N/A Class<?> argClass = (Class<?>) typeArg;
3243N/A if (argClass.isPrimitive()) {
3243N/A char tchar;
3243N/A switch (argClass.getName()) {
3243N/A case "void": tchar = 'V'; break;
3243N/A case "boolean": tchar = 'Z'; break;
3243N/A case "byte": tchar = 'B'; break;
3243N/A case "char": tchar = 'C'; break;
3243N/A case "short": tchar = 'S'; break;
3243N/A case "int": tchar = 'I'; break;
3243N/A case "long": tchar = 'J'; break;
3243N/A case "float": tchar = 'F'; break;
3243N/A case "double": tchar = 'D'; break;
3243N/A default: throw new InternalError(argClass.toString());
3243N/A }
3243N/A buf.append(tchar);
3243N/A } else {
3243N/A // should not happen, but...
3243N/A buf.append('L').append(argClass.getName().replace('.','/')).append(';');
3243N/A }
3243N/A } else if (typeArg instanceof Constant) {
3243N/A Constant argCon = (Constant) typeArg;
3243N/A if (argCon.tag == CONSTANT_Class) {
3243N/A String cn = pool.get(argCon.itemIndex()).itemString();
3243N/A if (cn.endsWith(";"))
3243N/A buf.append(cn);
3243N/A else
3243N/A buf.append('L').append(cn).append(';');
3243N/A } else {
3243N/A break decode;
3243N/A }
3243N/A } else {
3243N/A break decode;
3243N/A }
3243N/A if (rtype == null) {
3243N/A // first arg is treated differently
3243N/A rtype = buf.toString();
3243N/A buf.setLength(0);
3243N/A buf.append('(');
3243N/A }
3243N/A }
3243N/A buf.append(')').append(rtype);
3243N/A con = con = makeMethodTypeCon(buf.toString());
3243N/A args.clear(); args.add(con);
3243N/A continue;
3243N/A }
3243N/A case "lookup":
3243N/A case "dynamicInvoker":
3243N/A args.clear(); args.add(intrinsic);
3243N/A continue;
3243N/A case "lookupClass":
3243N/A if (args.equals(Arrays.asList("lookup"))) {
3243N/A // fold lookup().lookupClass() to the enclosing class
3243N/A args.clear(); args.add(pool.get(cf.thisc));
3243N/A continue;
3243N/A }
3243N/A break;
3907N/A case "invoke":
3243N/A case "invokeGeneric":
3243N/A case "invokeWithArguments":
3243N/A if (patternMark != 'I') break decode;
3243N/A if ("invokeWithArguments".equals(intrinsic))
3243N/A flattenVarargs(args);
3243N/A bsmArgs = new ArrayList(args);
3243N/A args.clear(); args.add("invokeGeneric");
3243N/A continue;
3243N/A case "Integer.valueOf":
3243N/A case "Float.valueOf":
3243N/A case "Long.valueOf":
3243N/A case "Double.valueOf":
3243N/A removeEmptyJVMSlots(args);
3243N/A if (args.size() == 1) {
3243N/A arg = args.remove(0);
3243N/A assert(3456 == (CONSTANT_Integer*1000 + CONSTANT_Float*100 + CONSTANT_Long*10 + CONSTANT_Double));
3243N/A if (isConstant(arg, CONSTANT_Integer + "IFLD".indexOf(intrinsic.charAt(0)))
3243N/A || arg instanceof Number) {
3243N/A args.add(arg); continue;
3243N/A }
3243N/A }
3243N/A break decode;
3528N/A case "StringBuilder.append":
3528N/A // allow calls like ("value = "+x)
3528N/A removeEmptyJVMSlots(args);
3528N/A args.subList(1, args.size()).clear();
3528N/A continue;
3528N/A case "StringBuilder.toString":
3528N/A args.clear();
3528N/A args.add(intrinsic);
3528N/A continue;
3243N/A }
3243N/A if (!hasRecv && ownMethod != null && patternMark != 0) {
3243N/A con = constants.get(ownMethod);
3243N/A if (con == null) break decode;
3243N/A args.clear(); args.add(con);
3243N/A continue;
3243N/A } else if (type.endsWith(")V")) {
3243N/A // allow calls like println("reached the pattern method")
3243N/A args.clear();
3243N/A continue;
3243N/A }
3243N/A break decode; // bail out for most calls
3243N/A }
3243N/A case opc_areturn:
3243N/A {
3243N/A ++branchCount;
3243N/A if (bsmArgs != null) {
3243N/A // parse bsmArgs as (MH, lookup, String, MT, [extra])
3243N/A Constant indyCon = makeInvokeDynamicCon(bsmArgs);
3243N/A if (indyCon != null) {
3243N/A Constant typeCon = (Constant) bsmArgs.get(3);
3243N/A indySignatures.put(m, pool.getString(typeCon.itemIndex()));
3243N/A return indyCon;
3243N/A }
3243N/A System.err.println(m+": inscrutable bsm arguments: "+bsmArgs);
3243N/A break decode; // bail out
3243N/A }
3243N/A arg = jvm.pop();
3243N/A if (branchCount == 2 && UNKNOWN_CON.equals(arg))
3243N/A break; // merge to next path
3243N/A if (isConstant(arg, wantTag))
3243N/A return (Constant) arg;
3243N/A break decode; // bail out
3243N/A }
3243N/A default:
3243N/A if (jvm.stackMotion(i.bc)) break;
3243N/A if (bc >= opc_nconst_MIN && bc <= opc_nconst_MAX)
3243N/A { jvm.push(INSTRUCTION_CONSTANTS[bc - opc_nconst_MIN]); break; }
3243N/A if (patternMark == 'I') {
3243N/A // these support caching paths in INDY_x methods
3243N/A if (bc == opc_aload || bc >= opc_aload_0 && bc <= opc_aload_MAX)
3243N/A { jvm.push(UNKNOWN_CON); break; }
3243N/A if (bc == opc_astore || bc >= opc_astore_0 && bc <= opc_astore_MAX)
3243N/A { jvm.pop(); break; }
3243N/A switch (bc) {
3243N/A case opc_getfield:
3243N/A case opc_aaload:
3243N/A jvm.push(UNKNOWN_CON); break;
3243N/A case opc_ifnull:
3243N/A case opc_ifnonnull:
3243N/A // ignore branch target
3243N/A if (++branchCount != 1) break decode;
3243N/A jvm.pop();
3243N/A break;
3243N/A case opc_checkcast:
3243N/A arg = jvm.top();
3243N/A if ("invokeWithArguments".equals(arg) ||
3243N/A "invokeGeneric".equals(arg))
3243N/A break; // assume it is a helpful cast
3243N/A break decode;
3243N/A default:
3243N/A break decode; // bail out
3243N/A }
3243N/A continue decode; // go to next instruction
3243N/A }
3243N/A break decode; // bail out
3243N/A } //end switch
3243N/A }
3243N/A System.err.println(m+": bailout on "+i+" jvm stack: "+jvm.stack);
3243N/A return null;
3243N/A }
3243N/A private final String UNKNOWN_CON = "<unknown>";
3243N/A
3243N/A private void flattenVarargs(List<Object> args) {
3243N/A int size = args.size();
3243N/A if (size > 0 && args.get(size-1) instanceof List)
3243N/A args.addAll((List<Object>) args.remove(size-1));
3243N/A }
3243N/A
3243N/A private boolean isConstant(Object x, int tag) {
3243N/A return x instanceof Constant && ((Constant)x).tag == tag;
3243N/A }
3243N/A private Constant makeMethodTypeCon(Object x) {
3243N/A short utfIndex;
3243N/A if (x instanceof String)
3243N/A utfIndex = (short) cf.pool.addConstant(CONSTANT_Utf8, x).index;
3243N/A else if (isConstant(x, CONSTANT_String))
3243N/A utfIndex = ((Constant)x).itemIndex();
3243N/A else return null;
3243N/A return cf.pool.addConstant(CONSTANT_MethodType, utfIndex);
3243N/A }
3243N/A private Constant parseMemberLookup(byte refKind, List<Object> args) {
3243N/A // E.g.: lookup().findStatic(Foo.class, "name", MethodType)
3243N/A if (args.size() != 4) return null;
3243N/A int argi = 0;
3243N/A if (!"lookup".equals(args.get(argi++))) return null;
3243N/A short refindex, cindex, ntindex, nindex, tindex;
3243N/A Object con;
3243N/A if (!isConstant(con = args.get(argi++), CONSTANT_Class)) return null;
3243N/A cindex = (short)((Constant)con).index;
3243N/A if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null;
3243N/A nindex = ((Constant)con).itemIndex();
3243N/A if (isConstant(con = args.get(argi++), CONSTANT_MethodType) ||
3243N/A isConstant(con, CONSTANT_Class)) {
3243N/A tindex = ((Constant)con).itemIndex();
3243N/A } else return null;
3243N/A ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType,
3243N/A new Short[]{ nindex, tindex }).index;
3243N/A byte reftag = CONSTANT_Method;
3243N/A if (refKind <= REF_putStatic)
3243N/A reftag = CONSTANT_Field;
3243N/A else if (refKind == REF_invokeInterface)
3243N/A reftag = CONSTANT_InterfaceMethod;
3243N/A Constant ref = cf.pool.addConstant(reftag, new Short[]{ cindex, ntindex });
3243N/A return cf.pool.addConstant(CONSTANT_MethodHandle, new Object[]{ refKind, (short)ref.index });
3243N/A }
3243N/A private Constant makeInvokeDynamicCon(List<Object> args) {
3243N/A // E.g.: MH_bsm.invokeGeneric(lookup(), "name", MethodType, "extraArg")
3243N/A removeEmptyJVMSlots(args);
3907N/A if (args.size() < 4) return null;
3243N/A int argi = 0;
3243N/A short nindex, tindex, ntindex, bsmindex;
3243N/A Object con;
3243N/A if (!isConstant(con = args.get(argi++), CONSTANT_MethodHandle)) return null;
3243N/A bsmindex = (short) ((Constant)con).index;
3243N/A if (!"lookup".equals(args.get(argi++))) return null;
3243N/A if (!isConstant(con = args.get(argi++), CONSTANT_String)) return null;
3243N/A nindex = ((Constant)con).itemIndex();
3243N/A if (!isConstant(con = args.get(argi++), CONSTANT_MethodType)) return null;
3243N/A tindex = ((Constant)con).itemIndex();
3243N/A ntindex = (short) cf.pool.addConstant(CONSTANT_NameAndType,
3243N/A new Short[]{ nindex, tindex }).index;
3907N/A List<Object> extraArgs = new ArrayList<Object>();
3907N/A if (argi < args.size()) {
3907N/A extraArgs.addAll(args.subList(argi, args.size() - 1));
3907N/A Object lastArg = args.get(args.size() - 1);
3907N/A if (lastArg instanceof List) {
3907N/A List<Object> lastArgs = (List<Object>) lastArg;
3907N/A removeEmptyJVMSlots(lastArgs);
3907N/A extraArgs.addAll(lastArgs);
3907N/A } else {
3907N/A extraArgs.add(lastArg);
3243N/A }
3243N/A }
3243N/A List<Short> extraArgIndexes = new CountedList<>(Short.class);
3243N/A for (Object x : extraArgs) {
3243N/A if (x instanceof Number) {
3243N/A Object num = null; byte numTag = 0;
3243N/A if (x instanceof Integer) { num = x; numTag = CONSTANT_Integer; }
3243N/A if (x instanceof Float) { num = Float.floatToRawIntBits((Float)x); numTag = CONSTANT_Float; }
3243N/A if (x instanceof Long) { num = x; numTag = CONSTANT_Long; }
3243N/A if (x instanceof Double) { num = Double.doubleToRawLongBits((Double)x); numTag = CONSTANT_Double; }
3243N/A if (num != null) x = cf.pool.addConstant(numTag, x);
3243N/A }
3907N/A if (!(x instanceof Constant)) {
3907N/A System.err.println("warning: unrecognized BSM argument "+x);
3907N/A return null;
3907N/A }
3243N/A extraArgIndexes.add((short) ((Constant)x).index);
3243N/A }
3243N/A List<Object[]> specs = bootstrapMethodSpecifiers(true);
3243N/A int specindex = -1;
3243N/A Object[] spec = new Object[]{ bsmindex, extraArgIndexes };
3243N/A for (Object[] spec1 : specs) {
3243N/A if (Arrays.equals(spec1, spec)) {
3243N/A specindex = specs.indexOf(spec1);
3243N/A if (verbose) System.err.println("reusing BSM specifier: "+spec1[0]+spec1[1]);
3243N/A break;
3243N/A }
3243N/A }
3243N/A if (specindex == -1) {
3243N/A specindex = (short) specs.size();
3243N/A specs.add(spec);
3243N/A if (verbose) System.err.println("adding BSM specifier: "+spec[0]+spec[1]);
3243N/A }
3243N/A return cf.pool.addConstant(CONSTANT_InvokeDynamic,
3243N/A new Short[]{ (short)specindex, ntindex });
3243N/A }
3243N/A
3243N/A List<Object[]> bootstrapMethodSpecifiers(boolean createIfNotFound) {
3243N/A Attr bsms = cf.findAttr("BootstrapMethods");
3243N/A if (bsms == null) {
3243N/A if (!createIfNotFound) return null;
3243N/A bsms = new Attr(cf, "BootstrapMethods", new byte[]{0,0});
3243N/A assert(bsms == cf.findAttr("BootstrapMethods"));
3243N/A }
3243N/A if (bsms.item instanceof byte[]) {
3243N/A // unflatten
3243N/A List<Object[]> specs = new CountedList<>(Object[].class);
3243N/A DataInputStream in = new DataInputStream(new ByteArrayInputStream((byte[]) bsms.item));
3243N/A try {
3243N/A int len = (char) in.readShort();
3243N/A for (int i = 0; i < len; i++) {
3243N/A short bsm = in.readShort();
3243N/A int argc = (char) in.readShort();
3243N/A List<Short> argv = new CountedList<>(Short.class);
3243N/A for (int j = 0; j < argc; j++)
3243N/A argv.add(in.readShort());
3243N/A specs.add(new Object[]{ bsm, argv });
3243N/A }
3243N/A } catch (IOException ex) { throw new InternalError(); }
3243N/A bsms.item = specs;
3243N/A }
3243N/A return (List<Object[]>) bsms.item;
3243N/A }
3243N/A }
3243N/A
3243N/A private DataInputStream openInput(File f) throws IOException {
3243N/A return new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
3243N/A }
3243N/A
3243N/A private DataOutputStream openOutput(File f) throws IOException {
3243N/A if (!overwrite && f.exists())
3243N/A throw new IOException("file already exists: "+f);
3243N/A ensureDirectory(f.getParentFile());
3243N/A return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
3243N/A }
3243N/A
3243N/A static byte[] readRawBytes(DataInputStream in, int size) throws IOException {
3243N/A byte[] bytes = new byte[size];
3243N/A int nr = in.read(bytes);
3243N/A if (nr != size)
3243N/A throw new InternalError("wrong size: "+nr);
3243N/A return bytes;
3243N/A }
3243N/A
3243N/A private interface Chunk {
3243N/A void readFrom(DataInputStream in) throws IOException;
3243N/A void writeTo(DataOutputStream out) throws IOException;
3243N/A }
3243N/A
3243N/A private static class CountedList<T> extends ArrayList<T> implements Chunk {
3243N/A final Class<? extends T> itemClass;
3243N/A final int rowlen;
3243N/A CountedList(Class<? extends T> itemClass, int rowlen) {
3243N/A this.itemClass = itemClass;
3243N/A this.rowlen = rowlen;
3243N/A }
3243N/A CountedList(Class<? extends T> itemClass) { this(itemClass, -1); }
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A int count = in.readUnsignedShort();
3243N/A while (size() < count) {
3243N/A if (rowlen < 0) {
3243N/A add(readInput(in, itemClass));
3243N/A } else {
3243N/A Class<?> elemClass = itemClass.getComponentType();
3243N/A Object[] row = (Object[]) java.lang.reflect.Array.newInstance(elemClass, rowlen);
3243N/A for (int i = 0; i < rowlen; i++)
3243N/A row[i] = readInput(in, elemClass);
3243N/A add(itemClass.cast(row));
3243N/A }
3243N/A }
3243N/A }
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A out.writeShort((short)size());
3243N/A for (T item : this) {
3243N/A writeOutput(out, item);
3243N/A }
3243N/A }
3243N/A }
3243N/A
3243N/A private static <T> T readInput(DataInputStream in, Class<T> dataClass) throws IOException {
3243N/A Object data;
3243N/A if (dataClass == Integer.class) {
3243N/A data = in.readInt();
3243N/A } else if (dataClass == Short.class) {
3243N/A data = in.readShort();
3243N/A } else if (dataClass == Byte.class) {
3243N/A data = in.readByte();
3243N/A } else if (dataClass == String.class) {
3243N/A data = in.readUTF();
3243N/A } else if (Chunk.class.isAssignableFrom(dataClass)) {
3243N/A T obj;
3243N/A try { obj = dataClass.newInstance(); }
3243N/A catch (Exception ex) { throw new RuntimeException(ex); }
3243N/A ((Chunk)obj).readFrom(in);
3243N/A data = obj;
3243N/A } else {
3243N/A throw new InternalError("bad input datum: "+dataClass);
3243N/A }
3243N/A return dataClass.cast(data);
3243N/A }
3243N/A private static <T> T readInput(byte[] bytes, Class<T> dataClass) {
3243N/A try {
3243N/A return readInput(new DataInputStream(new ByteArrayInputStream(bytes)), dataClass);
3243N/A } catch (IOException ex) {
3243N/A throw new InternalError();
3243N/A }
3243N/A }
3243N/A private static void readInputs(DataInputStream in, Object... data) throws IOException {
3243N/A for (Object x : data) ((Chunk)x).readFrom(in);
3243N/A }
3243N/A
3243N/A private static void writeOutput(DataOutputStream out, Object data) throws IOException {
3243N/A if (data == null) {
3243N/A return;
3243N/A } if (data instanceof Integer) {
3243N/A out.writeInt((Integer)data);
3243N/A } else if (data instanceof Long) {
3243N/A out.writeLong((Long)data);
3243N/A } else if (data instanceof Short) {
3243N/A out.writeShort((Short)data);
3243N/A } else if (data instanceof Byte) {
3243N/A out.writeByte((Byte)data);
3243N/A } else if (data instanceof String) {
3243N/A out.writeUTF((String)data);
3243N/A } else if (data instanceof byte[]) {
3243N/A out.write((byte[])data);
3243N/A } else if (data instanceof Object[]) {
3243N/A for (Object x : (Object[]) data)
3243N/A writeOutput(out, x);
3243N/A } else if (data instanceof Chunk) {
3243N/A Chunk x = (Chunk) data;
3243N/A x.writeTo(out);
3243N/A } else if (data instanceof List) {
3243N/A for (Object x : (List<?>) data)
3243N/A writeOutput(out, x);
3243N/A } else {
3243N/A throw new InternalError("bad output datum: "+data+" : "+data.getClass().getName());
3243N/A }
3243N/A }
3243N/A private static void writeOutputs(DataOutputStream out, Object... data) throws IOException {
3243N/A for (Object x : data) writeOutput(out, x);
3243N/A }
3243N/A
3243N/A public static abstract class Outer {
3243N/A public abstract List<? extends Inner> inners();
3243N/A protected void linkInners() {
3243N/A for (Inner i : inners()) {
3243N/A i.linkOuter(this);
3243N/A if (i instanceof Outer)
3243N/A ((Outer)i).linkInners();
3243N/A }
3243N/A }
3243N/A public <T extends Outer> T outer(Class<T> c) {
3243N/A for (Outer walk = this;; walk = ((Inner)walk).outer()) {
3243N/A if (c.isInstance(walk))
3243N/A return c.cast(walk);
3243N/A //if (!(walk instanceof Inner)) return null;
3243N/A }
3243N/A }
3243N/A
3243N/A public abstract List<Attr> attrs();
3243N/A public Attr findAttr(String name) {
3243N/A return findAttr(outer(ClassFile.class).pool.stringIndex(name, false));
3243N/A }
3243N/A public Attr findAttr(int name) {
3243N/A if (name == 0) return null;
3243N/A for (Attr a : attrs()) {
3243N/A if (a.name == name) return a;
3243N/A }
3243N/A return null;
3243N/A }
3243N/A }
3243N/A public interface Inner { Outer outer(); void linkOuter(Outer o); }
3243N/A public static abstract class InnerOuter extends Outer implements Inner {
3243N/A public Outer outer;
3243N/A public Outer outer() { return outer; }
3243N/A public void linkOuter(Outer o) { assert(outer == null); outer = o; }
3243N/A }
3243N/A public static class Constant<T> implements Chunk {
3243N/A public final byte tag;
3243N/A public final T item;
3243N/A public final int index;
3243N/A public Constant(int index, byte tag, T item) {
3243N/A this.index = index;
3243N/A this.tag = tag;
3243N/A this.item = item;
3243N/A }
3243N/A public Constant checkTag(byte tag) {
3243N/A if (this.tag != tag) throw new InternalError(this.toString());
3243N/A return this;
3243N/A }
3243N/A public String itemString() { return (String)item; }
3243N/A public Short itemIndex() { return (Short)item; }
3243N/A public Short[] itemIndexes() { return (Short[])item; }
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A throw new InternalError("do not call");
3243N/A }
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A writeOutputs(out, tag, item);
3243N/A }
3243N/A public boolean equals(Object x) { return (x instanceof Constant && equals((Constant)x)); }
3243N/A public boolean equals(Constant that) {
3243N/A return (this.tag == that.tag && this.itemAsComparable().equals(that.itemAsComparable()));
3243N/A }
3243N/A public int hashCode() { return (tag * 31) + this.itemAsComparable().hashCode(); }
3243N/A public Object itemAsComparable() {
3243N/A switch (tag) {
3243N/A case CONSTANT_Double: return Double.longBitsToDouble((Long)item);
3243N/A case CONSTANT_Float: return Float.intBitsToFloat((Integer)item);
3243N/A }
3243N/A return (item instanceof Object[] ? Arrays.asList((Object[])item) : item);
3243N/A }
3243N/A public String toString() {
3243N/A String itstr = String.valueOf(itemAsComparable());
3243N/A return (index + ":" + tagName(tag) + (itstr.startsWith("[")?"":"=") + itstr);
3243N/A }
3243N/A private static String[] TAG_NAMES;
3243N/A public static String tagName(byte tag) { // used for error messages
3243N/A if (TAG_NAMES == null)
3243N/A TAG_NAMES = ("None Utf8 Unicode Integer Float Long Double Class String"
3243N/A +" Fieldref Methodref InterfaceMethodref NameAndType #13 #14"
3243N/A +" MethodHandle MethodType InvokeDynamic#17 InvokeDynamic").split(" ");
3243N/A if ((tag & 0xFF) >= TAG_NAMES.length) return "#"+(tag & 0xFF);
3243N/A return TAG_NAMES[tag & 0xFF];
3243N/A }
3243N/A }
3243N/A
3243N/A public static class Pool extends CountedList<Constant> implements Chunk {
3243N/A private Map<String,Short> strings = new TreeMap<>();
3243N/A
3243N/A public Pool() {
3243N/A super(Constant.class);
3243N/A }
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A int count = in.readUnsignedShort();
3243N/A add(null); // always ignore first item
3243N/A while (size() < count) {
3243N/A readConstant(in);
3243N/A }
3243N/A }
3243N/A public <T> Constant<T> addConstant(byte tag, T item) {
3243N/A Constant<T> con = new Constant<>(size(), tag, item);
3243N/A int idx = indexOf(con);
3243N/A if (idx >= 0) return get(idx);
3243N/A add(con);
3243N/A if (tag == CONSTANT_Utf8) strings.put((String)item, (short) con.index);
3243N/A return con;
3243N/A }
3243N/A private void readConstant(DataInputStream in) throws IOException {
3243N/A byte tag = in.readByte();
3243N/A int index = size();
3243N/A Object arg;
3243N/A switch (tag) {
3243N/A case CONSTANT_Utf8:
3243N/A arg = in.readUTF();
3243N/A strings.put((String) arg, (short) size());
3243N/A break;
3243N/A case CONSTANT_Integer:
3243N/A case CONSTANT_Float:
3243N/A arg = in.readInt(); break;
3243N/A case CONSTANT_Long:
3243N/A case CONSTANT_Double:
3243N/A add(new Constant(index, tag, in.readLong()));
3243N/A add(null);
3243N/A return;
3243N/A case CONSTANT_Class:
3243N/A case CONSTANT_String:
3243N/A arg = in.readShort(); break;
3243N/A case CONSTANT_Field:
3243N/A case CONSTANT_Method:
3243N/A case CONSTANT_InterfaceMethod:
3243N/A case CONSTANT_NameAndType:
3243N/A case CONSTANT_InvokeDynamic:
3243N/A // read an ordered pair
3243N/A arg = new Short[] { in.readShort(), in.readShort() };
3243N/A break;
3243N/A case CONSTANT_MethodHandle:
3243N/A // read an ordered pair; first part is a u1 (not u2)
3243N/A arg = new Object[] { in.readByte(), in.readShort() };
3243N/A break;
3243N/A case CONSTANT_MethodType:
3243N/A arg = in.readShort(); break;
3243N/A default:
3243N/A throw new InternalError("bad CP tag "+tag);
3243N/A }
3243N/A add(new Constant(index, tag, arg));
3243N/A }
3243N/A
3243N/A // Access:
3243N/A public Constant get(int index) {
3243N/A // extra 1-bits get into the shorts
3243N/A return super.get((char) index);
3243N/A }
3243N/A String getString(byte tag, short index) {
3243N/A get(index).checkTag(tag);
3243N/A return getString(index);
3243N/A }
3243N/A String getString(short index) {
3243N/A Object v = get(index).item;
3243N/A if (v instanceof Short)
3243N/A v = get((Short)v).checkTag(CONSTANT_Utf8).item;
3243N/A return (String) v;
3243N/A }
3243N/A String[] getStrings(Short[] indexes) {
3243N/A String[] res = new String[indexes.length];
3243N/A for (int i = 0; i < indexes.length; i++)
3243N/A res[i] = getString(indexes[i]);
3243N/A return res;
3243N/A }
3243N/A int stringIndex(String name, boolean createIfNotFound) {
3243N/A Short x = strings.get(name);
3243N/A if (x != null) return (char)(int) x;
3243N/A if (!createIfNotFound) return 0;
3243N/A return addConstant(CONSTANT_Utf8, name).index;
3243N/A }
3243N/A Short[] getMemberRef(short index) {
3243N/A Short[] cls_nnt = get(index).itemIndexes();
3243N/A Short[] name_type = get(cls_nnt[1]).itemIndexes();
3243N/A return new Short[]{ cls_nnt[0], name_type[0], name_type[1] };
3243N/A }
3243N/A }
3243N/A
3243N/A public class ClassFile extends Outer implements Chunk {
3243N/A ClassFile(File f) throws IOException {
3243N/A DataInputStream in = openInput(f);
3243N/A try {
3243N/A readFrom(in);
3243N/A } finally {
3243N/A if (in != null) in.close();
3243N/A }
3243N/A }
3243N/A
3243N/A public int magic, version; // <min:maj>
3243N/A public final Pool pool = new Pool();
3243N/A public short access, thisc, superc;
3243N/A public final List<Short> interfaces = new CountedList<>(Short.class);
3243N/A public final List<Field> fields = new CountedList<>(Field.class);
3243N/A public final List<Method> methods = new CountedList<>(Method.class);
3243N/A public final List<Attr> attrs = new CountedList<>(Attr.class);
3243N/A
3243N/A public final void readFrom(DataInputStream in) throws IOException {
3243N/A magic = in.readInt(); version = in.readInt();
3243N/A if (magic != 0xCAFEBABE) throw new IOException("bad magic number");
3243N/A pool.readFrom(in);
3243N/A Code_index = pool.stringIndex("Code", false);
3243N/A access = in.readShort(); thisc = in.readShort(); superc = in.readShort();
3243N/A readInputs(in, interfaces, fields, methods, attrs);
3243N/A if (in.read() >= 0) throw new IOException("junk after end of file");
3243N/A linkInners();
3243N/A }
3243N/A
3243N/A void writeTo(File f) throws IOException {
3243N/A DataOutputStream out = openOutput(f);
3243N/A try {
3243N/A writeTo(out);
3243N/A } finally {
3243N/A out.close();
3243N/A }
3243N/A }
3243N/A
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A writeOutputs(out, magic, version, pool,
3243N/A access, thisc, superc, interfaces,
3243N/A fields, methods, attrs);
3243N/A }
3243N/A
3243N/A public byte[] toByteArray() {
3243N/A try {
3243N/A ByteArrayOutputStream buf = new ByteArrayOutputStream();
3243N/A writeTo(new DataOutputStream(buf));
3243N/A return buf.toByteArray();
3243N/A } catch (IOException ex) {
3243N/A throw new InternalError();
3243N/A }
3243N/A }
3243N/A
3243N/A public List<Inner> inners() {
3243N/A List<Inner> inns = new ArrayList<>();
3243N/A inns.addAll(fields); inns.addAll(methods); inns.addAll(attrs);
3243N/A return inns;
3243N/A }
3243N/A public List<Attr> attrs() { return attrs; }
3243N/A
3243N/A // derived stuff:
3243N/A public String nameString() { return pool.getString(CONSTANT_Class, thisc); }
3243N/A int Code_index;
3243N/A }
3243N/A
3243N/A private static <T extends Member> T findMember(List<T> mems, int name, int type) {
3243N/A if (name == 0 || type == 0) return null;
3243N/A for (T m : mems) {
3243N/A if (m.name == name && m.type == type) return m;
3243N/A }
3243N/A return null;
3243N/A }
3243N/A
3243N/A public static class Member extends InnerOuter implements Chunk {
3243N/A public short access, name, type;
3243N/A public final List<Attr> attrs = new CountedList<>(Attr.class);
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A access = in.readShort(); name = in.readShort(); type = in.readShort();
3243N/A readInputs(in, attrs);
3243N/A }
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A writeOutputs(out, access, name, type, attrs);
3243N/A }
3243N/A public List<Attr> inners() { return attrs; }
3243N/A public List<Attr> attrs() { return attrs; }
3243N/A public ClassFile outer() { return (ClassFile) outer; }
3243N/A public String nameString() { return outer().pool.getString(CONSTANT_Utf8, name); }
3243N/A public String typeString() { return outer().pool.getString(CONSTANT_Utf8, type); }
3243N/A public String toString() {
3243N/A if (outer == null) return super.toString();
3243N/A return nameString() + (this instanceof Method ? "" : ":")
3243N/A + simplifyType(typeString());
3243N/A }
3243N/A }
3243N/A public static class Field extends Member {
3243N/A }
3243N/A public static class Method extends Member {
3243N/A public Code code() {
3243N/A Attr a = findAttr("Code");
3243N/A if (a == null) return null;
3243N/A return (Code) a.item;
3243N/A }
3243N/A public Instruction instructions() {
3243N/A Code code = code();
3243N/A if (code == null) return null;
3243N/A return code.instructions();
3243N/A }
3243N/A }
3243N/A
3243N/A public static class Attr extends InnerOuter implements Chunk {
3243N/A public short name;
3243N/A public int size = -1; // no pre-declared size
3243N/A public Object item;
3243N/A
3243N/A public Attr() {}
3243N/A public Attr(Outer outer, String name, Object item) {
3243N/A ClassFile cf = outer.outer(ClassFile.class);
3243N/A linkOuter(outer);
3243N/A this.name = (short) cf.pool.stringIndex(name, true);
3243N/A this.item = item;
3243N/A outer.attrs().add(this);
3243N/A }
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A name = in.readShort();
3243N/A size = in.readInt();
3243N/A item = readRawBytes(in, size);
3243N/A }
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A out.writeShort(name);
3243N/A // write the 4-byte size header and then the contents:
3243N/A byte[] bytes;
3243N/A int trueSize;
3243N/A if (item instanceof byte[]) {
3243N/A bytes = (byte[]) item;
3243N/A out.writeInt(trueSize = bytes.length);
3243N/A out.write(bytes);
3243N/A } else {
3243N/A trueSize = flatten(out);
3528N/A //if (!(item instanceof Code)) System.err.println("wrote complex attr name="+(int)(char)name+" size="+trueSize+" data="+Arrays.toString(flatten()));
3243N/A }
3243N/A if (trueSize != size && size >= 0)
3243N/A System.err.println("warning: attribute size changed "+size+" to "+trueSize);
3243N/A }
3243N/A public void linkOuter(Outer o) {
3243N/A super.linkOuter(o);
3243N/A if (item instanceof byte[] &&
3243N/A outer instanceof Method &&
3243N/A ((Method)outer).outer().Code_index == name) {
3243N/A item = readInput((byte[])item, Code.class);
3243N/A }
3243N/A }
3243N/A public List<Inner> inners() {
3243N/A if (item instanceof Inner)
3243N/A return Collections.nCopies(1, (Inner)item);
3243N/A return Collections.emptyList();
3243N/A }
3243N/A public List<Attr> attrs() { return null; } // Code overrides this
3243N/A public byte[] flatten() {
3528N/A ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
3243N/A flatten(buf);
3243N/A return buf.toByteArray();
3243N/A }
3243N/A public int flatten(DataOutputStream out) throws IOException {
3243N/A ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
3243N/A int trueSize = flatten(buf);
3243N/A out.writeInt(trueSize);
3243N/A buf.writeTo(out);
3243N/A return trueSize;
3243N/A }
3243N/A private int flatten(ByteArrayOutputStream buf) {
3243N/A try {
3243N/A writeOutput(new DataOutputStream(buf), item);
3243N/A return buf.size();
3243N/A } catch (IOException ex) {
3243N/A throw new InternalError();
3243N/A }
3243N/A }
3243N/A public String nameString() {
3243N/A ClassFile cf = outer(ClassFile.class);
3243N/A if (cf == null) return "#"+name;
3243N/A return cf.pool.getString(name);
3243N/A }
3243N/A public String toString() {
3243N/A return nameString()+(size < 0 ? "=" : "["+size+"]=")+item;
3243N/A }
3243N/A }
3243N/A
3243N/A public static class Code extends InnerOuter implements Chunk {
3243N/A public short stacks, locals;
3243N/A public byte[] bytes;
3243N/A public final List<Short[]> etable = new CountedList<>(Short[].class, 4);
3243N/A public final List<Attr> attrs = new CountedList<>(Attr.class);
3243N/A // etable[N] = (N)*{ startpc, endpc, handlerpc, catchtype }
3243N/A public void readFrom(DataInputStream in) throws IOException {
3243N/A stacks = in.readShort(); locals = in.readShort();
3243N/A bytes = readRawBytes(in, in.readInt());
3243N/A readInputs(in, etable, attrs);
3243N/A }
3243N/A public void writeTo(DataOutputStream out) throws IOException {
3243N/A writeOutputs(out, stacks, locals, bytes.length, bytes, etable, attrs);
3243N/A }
3243N/A public List<Attr> inners() { return attrs; }
3243N/A public List<Attr> attrs() { return attrs; }
3243N/A public Instruction instructions() {
3243N/A return new Instruction(bytes, 0);
3243N/A }
3243N/A }
3243N/A
3243N/A // lots of constants
3243N/A private static final byte
3243N/A CONSTANT_Utf8 = 1,
3243N/A CONSTANT_Integer = 3,
3243N/A CONSTANT_Float = 4,
3243N/A CONSTANT_Long = 5,
3243N/A CONSTANT_Double = 6,
3243N/A CONSTANT_Class = 7,
3243N/A CONSTANT_String = 8,
3243N/A CONSTANT_Field = 9,
3243N/A CONSTANT_Method = 10,
3243N/A CONSTANT_InterfaceMethod = 11,
3243N/A CONSTANT_NameAndType = 12,
3243N/A CONSTANT_MethodHandle = 15, // JSR 292
3243N/A CONSTANT_MethodType = 16, // JSR 292
3243N/A CONSTANT_InvokeDynamic = 18; // JSR 292
3243N/A private static final byte
3243N/A REF_getField = 1,
3243N/A REF_getStatic = 2,
3243N/A REF_putField = 3,
3243N/A REF_putStatic = 4,
3243N/A REF_invokeVirtual = 5,
3243N/A REF_invokeStatic = 6,
3243N/A REF_invokeSpecial = 7,
3243N/A REF_newInvokeSpecial = 8,
3243N/A REF_invokeInterface = 9;
3243N/A
3243N/A private static final int
3243N/A opc_nop = 0,
3243N/A opc_aconst_null = 1,
3243N/A opc_nconst_MIN = 2, // iconst_m1
3243N/A opc_nconst_MAX = 15, // dconst_1
3243N/A opc_bipush = 16,
3243N/A opc_sipush = 17,
3243N/A opc_ldc = 18,
3243N/A opc_ldc_w = 19,
3243N/A opc_ldc2_w = 20,
3243N/A opc_aload = 25,
3243N/A opc_aload_0 = 42,
3243N/A opc_aload_MAX = 45,
3243N/A opc_aaload = 50,
3243N/A opc_astore = 58,
3243N/A opc_astore_0 = 75,
3243N/A opc_astore_MAX = 78,
3243N/A opc_aastore = 83,
3243N/A opc_pop = 87,
3243N/A opc_pop2 = 88,
3243N/A opc_dup = 89,
3243N/A opc_dup_x1 = 90,
3243N/A opc_dup_x2 = 91,
3243N/A opc_dup2 = 92,
3243N/A opc_dup2_x1 = 93,
3243N/A opc_dup2_x2 = 94,
3243N/A opc_swap = 95,
3243N/A opc_tableswitch = 170,
3243N/A opc_lookupswitch = 171,
3243N/A opc_areturn = 176,
3243N/A opc_getstatic = 178,
3243N/A opc_putstatic = 179,
3243N/A opc_getfield = 180,
3243N/A opc_putfield = 181,
3243N/A opc_invokevirtual = 182,
3243N/A opc_invokespecial = 183,
3243N/A opc_invokestatic = 184,
3243N/A opc_invokeinterface = 185,
3243N/A opc_invokedynamic = 186,
3528N/A opc_new = 187,
3243N/A opc_anewarray = 189,
3243N/A opc_checkcast = 192,
3243N/A opc_ifnull = 198,
3243N/A opc_ifnonnull = 199,
3243N/A opc_wide = 196;
3243N/A
3243N/A private static final Object[] INSTRUCTION_CONSTANTS = {
3243N/A -1, 0, 1, 2, 3, 4, 5, 0L, 1L, 0.0F, 1.0F, 2.0F, 0.0D, 1.0D
3243N/A };
3243N/A
3243N/A private static final String INSTRUCTION_FORMATS =
3243N/A "nop$ aconst_null$L iconst_m1$I iconst_0$I iconst_1$I "+
3243N/A "iconst_2$I iconst_3$I iconst_4$I iconst_5$I lconst_0$J_ "+
3243N/A "lconst_1$J_ fconst_0$F fconst_1$F fconst_2$F dconst_0$D_ "+
3243N/A "dconst_1$D_ bipush=bx$I sipush=bxx$I ldc=bk$X ldc_w=bkk$X "+
3243N/A "ldc2_w=bkk$X_ iload=bl/wbll$I lload=bl/wbll$J_ fload=bl/wbll$F "+
3243N/A "dload=bl/wbll$D_ aload=bl/wbll$L iload_0$I iload_1$I "+
3243N/A "iload_2$I iload_3$I lload_0$J_ lload_1$J_ lload_2$J_ "+
3243N/A "lload_3$J_ fload_0$F fload_1$F fload_2$F fload_3$F dload_0$D_ "+
3243N/A "dload_1$D_ dload_2$D_ dload_3$D_ aload_0$L aload_1$L "+
3243N/A "aload_2$L aload_3$L iaload$LI$I laload$LI$J_ faload$LI$F "+
3243N/A "daload$LI$D_ aaload$LI$L baload$LI$I caload$LI$I saload$LI$I "+
3243N/A "istore=bl/wbll$I$ lstore=bl/wbll$J_$ fstore=bl/wbll$F$ "+
3243N/A "dstore=bl/wbll$D_$ astore=bl/wbll$L$ istore_0$I$ istore_1$I$ "+
3243N/A "istore_2$I$ istore_3$I$ lstore_0$J_$ lstore_1$J_$ "+
3243N/A "lstore_2$J_$ lstore_3$J_$ fstore_0$F$ fstore_1$F$ fstore_2$F$ "+
3243N/A "fstore_3$F$ dstore_0$D_$ dstore_1$D_$ dstore_2$D_$ "+
3243N/A "dstore_3$D_$ astore_0$L$ astore_1$L$ astore_2$L$ astore_3$L$ "+
3243N/A "iastore$LII$ lastore$LIJ_$ fastore$LIF$ dastore$LID_$ "+
3243N/A "aastore$LIL$ bastore$LII$ castore$LII$ sastore$LII$ pop$X$ "+
3243N/A "pop2$XX$ dup$X$XX dup_x1$XX$XXX dup_x2$XXX$XXXX dup2$XX$XXXX "+
3243N/A "dup2_x1$XXX$XXXXX dup2_x2$XXXX$XXXXXX swap$XX$XX "+
3243N/A "iadd$II$I ladd$J_J_$J_ fadd$FF$F dadd$D_D_$D_ isub$II$I "+
3243N/A "lsub$J_J_$J_ fsub$FF$F dsub$D_D_$D_ imul$II$I lmul$J_J_$J_ "+
3243N/A "fmul$FF$F dmul$D_D_$D_ idiv$II$I ldiv$J_J_$J_ fdiv$FF$F "+
3243N/A "ddiv$D_D_$D_ irem$II$I lrem$J_J_$J_ frem$FF$F drem$D_D_$D_ "+
3243N/A "ineg$I$I lneg$J_$J_ fneg$F$F dneg$D_$D_ ishl$II$I lshl$J_I$J_ "+
3243N/A "ishr$II$I lshr$J_I$J_ iushr$II$I lushr$J_I$J_ iand$II$I "+
3243N/A "land$J_J_$J_ ior$II$I lor$J_J_$J_ ixor$II$I lxor$J_J_$J_ "+
3243N/A "iinc=blx/wbllxx$ i2l$I$J_ i2f$I$F i2d$I$D_ l2i$J_$I l2f$J_$F "+
3243N/A "l2d$J_$D_ f2i$F$I f2l$F$J_ f2d$F$D_ d2i$D_$I d2l$D_$J_ "+
3243N/A "d2f$D_$F i2b$I$I i2c$I$I i2s$I$I lcmp fcmpl fcmpg dcmpl dcmpg "+
3243N/A "ifeq=boo ifne=boo iflt=boo ifge=boo ifgt=boo ifle=boo "+
3243N/A "if_icmpeq=boo if_icmpne=boo if_icmplt=boo if_icmpge=boo "+
3243N/A "if_icmpgt=boo if_icmple=boo if_acmpeq=boo if_acmpne=boo "+
3243N/A "goto=boo jsr=boo ret=bl/wbll tableswitch=* lookupswitch=* "+
3243N/A "ireturn lreturn freturn dreturn areturn return "+
3243N/A "getstatic=bkf$Q putstatic=bkf$Q$ getfield=bkf$L$Q "+
3243N/A "putfield=bkf$LQ$ invokevirtual=bkm$LQ$Q "+
3243N/A "invokespecial=bkm$LQ$Q invokestatic=bkm$Q$Q "+
3243N/A "invokeinterface=bkixx$LQ$Q invokedynamic=bkd__$Q$Q new=bkc$L "+
3243N/A "newarray=bx$I$L anewarray=bkc$I$L arraylength$L$I athrow "+
3243N/A "checkcast=bkc$L$L instanceof=bkc$L$I monitorenter$L "+
3243N/A "monitorexit$L wide=* multianewarray=bkcx ifnull=boo "+
3243N/A "ifnonnull=boo goto_w=boooo jsr_w=boooo ";
3243N/A private static final String[] INSTRUCTION_NAMES;
3243N/A private static final String[] INSTRUCTION_POPS;
3243N/A private static final int[] INSTRUCTION_INFO;
3243N/A static {
3243N/A String[] insns = INSTRUCTION_FORMATS.split(" ");
3243N/A assert(insns[opc_lookupswitch].startsWith("lookupswitch"));
3243N/A assert(insns[opc_tableswitch].startsWith("tableswitch"));
3243N/A assert(insns[opc_wide].startsWith("wide"));
3243N/A assert(insns[opc_invokedynamic].startsWith("invokedynamic"));
3243N/A int[] info = new int[256];
3243N/A String[] names = new String[256];
3243N/A String[] pops = new String[256];
3243N/A for (int i = 0; i < insns.length; i++) {
3243N/A String insn = insns[i];
3243N/A int dl = insn.indexOf('$');
3243N/A if (dl > 0) {
3243N/A String p = insn.substring(dl+1);
3243N/A if (p.indexOf('$') < 0) p = "$" + p;
3243N/A pops[i] = p;
3243N/A insn = insn.substring(0, dl);
3243N/A }
3243N/A int eq = insn.indexOf('=');
3243N/A if (eq < 0) {
3243N/A info[i] = 1;
3243N/A names[i] = insn;
3243N/A continue;
3243N/A }
3243N/A names[i] = insn.substring(0, eq);
3243N/A String fmt = insn.substring(eq+1);
3243N/A if (fmt.equals("*")) {
3243N/A info[i] = 0;
3243N/A continue;
3243N/A }
3243N/A int sl = fmt.indexOf('/');
3243N/A if (sl < 0) {
3243N/A info[i] = (char) fmt.length();
3243N/A } else {
3243N/A String wfmt = fmt.substring(sl+1);
3243N/A fmt = fmt.substring(0, sl);
3243N/A info[i] = (char)( fmt.length() + (wfmt.length() * 16) );
3243N/A }
3243N/A }
3243N/A INSTRUCTION_INFO = info;
3243N/A INSTRUCTION_NAMES = names;
3243N/A INSTRUCTION_POPS = pops;
3243N/A }
3243N/A
3243N/A public static class Instruction implements Cloneable {
3243N/A byte[] codeBase;
3243N/A int pc;
3243N/A int bc;
3243N/A int info;
3243N/A int wide;
3243N/A int len;
3243N/A Instruction(byte[] codeBase, int pc) {
3243N/A this.codeBase = codeBase;
3243N/A init(pc);
3243N/A }
3243N/A public Instruction clone() {
3243N/A try {
3243N/A return (Instruction) super.clone();
3243N/A } catch (CloneNotSupportedException ex) {
3243N/A throw new InternalError();
3243N/A }
3243N/A }
3243N/A private Instruction init(int pc) {
3243N/A this.pc = pc;
3243N/A this.bc = codeBase[pc] & 0xFF;
3243N/A this.info = INSTRUCTION_INFO[bc];
3243N/A this.wide = 0;
3243N/A this.len = (info & 0x0F);
3243N/A if (len == 0)
3243N/A computeLength();
3243N/A return this;
3243N/A }
3243N/A Instruction next() {
3243N/A if (len == 0 && bc != 0) throw new InternalError();
3243N/A int npc = pc + len;
3243N/A if (npc == codeBase.length)
3243N/A return null;
3243N/A return init(npc);
3243N/A }
3243N/A void forceNext(int newLen) {
3243N/A bc = opc_nop;
3243N/A len = newLen;
3243N/A }
3243N/A
3243N/A public String toString() {
3243N/A StringBuilder buf = new StringBuilder();
3243N/A buf.append(pc).append(":").append(INSTRUCTION_NAMES[bc]);
3243N/A switch (len) {
3243N/A case 3: buf.append(" ").append(u2At(1)); break;
3243N/A case 5: buf.append(" ").append(u2At(1)).append(" ").append(u2At(3)); break;
3243N/A default: for (int i = 1; i < len; i++) buf.append(" ").append(u1At(1));
3243N/A }
3243N/A return buf.toString();
3243N/A }
3243N/A
3243N/A // these are the hard parts
3243N/A private void computeLength() {
3243N/A int cases;
3243N/A switch (bc) {
3243N/A case opc_wide:
3243N/A bc = codeBase[pc + 1];
3243N/A info = INSTRUCTION_INFO[bc];
3243N/A len = ((info >> 4) & 0x0F);
3243N/A if (len == 0) throw new RuntimeException("misplaced wide bytecode: "+bc);
3243N/A return;
3243N/A
3243N/A case opc_tableswitch:
3243N/A cases = (u4At(alignedIntOffset(2)) - u4At(alignedIntOffset(1)) + 1);
3243N/A len = alignedIntOffset(3 + cases*1);
3243N/A return;
3243N/A
3243N/A case opc_lookupswitch:
3243N/A cases = u4At(alignedIntOffset(1));
3243N/A len = alignedIntOffset(2 + cases*2);
3243N/A return;
3243N/A
3243N/A default:
3243N/A throw new RuntimeException("unknown bytecode: "+bc);
3243N/A }
3243N/A }
3243N/A // switch code
3243N/A // clget the Nth int (where 0 is the first after the opcode itself)
3243N/A public int alignedIntOffset(int n) {
3243N/A int pos = pc + 1;
3243N/A pos += ((-pos) & 0x03); // align it
3243N/A pos += (n * 4);
3243N/A return pos - pc;
3243N/A }
3243N/A public int u1At(int pos) {
3243N/A return (codeBase[pc+pos] & 0xFF);
3243N/A }
3243N/A public int u2At(int pos) {
3243N/A return (u1At(pos+0)<<8) + u1At(pos+1);
3243N/A }
3243N/A public int u4At(int pos) {
3243N/A return (u2At(pos+0)<<16) + u2At(pos+2);
3243N/A }
3243N/A public void u1AtPut(int pos, int x) {
3243N/A codeBase[pc+pos] = (byte)x;
3243N/A }
3243N/A public void u2AtPut(int pos, int x) {
3243N/A codeBase[pc+pos+0] = (byte)(x >> 8);
3243N/A codeBase[pc+pos+1] = (byte)(x >> 0);
3243N/A }
3243N/A }
3243N/A
3243N/A static String simplifyType(String type) {
3243N/A String simpleType = OBJ_SIGNATURE.matcher(type).replaceAll("L");
3243N/A assert(simpleType.matches("^\\([A-Z]*\\)[A-Z]$"));
3243N/A // change (DD)D to (D_D_)D_
3243N/A simpleType = WIDE_SIGNATURE.matcher(simpleType).replaceAll("\\0_");
3243N/A return simpleType;
3243N/A }
3243N/A static int argsize(String type) {
3243N/A return simplifyType(type).length()-3;
3243N/A }
3243N/A private static final Pattern OBJ_SIGNATURE = Pattern.compile("\\[*L[^;]*;|\\[+[A-Z]");
3243N/A private static final Pattern WIDE_SIGNATURE = Pattern.compile("[JD]");
3243N/A}