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