/* * 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; /* * @author jrose */ abstract class InstructionAssembler extends InstructionSyntax { InstructionAssembler() { } public static String assemble(Element instructions, String pcAttrName, ClassSyntax.GetCPIndex getCPI) { int insCount = instructions.size(); Element[] insElems = new Element[insCount]; int[] elemToIndexMap; int[] insLocs; byte[] ops = new byte[insCount]; int[] operands = new int[insCount]; boolean[] isWide = new boolean[insCount]; int[] branches; int[] branchInsLocs; HashMap labels = new HashMap(); final int WIDE = 0xc4; final int GOTO = 0xa7; final int GOTO_W = 0xc8; final int GOTO_LEN = 3; final int GOTO_W_LEN = 5; assert ("wide".equals(bcNames[WIDE])); assert ("goto".equals(bcNames[GOTO])); assert ("goto_w".equals(bcNames[GOTO_W])); assert (bcFormats[GOTO].length() == GOTO_LEN); assert (bcFormats[GOTO_W].length() == GOTO_W_LEN); // Unpack instructions into temp. arrays, and find branches and labels. { elemToIndexMap = (pcAttrName != null) ? new int[insCount] : null; int[] buffer = operands; int id = 0; int branchCount = 0; for (int i = 0; i < insCount; i++) { Element ins = (Element) instructions.get(i); if (elemToIndexMap != null) { elemToIndexMap[i] = (ins.getAttr(pcAttrName) != null ? id : -1); } String lab = ins.getAttr("pc"); if (lab != null) { labels.put(lab, String.valueOf(id)); } int op = opCode(ins.getName()); if (op < 0) { assert (ins.getAttr(pcAttrName) != null || ins.getName().equals("label")); continue; // delete PC holder element } if (op == WIDE) { //0xc4 isWide[id] = true; // force wide format continue; } if (bcFormats[op].indexOf('o') >= 0) { buffer[branchCount++] = id; } if (bcFormats[op] == bcWideFormats[op]) { isWide[id] = false; } insElems[id] = ins; ops[id] = (byte) op; id++; } insCount = id; // maybe we deleted some wide prefixes, etc. branches = new int[branchCount + 1]; System.arraycopy(buffer, 0, branches, 0, branchCount); branches[branchCount] = -1; // sentinel } // Compute instruction sizes. These sizes are final, // except for branch instructions, which may need lengthening. // Some instructions (ldc, bipush, iload, iinc) are automagically widened. insLocs = new int[insCount + 1]; int loc = 0; for (int bn = 0, id = 0; id < insCount; id++) { insLocs[id] = loc; Element ins = insElems[id]; int op = ops[id] & 0xFF; String format = opFormat(op, isWide[id]); // Make sure operands fit within the given format. for (int j = 1, jlimit = format.length(); j < jlimit; j++) { char fc = format.charAt(j); int x = 0; switch (fc) { case 'l': x = (int) ins.getAttrLong("loc"); assert (x >= 0); if (x > 0xFF && !isWide[id]) { isWide[id] = true; format = opFormat(op, isWide[id]); } assert (x <= 0xFFFF); break; case 'k': char fc2 = format.charAt(Math.min(j + 1, format.length() - 1)); x = getCPIndex(ins, fc2, getCPI); if (x > 0xFF && j == jlimit - 1) { assert (op == 0x12); //ldc ops[id] = (byte) (op = 0x13); //ldc_w format = opFormat(op); } assert (x <= 0xFFFF); j++; // skip type-of-constant marker break; case 'x': x = (int) ins.getAttrLong("num"); assert (x >= 0 && x <= ((j == jlimit - 1) ? 0xFF : 0xFFFF)); break; case 's': x = (int) ins.getAttrLong("num"); if (x != (byte) x && j == jlimit - 1) { switch (op) { case 0x10: //bipush ops[id] = (byte) (op = 0x11); //sipush break; case 0x84: //iinc isWide[id] = true; format = opFormat(op, isWide[id]); break; default: assert (false); // cannot lengthen } } // unsign the value now, to make later steps clearer if (j == jlimit - 1) { assert (x == (byte) x); x = x & 0xFF; } else { assert (x == (short) x); x = x & 0xFFFF; } break; case 'o': assert (branches[bn] == id); bn++; // make local copies of the branches, and fix up labels insElems[id] = ins = new Element(ins); String newLab = labels.get(ins.getAttr("lab")); assert (newLab != null); ins.setAttr("lab", newLab); int prevCas = 0; int k = 0; for (Element cas : ins.elements()) { assert (cas.getName().equals("Case")); ins.set(k++, cas = new Element(cas)); newLab = labels.get(cas.getAttr("lab")); assert (newLab != null); cas.setAttr("lab", newLab); int thisCas = (int) cas.getAttrLong("num"); assert (op == 0xab || op == 0xaa && (k == 0 || thisCas == prevCas + 1)); prevCas = thisCas; } break; case 't': // switch table is represented as Switch.Case sub-elements break; default: assert (false); } operands[id] = x; // record operand (last if there are 2) // skip redundant chars while (j + 1 < jlimit && format.charAt(j + 1) == fc) { ++j; } } switch (op) { case 0xaa: //tableswitch loc = switchBase(loc); loc += 4 * (3 + ins.size()); break; case 0xab: //lookupswitch loc = switchBase(loc); loc += 4 * (2 + 2 * ins.size()); break; default: if (isWide[id]) { loc++; // 'wide' opcode prefix } loc += format.length(); break; } } insLocs[insCount] = loc; // compute branch offsets, and see if any branches need expansion for (int maxTries = 9, tries = 0;; ++tries) { boolean overflowing = false; boolean[] branchExpansions = null; for (int bn = 0; bn < branches.length - 1; bn++) { int id = branches[bn]; Element ins = insElems[id]; int insSize = insLocs[id + 1] - insLocs[id]; int origin = insLocs[id]; int target = insLocs[(int) ins.getAttrLong("lab")]; int offset = target - origin; operands[id] = offset; //System.out.println("branch id="+id+" len="+insSize+" to="+target+" offset="+offset); assert (insSize == GOTO_LEN || insSize == GOTO_W_LEN || ins.getName().indexOf("switch") > 0); boolean thisOverflow = (insSize == GOTO_LEN && (offset != (short) offset)); if (thisOverflow && !overflowing) { overflowing = true; branchExpansions = new boolean[branches.length]; } if (thisOverflow || tries == maxTries - 1) { // lengthen the branch assert (!(thisOverflow && isWide[id])); isWide[id] = true; branchExpansions[bn] = true; } } if (!overflowing) { break; // done, usually on first try } assert (tries <= maxTries); // Walk over all instructions, expanding branches and updating locations. int fixup = 0; for (int bn = 0, id = 0; id < insCount; id++) { insLocs[id] += fixup; if (branches[bn] == id) { int op = ops[id] & 0xFF; int wop; boolean invert; if (branchExpansions[bn]) { switch (op) { case GOTO: //0xa7 wop = GOTO_W; //0xc8 invert = false; break; case 0xa8: //jsr wop = 0xc9; //jsr_w invert = false; break; default: wop = invertBranchOp(op); invert = true; break; } assert (op != wop); ops[id] = (byte) wop; isWide[id] = invert; if (invert) { fixup += GOTO_W_LEN; //branch around a wide goto } else { fixup += (GOTO_W_LEN - GOTO_LEN); } // done expanding: ops and isWide reflect the decision } bn++; } } insLocs[insCount] += fixup; } // we know the layout now // notify the caller of offsets, if requested if (elemToIndexMap != null) { for (int i = 0; i < elemToIndexMap.length; i++) { int id = elemToIndexMap[i]; if (id >= 0) { Element ins = (Element) instructions.get(i); ins.setAttr(pcAttrName, "" + insLocs[id]); } } elemToIndexMap = null; // release the pointer } // output the bytes StringBuffer sbuf = new StringBuffer(insLocs[insCount]); for (int bn = 0, id = 0; id < insCount; id++) { //System.out.println("output id="+id+" loc="+insLocs[id]+" len="+(insLocs[id+1]-insLocs[id])+" #sbuf="+sbuf.length()); assert (sbuf.length() == insLocs[id]); Element ins; int pc = insLocs[id]; int nextpc = insLocs[id + 1]; int op = ops[id] & 0xFF; int opnd = operands[id]; String format; if (branches[bn] == id) { bn++; sbuf.append((char) op); if (isWide[id]) { // emit