123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /*
- * 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++;
- }
- }
- }
- }
- }
- }
|