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