xapi.bytecode.CodeAnalyzer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-gwt Show documentation
Show all versions of xapi-gwt Show documentation
This module exists solely to package all other gwt modules into a single
uber jar. This makes deploying to non-mavenized targets much easier.
Of course, you would be wise to inherit your dependencies individually;
the uber jar is intended for projects like collide,
which have complex configuration, and adding many jars would be a pain.
The newest version!
/*
* 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.
*
* MODIFIED BY James Nelson of We The Internet, 2013.
* Repackaged to avoid conflicts with different versions of Javassist,
* and modified Javassist APIs to make them more accessible to outside code.
*/
package xapi.bytecode;
import xapi.bytecode.api.Opcode;
import xapi.bytecode.attributes.CodeAttribute;
import xapi.bytecode.attributes.ExceptionTable;
public class CodeAnalyzer implements Opcode {
private ConstPool constPool;
private CodeAttribute codeAttr;
public CodeAnalyzer(CodeAttribute ca) {
codeAttr = ca;
constPool = ca.getConstPool();
}
public int computeMaxStack()
throws BadBytecode
{
/* d = stack[i]
* d == 0: not visited
* d > 0: the depth is d - 1 after executing the bytecode at i.
* d < 0: not visited. the initial depth (before execution) is 1 - d.
*/
CodeIterator ci = codeAttr.iterator();
int length = ci.getCodeLength();
int[] stack = new int[length];
constPool = codeAttr.getConstPool();
initStack(stack, codeAttr);
boolean repeat;
do {
repeat = false;
for (int i = 0; i < length; ++i) {
if (stack[i] < 0) {
repeat = true;
visitBytecode(ci, stack, i);
}
}
} while (repeat);
int maxStack = 1;
for (int i = 0; i < length; ++i) {
if (stack[i] > maxStack) {
maxStack = stack[i];
}
}
return maxStack - 1; // the base is 1.
}
private void initStack(int[] stack, CodeAttribute ca) {
stack[0] = -1;
ExceptionTable et = ca.getExceptionTable();
if (et != null) {
int size = et.size();
for (int i = 0; i < size; ++i)
{
stack[et.handlerPc(i)] = -2; // an exception is on stack
}
}
}
private void visitBytecode(CodeIterator ci, int[] stack, int index)
throws BadBytecode
{
int codeLength = stack.length;
ci.move(index);
int stackDepth = -stack[index];
while (ci.hasNext()) {
index = ci.next();
stack[index] = stackDepth;
int op = ci.byteAt(index);
stackDepth = visitInst(op, ci, index, stackDepth);
if (stackDepth < 1) {
throw new BadBytecode("stack underflow at " + index);
}
if (processBranch(op, ci, index, codeLength, stack, stackDepth)) {
break;
}
if (isEnd(op)) {
break;
}
if (op == JSR || op == JSR_W) {
--stackDepth;
}
}
}
private boolean processBranch(int opcode, CodeIterator ci, int index,
int codeLength, int[] stack, int stackDepth)
throws BadBytecode
{
if ((IFEQ <= opcode && opcode <= IF_ACMPNE)
|| opcode == IFNULL || opcode == IFNONNULL) {
int target = index + ci.s16bitAt(index + 1);
checkTarget(index, target, codeLength, stack, stackDepth);
}
else {
int target, index2;
switch (opcode) {
case GOTO :
target = index + ci.s16bitAt(index + 1);
checkTarget(index, target, codeLength, stack, stackDepth);
return true;
case GOTO_W :
target = index + ci.s32bitAt(index + 1);
checkTarget(index, target, codeLength, stack, stackDepth);
return true;
case JSR :
case JSR_W :
if (opcode == JSR) {
target = index + ci.s16bitAt(index + 1);
} else {
target = index + ci.s32bitAt(index + 1);
}
checkTarget(index, target, codeLength, stack, stackDepth);
if (stackDepth == 2) {
return false;
} else {
throw new BadBytecode(
"sorry, cannot compute this data flow due to JSR");
}
case RET :
if (stackDepth == 1) {
return true;
} else {
throw new BadBytecode(
"sorry, cannot compute this data flow due to RET");
}
case LOOKUPSWITCH :
case TABLESWITCH :
index2 = (index & ~3) + 4;
target = index + ci.s32bitAt(index2);
checkTarget(index, target, codeLength, stack, stackDepth);
if (opcode == LOOKUPSWITCH) {
int npairs = ci.s32bitAt(index2 + 4);
index2 += 12;
for (int i = 0; i < npairs; ++i) {
target = index + ci.s32bitAt(index2);
checkTarget(index, target, codeLength,
stack, stackDepth);
index2 += 8;
}
}
else {
int low = ci.s32bitAt(index2 + 4);
int high = ci.s32bitAt(index2 + 8);
int n = high - low + 1;
index2 += 12;
for (int i = 0; i < n; ++i) {
target = index + ci.s32bitAt(index2);
checkTarget(index, target, codeLength,
stack, stackDepth);
index2 += 4;
}
}
return true; // always branch.
}
}
return false; // may not branch.
}
private void checkTarget(int opIndex, int target, int codeLength,
int[] stack, int stackDepth)
throws BadBytecode
{
if (target < 0 || codeLength <= target) {
throw new BadBytecode("bad branch offset at " + opIndex);
}
int d = stack[target];
if (d == 0) {
stack[target] = -stackDepth;
} else if (d != stackDepth && d != -stackDepth) {
throw new BadBytecode("verification error (" + stackDepth +
"," + d + ") at " + opIndex);
}
}
private static boolean isEnd(int opcode) {
return (IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW;
}
/**
* Visits an instruction.
* @throws BadBytecode
*/
private int visitInst(int op, CodeIterator ci, int index, int stack)
throws BadBytecode
{
String desc;
switch (op) {
case GETFIELD :
stack += getFieldSize(ci, index) - 1;
break;
case PUTFIELD :
stack -= getFieldSize(ci, index) + 1;
break;
case GETSTATIC :
stack += getFieldSize(ci, index);
break;
case PUTSTATIC :
stack -= getFieldSize(ci, index);
break;
case INVOKEVIRTUAL :
case INVOKESPECIAL :
desc = constPool.getMethodrefType(ci.u16bitAt(index + 1));
stack += Descriptor.dataSize(desc) - 1;
break;
case INVOKESTATIC :
desc = constPool.getMethodrefType(ci.u16bitAt(index + 1));
stack += Descriptor.dataSize(desc);
break;
case INVOKEINTERFACE :
desc = constPool.getInterfaceMethodrefType(
ci.u16bitAt(index + 1));
stack += Descriptor.dataSize(desc) - 1;
break;
case ATHROW :
stack = 1; // the stack becomes empty (1 means no values).
break;
case MULTIANEWARRAY :
stack += 1 - ci.byteAt(index + 3);
break;
case WIDE :
op = ci.byteAt(index + 1);
// don't break here.
default :
stack += STACK_GROW[op];
}
return stack;
}
private int getFieldSize(CodeIterator ci, int index) {
String desc = constPool.getFieldrefType(ci.u16bitAt(index + 1));
return Descriptor.dataSize(desc);
}
}