/* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- import xmlkit.XMLKit.Element; import java.util.HashMap; import java.util.Map; /* * @author jrose */ public abstract class InstructionSyntax { InstructionSyntax() { } static final String[] bcNames; static final String[] bcFormats; static final String[] bcWideFormats; static final HashMap<String, Integer> bcCodes; static final HashMap<String, Element> abbrevs; static final HashMap<Element, String> rabbrevs; static { TokenList tl = new TokenList( " nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3" + " iconst_4 iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2" + " dconst_0 dconst_1 bipush/s sipush/ss ldc/k ldc_w/kk ldc2_w/kk" + " iload/wl lload/wl fload/wl dload/wl aload/wl iload_0 iload_1" + " iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1" + " fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1" + " aload_2 aload_3 iaload laload faload daload aaload baload caload" + " saload istore/wl lstore/wl fstore/wl dstore/wl astore/wl" + " istore_0 istore_1 istore_2 istore_3 lstore_0 lstore_1 lstore_2" + " lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 dstore_1" + " dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore" + " lastore fastore dastore aastore bastore castore sastore pop pop2" + " dup dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd" + " isub lsub fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem" + " lrem frem drem ineg lneg fneg dneg ishl lshl ishr lshr iushr" + " lushr iand land ior lor ixor lxor iinc/wls i2l i2f i2d l2i l2f" + " l2d f2i f2l f2d d2i d2l d2f i2b i2c i2s lcmp fcmpl fcmpg dcmpl" + " dcmpg ifeq/oo ifne/oo iflt/oo ifge/oo ifgt/oo ifle/oo" + " if_icmpeq/oo if_icmpne/oo if_icmplt/oo if_icmpge/oo if_icmpgt/oo" + " if_icmple/oo if_acmpeq/oo if_acmpne/oo goto/oo jsr/oo ret/wl" + " tableswitch/oooot lookupswitch/oooot ireturn lreturn freturn dreturn areturn" + " return getstatic/kf putstatic/kf getfield/kf putfield/kf" + " invokevirtual/km invokespecial/km invokestatic/km" + " invokeinterface/knxx xxxunusedxxx new/kc newarray/x anewarray/kc" + " arraylength athrow checkcast/kc instanceof/kc monitorenter" + " monitorexit wide multianewarray/kcx ifnull/oo ifnonnull/oo" + " goto_w/oooo jsr_w/oooo"); assert (tl.size() == 202); // this many instructions! HashMap<String, Integer> map = new HashMap<String, Integer>(tl.size()); String[] names = tl.toArray(new String[tl.size()]); String[] formats = new String[names.length]; String[] wideFormats = new String[names.length]; StringBuilder sbuf = new StringBuilder(); sbuf.append('i'); // all op formats begin with "i" int i = 0; for (String ins : names) { assert (ins == ins.trim()); // no whitespace int sfx = ins.indexOf('/'); String format = "i"; String wideFormat = null; if (sfx >= 0) { format = ins.substring(sfx + 1); ins = ins.substring(0, sfx); if (format.charAt(0) == 'w') { format = format.substring(1); sbuf.setLength(1); for (int j = 0; j < format.length(); j++) { // double everything except the initial 'i' sbuf.append(format.charAt(j)); sbuf.append(format.charAt(j)); } wideFormat = sbuf.toString().intern(); } sbuf.setLength(1); sbuf.append(format); format = sbuf.toString().intern(); } ins = ins.intern(); names[i] = ins; formats[i] = format; wideFormats[i] = (wideFormat != null) ? wideFormat : format; //System.out.println(ins+" "+format+" "+wideFormat); map.put(ins, i++); } //map = Collections.unmodifiableMap(map); HashMap<String, Element> abb = new HashMap<String, Element>(tl.size() / 2); abb.put("iconst_m1", new Element("bipush", "num", "-1")); for (String ins : names) { int sfx = ins.indexOf('_'); if (sfx >= 0 && Character.isDigit(ins.charAt(sfx + 1))) { String pfx = ins.substring(0, sfx).intern(); String num = ins.substring(sfx + 1); String att = pfx.endsWith("const") ? "num" : "loc"; Element exp = new Element(pfx, att, num).deepFreeze(); abb.put(ins, exp); } } //abb = Collections.unmodifiableMap(abb); HashMap<Element, String> rabb = new HashMap<Element, String>(tl.size() / 2); for (Map.Entry<String, Element> e : abb.entrySet()) { rabb.put(e.getValue(), e.getKey()); } //rabb = Collections.unmodifiableMap(rabb); bcNames = names; bcFormats = formats; bcWideFormats = wideFormats; bcCodes = map; abbrevs = abb; rabbrevs = rabb; } public static String opName(int op) { if (op >= 0 && op < bcNames.length) { return bcNames[op]; } return "unknown#" + op; } public static String opFormat(int op) { return opFormat(op, false); } public static String opFormat(int op, boolean isWide) { if (op >= 0 && op < bcFormats.length) { return (isWide ? bcWideFormats[op] : bcFormats[op]); } return "?"; } public static int opCode(String opName) { Integer op = (Integer) bcCodes.get(opName); if (op != null) { return op.intValue(); } return -1; } public static Element expandAbbrev(String opName) { return abbrevs.get(opName); } public static String findAbbrev(Element op) { return rabbrevs.get(op); } public static int invertBranchOp(int op) { assert (opFormat(op).indexOf('o') >= 0); final int IFMIN = 0x99; final int IFMAX = 0xa6; final int IFMIN2 = 0xc6; final int IFMAX2 = 0xc7; assert (bcNames[IFMIN] == "ifeq"); assert (bcNames[IFMAX] == "if_acmpne"); assert (bcNames[IFMIN2] == "ifnonnull"); assert (bcNames[IFMAX2] == "ifnull"); int rop; if (op >= IFMIN && op <= IFMAX) { rop = IFMIN + ((op - IFMIN) ^ 1); } else if (op >= IFMIN2 && op <= IFMAX2) { rop = IFMIN2 + ((op - IFMIN2) ^ 1); } else { assert (false); rop = op; } assert (opFormat(rop).indexOf('o') >= 0); return rop; } public static Element parse(String bytes) { Element e = new Element("Instructions", bytes.length()); boolean willBeWide; boolean isWide = false; Element[] tempMap = new Element[bytes.length()]; for (int pc = 0, nextpc; pc < bytes.length(); pc = nextpc) { int op = bytes.charAt(pc); Element i = new Element(opName(op)); nextpc = pc + 1; int locarg = 0; int cparg = 0; int intarg = 0; int labelarg = 0; willBeWide = false; switch (op) { case 0xc4: //wide willBeWide = true; break; case 0x10: //bipush intarg = nextpc++; intarg *= -1; //mark signed break; case 0x11: //sipush intarg = nextpc; nextpc += 2; intarg *= -1; //mark signed break; case 0x12: //ldc cparg = nextpc++; break; case 0x13: //ldc_w case 0x14: //ldc2_w case 0xb2: //getstatic case 0xb3: //putstatic case 0xb4: //getfield case 0xb5: //putfield case 0xb6: //invokevirtual case 0xb7: //invokespecial case 0xb8: //invokestatic case 0xbb: //new case 0xbd: //anewarray case 0xc0: //checkcast case 0xc1: //instanceof cparg = nextpc; nextpc += 2; break; case 0xb9: //invokeinterface cparg = nextpc; nextpc += 2; intarg = nextpc; nextpc += 2; break; case 0xc5: //multianewarray cparg = nextpc; nextpc += 2; intarg = nextpc++; break; case 0x15: //iload case 0x16: //lload case 0x17: //fload case 0x18: //dload case 0x19: //aload case 0x36: //istore case 0x37: //lstore case 0x38: //fstore case 0x39: //dstore case 0x3a: //astore case 0xa9: //ret locarg = nextpc++; if (isWide) { nextpc++; } break; case 0x84: //iinc locarg = nextpc++; if (isWide) { nextpc++; } intarg = nextpc++; if (isWide) { nextpc++; } intarg *= -1; //mark signed break; case 0x99: //ifeq case 0x9a: //ifne case 0x9b: //iflt case 0x9c: //ifge case 0x9d: //ifgt case 0x9e: //ifle case 0x9f: //if_icmpeq case 0xa0: //if_icmpne case 0xa1: //if_icmplt case 0xa2: //if_icmpge case 0xa3: //if_icmpgt case 0xa4: //if_icmple case 0xa5: //if_acmpeq case 0xa6: //if_acmpne case 0xa7: //goto case 0xa8: //jsr labelarg = nextpc; nextpc += 2; break; case 0xbc: //newarray intarg = nextpc++; break; case 0xc6: //ifnull case 0xc7: //ifnonnull labelarg = nextpc; nextpc += 2; break; case 0xc8: //goto_w case 0xc9: //jsr_w labelarg = nextpc; nextpc += 4; break; // save the best for last: case 0xaa: //tableswitch nextpc = parseSwitch(bytes, pc, true, i); break; case 0xab: //lookupswitch nextpc = parseSwitch(bytes, pc, false, i); break; } String format = null; assert ((format = opFormat(op, isWide)) != null); //System.out.println("pc="+pc+" len="+(nextpc - pc)+" w="+isWide+" op="+op+" name="+opName(op)+" format="+format); assert ((nextpc - pc) == format.length() || format.indexOf('t') >= 0); // Parse out instruction fields. if (locarg != 0) { int len = nextpc - locarg; if (intarg != 0) { len /= 2; // split } i.setAttr("loc", "" + getInt(bytes, locarg, len)); assert ('l' == format.charAt(locarg - pc + 0)); assert ('l' == format.charAt(locarg - pc + len - 1)); } if (cparg != 0) { int len = nextpc - cparg; if (len > 2) { len = 2; } i.setAttr("ref", "" + getInt(bytes, cparg, len)); assert ('k' == format.charAt(cparg - pc + 0)); } if (intarg != 0) { boolean isSigned = (intarg < 0); if (isSigned) { intarg *= -1; } int len = nextpc - intarg; i.setAttr("num", "" + getInt(bytes, intarg, isSigned ? -len : len)); assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + 0)); assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + len - 1)); } if (labelarg != 0) { int len = nextpc - labelarg; int offset = getInt(bytes, labelarg, -len); int target = pc + offset; i.setAttr("lab", "" + target); assert ('o' == format.charAt(labelarg - pc + 0)); assert ('o' == format.charAt(labelarg - pc + len - 1)); } e.add(i); tempMap[pc] = i; isWide = willBeWide; } // Mark targets of branches. for (Element i : e.elements()) { for (int j = -1; j < i.size(); j++) { Element c = (j < 0) ? i : (Element) i.get(j); Number targetNum = c.getAttrNumber("lab"); if (targetNum != null) { int target = targetNum.intValue(); Element ti = null; if (target >= 0 && target < tempMap.length) { ti = tempMap[target]; } if (ti != null) { ti.setAttr("pc", "" + target); } else { c.setAttr("lab.error", ""); } } } } // Shrink to fit: for (Element i : e.elements()) { i.trimToSize(); } e.trimToSize(); /* String assem = assemble(e); if (!assem.equals(bytes)) { System.out.println("Bytes: "+bytes); System.out.println("Insns: "+e); System.out.println("Assem: "+parse(assem)); } */ return e; } static int switchBase(int pc) { int apc = pc + 1; apc += (-apc) & 3; return apc; } static int parseSwitch(String s, int pc, boolean isTable, Element i) { int apc = switchBase(pc); int defLabel = pc + getInt(s, apc + 4 * 0, 4); i.setAttr("lab", "" + defLabel); if (isTable) { int lowCase = getInt(s, apc + 4 * 1, 4); int highCase = getInt(s, apc + 4 * 2, 4); int caseCount = highCase - lowCase + 1; for (int n = 0; n < caseCount; n++) { Element c = new Element("Case", 4); int caseVal = lowCase + n; int caseLab = getInt(s, apc + 4 * (3 + n), 4) + pc; c.setAttr("num", "" + caseVal); c.setAttr("lab", "" + caseLab); assert (c.getExtraCapacity() == 0); i.add(c); } return apc + 4 * (3 + caseCount); } else { int caseCount = getInt(s, apc + 4 * 1, 4); for (int n = 0; n < caseCount; n++) { Element c = new Element("Case", 4); int caseVal = getInt(s, apc + 4 * (2 + (2 * n) + 0), 4); int caseLab = getInt(s, apc + 4 * (2 + (2 * n) + 1), 4) + pc; c.setAttr("num", "" + caseVal); c.setAttr("lab", "" + caseLab); assert (c.getExtraCapacity() == 0); i.add(c); } return apc + 4 * (2 + 2 * caseCount); } } static int getInt(String s, int pc, int len) { //System.out.println("getInt s["+s.length()+"] pc="+pc+" len="+len); int result = s.charAt(pc); if (len < 0) { len = -len; result = (byte) result; } if (!(len == 1 || len == 2 || len == 4)) { System.out.println("len=" + len); } assert (len == 1 || len == 2 || len == 4); for (int i = 1; i < len; i++) { result <<= 8; result += s.charAt(pc + i) & 0xFF; } return result; } public static String assemble(Element instructions) { return InstructionAssembler.assemble(instructions, null, null); } public static String assemble(Element instructions, String pcAttrName) { return InstructionAssembler.assemble(instructions, pcAttrName, null); } public static String assemble(Element instructions, ClassSyntax.GetCPIndex getCPI) { return InstructionAssembler.assemble(instructions, null, getCPI); } public static String assemble(Element instructions, String pcAttrName, ClassSyntax.GetCPIndex getCPI) { return InstructionAssembler.assemble(instructions, pcAttrName, getCPI); } }