diff options
Diffstat (limited to 'src/main/javassist/bytecode/stackmap/Liveness.java')
-rw-r--r-- | src/main/javassist/bytecode/stackmap/Liveness.java | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/src/main/javassist/bytecode/stackmap/Liveness.java b/src/main/javassist/bytecode/stackmap/Liveness.java new file mode 100644 index 0000000..4acd65e --- /dev/null +++ b/src/main/javassist/bytecode/stackmap/Liveness.java @@ -0,0 +1,365 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2007 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. + * + * 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.*; + +public class Liveness { + protected static final byte UNKNOWN = 0; + protected static final byte READ = 1; + protected static final byte UPDATED = 2; + protected byte[] localsUsage; + + /** + * If true, all the arguments become alive within the whole method body. + * + * To correctly compute a stack map table, all the arguments must + * be alive (localsUsage[?] must be READ) at least in the first block. + */ + public static boolean useArgs = true; + + public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals, + TypeData[] args) + throws BadBytecode + { + computeUsage(ci, blocks, maxLocals); + if (useArgs) + useAllArgs(blocks, args); + + computeLiveness1(blocks[0]); + while (hasChanged(blocks)) + computeLiveness2(blocks[0]); + } + + private void useAllArgs(TypedBlock[] blocks, TypeData[] args) { + for (int k = 0; k < blocks.length; k++) { + byte[] usage = blocks[k].localsUsage; + for (int i = 0; i < args.length; i++) + if (args[i] != TypeTag.TOP) + usage[i] = READ; + } + } + + static final int NOT_YET = 0; + static final int CHANGED_LAST = 1; + static final int DONE = 2; + static final int CHANGED_NOW = 3; + + private void computeLiveness1(TypedBlock tb) { + if (tb.updating) { + // a loop was detected. + computeLiveness1u(tb); + return; + } + + if (tb.inputs != null) + return; + + tb.updating = true; + byte[] usage = tb.localsUsage; + int n = usage.length; + boolean[] in = new boolean[n]; + for (int i = 0; i < n; i++) + in[i] = usage[i] == READ; + + BasicBlock.Catch handlers = tb.toCatch; + while (handlers != null) { + TypedBlock h = (TypedBlock)handlers.body; + computeLiveness1(h); + for (int k = 0; k < n; k++) + if (h.inputs[k]) + in[k] = true; + + handlers = handlers.next; + } + + if (tb.exit != null) { + for (int i = 0; i < tb.exit.length; i++) { + TypedBlock e = (TypedBlock)tb.exit[i]; + computeLiveness1(e); + for (int k = 0; k < n; k++) + if (!in[k]) + in[k] = usage[k] == UNKNOWN && e.inputs[k]; + } + } + + tb.updating = false; + if (tb.inputs == null) { + tb.inputs = in; + tb.status = DONE; + } + else { + for (int i = 0; i < n; i++) + if (in[i] && !tb.inputs[i]) { + tb.inputs[i] = true; + tb.status = CHANGED_NOW; + } + } + } + + private void computeLiveness1u(TypedBlock tb) { + if (tb.inputs == null) { + byte[] usage = tb.localsUsage; + int n = usage.length; + boolean[] in = new boolean[n]; + for (int i = 0; i < n; i++) + in[i] = usage[i] == READ; + + tb.inputs = in; + tb.status = DONE; + } + } + + private void computeLiveness2(TypedBlock tb) { + if (tb.updating || tb.status >= DONE) + return; + + tb.updating = true; + if (tb.exit == null) + tb.status = DONE; + else { + boolean changed = false; + for (int i = 0; i < tb.exit.length; i++) { + TypedBlock e = (TypedBlock)tb.exit[i]; + computeLiveness2(e); + if (e.status != DONE) + changed = true; + } + + if (changed) { + changed = false; + byte[] usage = tb.localsUsage; + int n = usage.length; + for (int i = 0; i < tb.exit.length; i++) { + TypedBlock e = (TypedBlock)tb.exit[i]; + if (e.status != DONE) + for (int k = 0; k < n; k++) + if (!tb.inputs[k]) { + if (usage[k] == UNKNOWN && e.inputs[k]) { + tb.inputs[k] = true; + changed = true; + } + } + } + + tb.status = changed ? CHANGED_NOW : DONE; + } + else + tb.status = DONE; + } + + if (computeLiveness2except(tb)) + tb.status = CHANGED_NOW; + + tb.updating = false; + } + + private boolean computeLiveness2except(TypedBlock tb) { + BasicBlock.Catch handlers = tb.toCatch; + boolean changed = false; + while (handlers != null) { + TypedBlock h = (TypedBlock)handlers.body; + computeLiveness2(h); + if (h.status != DONE) { + boolean[] in = tb.inputs; + int n = in.length; + for (int k = 0; k < n; k++) + if (!in[k] && h.inputs[k]) { + in[k] = true; + changed = true; + } + } + + handlers = handlers.next; + } + + return changed; + } + + private boolean hasChanged(TypedBlock[] blocks) { + int n = blocks.length; + boolean changed = false; + for (int i = 0; i < n; i++) { + TypedBlock tb = blocks[i]; + if (tb.status == CHANGED_NOW) { + tb.status = CHANGED_LAST; + changed = true; + } + else + tb.status = NOT_YET; + } + + return changed; + } + + private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals) + throws BadBytecode + { + int n = blocks.length; + for (int i = 0; i < n; i++) { + TypedBlock tb = blocks[i]; + localsUsage = tb.localsUsage = new byte[maxLocals]; + int pos = tb.position; + analyze(ci, pos, pos + tb.length); + localsUsage = null; + } + } + + protected final void readLocal(int reg) { + if (localsUsage[reg] == UNKNOWN) + localsUsage[reg] = READ; + } + + protected final void writeLocal(int reg) { + if (localsUsage[reg] == UNKNOWN) + localsUsage[reg] = UPDATED; + } + + protected void analyze(CodeIterator ci, int begin, int end) + throws BadBytecode + { + ci.begin(); + ci.move(begin); + while (ci.hasNext()) { + int index = ci.next(); + if (index >= end) + break; + + int op = ci.byteAt(index); + if (op < 96) + if (op < 54) + doOpcode0_53(ci, index, op); + else + doOpcode54_95(ci, index, op); + else + if (op == Opcode.IINC) { + // this does not call writeLocal(). + readLocal(ci.byteAt(index + 1)); + } + else if (op == Opcode.WIDE) + doWIDE(ci, index); + } + } + + private void doOpcode0_53(CodeIterator ci, int pos, int op) { + switch (op) { + case Opcode.ILOAD : + case Opcode.LLOAD : + case Opcode.FLOAD : + case Opcode.DLOAD : + case Opcode.ALOAD : + readLocal(ci.byteAt(pos + 1)); + break; + case Opcode.ILOAD_0 : + case Opcode.ILOAD_1 : + case Opcode.ILOAD_2 : + case Opcode.ILOAD_3 : + readLocal(op - Opcode.ILOAD_0); + break; + case Opcode.LLOAD_0 : + case Opcode.LLOAD_1 : + case Opcode.LLOAD_2 : + case Opcode.LLOAD_3 : + readLocal(op - Opcode.LLOAD_0); + break; + case Opcode.FLOAD_0 : + case Opcode.FLOAD_1 : + case Opcode.FLOAD_2 : + case Opcode.FLOAD_3 : + readLocal(op - Opcode.FLOAD_0); + break; + case Opcode.DLOAD_0 : + case Opcode.DLOAD_1 : + case Opcode.DLOAD_2 : + case Opcode.DLOAD_3 : + readLocal(op - Opcode.DLOAD_0); + break; + case Opcode.ALOAD_0 : + case Opcode.ALOAD_1 : + case Opcode.ALOAD_2 : + case Opcode.ALOAD_3 : + readLocal(op - Opcode.ALOAD_0); + break; + } + } + + private void doOpcode54_95(CodeIterator ci, int pos, int op) { + switch (op) { + case Opcode.ISTORE : + case Opcode.LSTORE : + case Opcode.FSTORE : + case Opcode.DSTORE : + case Opcode.ASTORE : + writeLocal(ci.byteAt(pos + 1)); + break; + case Opcode.ISTORE_0 : + case Opcode.ISTORE_1 : + case Opcode.ISTORE_2 : + case Opcode.ISTORE_3 : + writeLocal(op - Opcode.ISTORE_0); + break; + case Opcode.LSTORE_0 : + case Opcode.LSTORE_1 : + case Opcode.LSTORE_2 : + case Opcode.LSTORE_3 : + writeLocal(op - Opcode.LSTORE_0); + break; + case Opcode.FSTORE_0 : + case Opcode.FSTORE_1 : + case Opcode.FSTORE_2 : + case Opcode.FSTORE_3 : + writeLocal(op - Opcode.FSTORE_0); + break; + case Opcode.DSTORE_0 : + case Opcode.DSTORE_1 : + case Opcode.DSTORE_2 : + case Opcode.DSTORE_3 : + writeLocal(op - Opcode.DSTORE_0); + break; + case Opcode.ASTORE_0 : + case Opcode.ASTORE_1 : + case Opcode.ASTORE_2 : + case Opcode.ASTORE_3 : + writeLocal(op - Opcode.ASTORE_0); + break; + } + } + + private void doWIDE(CodeIterator ci, int pos) throws BadBytecode { + int op = ci.byteAt(pos + 1); + int var = ci.u16bitAt(pos + 2); + switch (op) { + case Opcode.ILOAD : + case Opcode.LLOAD : + case Opcode.FLOAD : + case Opcode.DLOAD : + case Opcode.ALOAD : + readLocal(var); + break; + case Opcode.ISTORE : + case Opcode.LSTORE : + case Opcode.FSTORE : + case Opcode.DSTORE : + case Opcode.ASTORE : + writeLocal(var); + break; + case Opcode.IINC : + readLocal(var); + // this does not call writeLocal(). + break; + } + } +} |