/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.*; import java.util.HashMap; import java.util.ArrayList; /** * A basic block is a sequence of bytecode that does not contain jump/branch * instructions except at the last bytecode. * Since Java7 or later does not allow JSR, this class throws an exception when * it finds JSR. */ public class BasicBlock { static class JsrBytecode extends BadBytecode { JsrBytecode() { super("JSR"); } } protected int position, length; protected int incoming; // the number of incoming branches. protected BasicBlock[] exit; // null if the block is a leaf. protected boolean stop; // true if the block ends with an unconditional jump. protected Catch toCatch; protected BasicBlock(int pos) { position = pos; length = 0; incoming = 0; } public static BasicBlock find(BasicBlock[] blocks, int pos) throws BadBytecode { for (int i = 0; i < blocks.length; i++) { int iPos = blocks[i].position; if (iPos <= pos && pos < iPos + blocks[i].length) return blocks[i]; } throw new BadBytecode("no basic block at " + pos); } public static class Catch { public Catch next; public BasicBlock body; public int typeIndex; Catch(BasicBlock b, int i, Catch c) { body = b; typeIndex = i; next = c; } } public String toString() { StringBuffer sbuf = new StringBuffer(); String cname = this.getClass().getName(); int i = cname.lastIndexOf('.'); sbuf.append(i < 0 ? cname : cname.substring(i + 1)); sbuf.append("["); toString2(sbuf); sbuf.append("]"); return sbuf.toString(); } protected void toString2(StringBuffer sbuf) { sbuf.append("pos=").append(position).append(", len=") .append(length).append(", in=").append(incoming) .append(", exit{"); if (exit != null) { for (int i = 0; i < exit.length; i++) sbuf.append(exit[i].position).append(","); } sbuf.append("}, {"); Catch th = toCatch; while (th != null) { sbuf.append("(").append(th.body.position).append(", ") .append(th.typeIndex).append("), "); th = th.next; } sbuf.append("}"); } /** * A Mark indicates the position of a branch instruction * or a branch target. */ static class Mark implements Comparable { int position; BasicBlock block; BasicBlock[] jump; boolean alwaysJmp; // true if an unconditional branch. int size; // 0 unless the mark indicates RETURN etc. Catch catcher; Mark(int p) { position = p; block = null; jump = null; alwaysJmp = false; size = 0; catcher = null; } public int compareTo(Object obj) { if (obj instanceof Mark) { int pos = ((Mark)obj).position; return position - pos; } return -1; } void setJump(BasicBlock[] bb, int s, boolean always) { jump = bb; size = s; alwaysJmp = always; } } public static class Maker { /* Override these two methods if a subclass of BasicBlock must be * instantiated. */ protected BasicBlock makeBlock(int pos) { return new BasicBlock(pos); } protected BasicBlock[] makeArray(int size) { return new BasicBlock[size]; } private BasicBlock[] makeArray(BasicBlock b) { BasicBlock[] array = makeArray(1); array[0] = b; return array; } private BasicBlock[] makeArray(BasicBlock b1, BasicBlock b2) { BasicBlock[] array = makeArray(2); array[0] = b1; array[1] = b2; return array; } public BasicBlock[] make(MethodInfo minfo) throws BadBytecode { CodeAttribute ca = minfo.getCodeAttribute(); if (ca == null) return null; CodeIterator ci = ca.iterator(); return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable()); } public BasicBlock[] make(CodeIterator ci, int begin, int end, ExceptionTable et) throws BadBytecode { HashMap marks = makeMarks(ci, begin, end, et); BasicBlock[] bb = makeBlocks(marks); addCatchers(bb, et); return bb; } /* Branch target */ private Mark makeMark(HashMap table, int pos) { return makeMark0(table, pos, true, true); } /* Branch instruction. * size > 0 */ private Mark makeMark(HashMap table, int pos, BasicBlock[] jump, int size, boolean always) { Mark m = makeMark0(table, pos, false, false); m.setJump(jump, size, always); return m; } private Mark makeMark0(HashMap table, int pos, boolean isBlockBegin, boolean isTarget) { Integer p = new Integer(pos); Mark m = (Mark)table.get(p); if (m == null) { m = new Mark(pos); table.put(p, m); } if (isBlockBegin) { if (m.block == null) m.block = makeBlock(pos); if (isTarget) m.block.incoming++; } return m; } private HashMap makeMarks(CodeIterator ci, int begin, int end, ExceptionTable et) throws BadBytecode { ci.begin(); ci.move(begin); HashMap marks = new HashMap(); while (ci.hasNext()) { int index = ci.next(); if (index >= end) break; int op = ci.byteAt(index); if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) || op == Opcode.IFNULL || op == Opcode.IFNONNULL) { Mark to = makeMark(marks, index + ci.s16bitAt(index + 1)); Mark next = makeMark(marks, index + 3); makeMark(marks, index, makeArray(to.block, next.block), 3, false); } else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH) switch (op) { case Opcode.GOTO : makeGoto(marks, index, index + ci.s16bitAt(index + 1), 3); break; case Opcode.JSR : makeJsr(marks, index, index + ci.s16bitAt(index + 1), 3); break; case Opcode.RET : makeMark(marks, index, null, 2, true); break; case Opcode.TABLESWITCH : { int pos = (index & ~3) + 4; int low = ci.s32bitAt(pos + 4); int high = ci.s32bitAt(pos + 8); int ncases = high - low + 1; BasicBlock[] to = makeArray(ncases + 1); to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target int p = pos + 12; int n = p + ncases * 4; int k = 1; while (p < n) { to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; p += 4; } makeMark(marks, index, to, n - index, true); break; } case Opcode.LOOKUPSWITCH : { int pos = (index & ~3) + 4; int ncases = ci.s32bitAt(pos + 4); BasicBlock[] to = makeArray(ncases + 1); to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target int p = pos + 8 + 4; int n = p + ncases * 8 - 4; int k = 1; while (p < n) { to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; p += 8; } makeMark(marks, index, to, n - index, true); break; } } else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW) makeMark(marks, index, null, 1, true); else if (op == Opcode.GOTO_W) makeGoto(marks, index, index + ci.s32bitAt(index + 1), 5); else if (op == Opcode.JSR_W) makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5); else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET) makeMark(marks, index, null, 4, true); } if (et != null) { int i = et.size(); while (--i >= 0) { makeMark0(marks, et.startPc(i), true, false); makeMark(marks, et.handlerPc(i)); } } return marks; } private void makeGoto(HashMap marks, int pos, int target, int size) { Mark to = makeMark(marks, target); BasicBlock[] jumps = makeArray(to.block); makeMark(marks, pos, jumps, size, true); } /* * We could ignore JSR since Java 7 or later does not allow it. * See The JVM Spec. Sec. 4.10.2.5. */ protected void makeJsr(HashMap marks, int pos, int target, int size) throws BadBytecode { /* Mark to = makeMark(marks, target); Mark next = makeMark(marks, pos + size); BasicBlock[] jumps = makeArray(to.block, next.block); makeMark(marks, pos, jumps, size, false); */ throw new JsrBytecode(); } private BasicBlock[] makeBlocks(HashMap markTable) { Mark[] marks = (Mark[])markTable.values() .toArray(new Mark[markTable.size()]); java.util.Arrays.sort(marks); ArrayList blocks = new ArrayList(); int i = 0; BasicBlock prev; if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null) prev = getBBlock(marks[i++]); else prev = makeBlock(0); blocks.add(prev); while (i < marks.length) { Mark m = marks[i++]; BasicBlock bb = getBBlock(m); if (bb == null) { // the mark indicates a branch instruction if (prev.length > 0) { // the previous mark already has exits. prev = makeBlock(prev.position + prev.length); blocks.add(prev); } prev.length = m.position + m.size - prev.position; prev.exit = m.jump; prev.stop = m.alwaysJmp; } else { // the mark indicates a branch target if (prev.length == 0) { prev.length = m.position - prev.position; bb.incoming++; prev.exit = makeArray(bb); } else { // the previous mark already has exits. if (prev.position + prev.length < m.position) { // dead code is found. prev = makeBlock(prev.position + prev.length); blocks.add(prev); prev.length = m.position - prev.position; // the incoming flow from dead code is not counted // bb.incoming++; prev.exit = makeArray(bb); } } blocks.add(bb); prev = bb; } } return (BasicBlock[])blocks.toArray(makeArray(blocks.size())); } private static BasicBlock getBBlock(Mark m) { BasicBlock b = m.block; if (b != null && m.size > 0) { b.exit = m.jump; b.length = m.size; b.stop = m.alwaysJmp; } return b; } private void addCatchers(BasicBlock[] blocks, ExceptionTable et) throws BadBytecode { if (et == null) return; int i = et.size(); while (--i >= 0) { BasicBlock handler = find(blocks, et.handlerPc(i)); int start = et.startPc(i); int end = et.endPc(i); int type = et.catchType(i); handler.incoming--; for (int k = 0; k < blocks.length; k++) { BasicBlock bb = blocks[k]; int iPos = bb.position; if (start <= iPos && iPos < end) { bb.toCatch = new Catch(handler, type, bb.toCatch); handler.incoming++; } } } } } }