/*
* 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.
ClassPool;
import javassist.bytecode.*;
/**
* Stack map maker.
*/
public class
MapMaker extends
Tracer {
/*
public static void main(String[] args) throws Exception {
boolean useMain2 = args[0].equals("0");
if (useMain2 && args.length > 1) {
main2(args);
return;
}
for (int i = 0; i < args.length; i++)
main1(args[i]);
}
public static void main1(String className) throws Exception {
ClassPool cp = ClassPool.getDefault();
//javassist.CtClass cc = cp.get(className);
javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className));
System.out.println(className);
ClassFile cf = cc.getClassFile();
java.util.List minfos = cf.getMethods();
for (int i = 0; i < minfos.size(); i++) {
MethodInfo minfo = (MethodInfo)minfos.get(i);
CodeAttribute ca = minfo.getCodeAttribute();
if (ca != null)
ca.setAttribute(make(cp, minfo));
}
cc.writeFile("tmp");
}
public static void main2(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
//javassist.CtClass cc = cp.get(args[1]);
javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1]));
MethodInfo minfo;
if (args[2].equals("_init_"))
minfo = cc.getDeclaredConstructors()[0].getMethodInfo();
// minfo = cc.getClassInitializer().getMethodInfo();
else
minfo = cc.getDeclaredMethod(args[2]).getMethodInfo();
CodeAttribute ca = minfo.getCodeAttribute();
if (ca == null) {
System.out.println("abstarct method");
return;
}
TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false);
MapMaker mm = new MapMaker(cp, minfo, ca);
mm.make(blocks, ca.getCode());
for (int i = 0; i < blocks.length; i++)
System.out.println(blocks[i]);
}
*/
/**
* Computes the stack map table of the given method and returns it.
* It returns null if the given method does not have to have a
* stack map table.
*/
public static
StackMapTable make(
ClassPool classes,
MethodInfo minfo)
throws
BadBytecode
{
CodeAttribute ca =
minfo.
getCodeAttribute();
if (
ca == null)
return null;
TypedBlock[]
blocks =
TypedBlock.
makeBlocks(
minfo,
ca, true);
if (
blocks == null)
return null;
MapMaker mm = new
MapMaker(
classes,
minfo,
ca);
mm.
make(
blocks,
ca.
getCode());
return
mm.
toStackMap(
blocks);
}
public
MapMaker(
ClassPool classes,
MethodInfo minfo,
CodeAttribute ca) {
super(
classes,
minfo.
getConstPool(),
ca.
getMaxStack(),
ca.
getMaxLocals(),
TypedBlock.
getRetType(
minfo.
getDescriptor()));
}
protected
MapMaker(
MapMaker old, boolean
copyStack) {
super(
old,
copyStack);
}
/**
* Runs an analyzer (Phase 1 and 2).
*/
void
make(
TypedBlock[]
blocks, byte[]
code)
throws
BadBytecode
{
TypedBlock first =
blocks[0];
fixParamTypes(
first);
TypeData[]
srcTypes =
first.
localsTypes;
copyFrom(
srcTypes.length,
srcTypes, this.
localsTypes);
make(
code,
first);
int
n =
blocks.length;
for (int
i = 0;
i <
n;
i++)
evalExpected(
blocks[
i]);
}
/*
* If a parameter type is String but it is used only as Object
* within the method body, this MapMaker class will report its type
* is Object. To avoid this, fixParamTypes calls TypeData.setType()
* on each parameter type.
*/
private void
fixParamTypes(
TypedBlock first) throws
BadBytecode {
TypeData[]
types =
first.
localsTypes;
int
n =
types.length;
for (int
i = 0;
i <
n;
i++) {
TypeData t =
types[
i];
if (
t instanceof
TypeData.
ClassName) {
/* Skip the following statement if t.isNullType() is true
* although a parameter type is never null type.
*/
TypeData.
setType(
t,
t.
getName(),
classPool);
}
}
}
// Phase 1
private void
make(byte[]
code,
TypedBlock tb)
throws
BadBytecode
{
BasicBlock.
Catch handlers =
tb.
toCatch;
while (
handlers != null) {
traceException(
code,
handlers);
handlers =
handlers.
next;
}
int
pos =
tb.
position;
int
end =
pos +
tb.
length;
while (
pos <
end)
pos +=
doOpcode(
pos,
code);
if (
tb.
exit != null) {
for (int
i = 0;
i <
tb.
exit.length;
i++) {
TypedBlock e = (
TypedBlock)
tb.
exit[
i];
if (
e.
alreadySet())
mergeMap(
e, true);
else {
recordStackMap(
e);
MapMaker maker = new
MapMaker(this, true);
maker.
make(
code,
e);
}
}
}
}
private void
traceException(byte[]
code,
TypedBlock.
Catch handler)
throws
BadBytecode
{
TypedBlock tb = (
TypedBlock)
handler.
body;
if (
tb.
alreadySet())
mergeMap(
tb, false);
else {
recordStackMap(
tb,
handler.
typeIndex);
MapMaker maker = new
MapMaker(this, false);
/* the following code is equivalent to maker.copyFrom(this)
* except stackTypes are not copied.
*/
maker.
stackTypes[0] =
tb.
stackTypes[0].
getSelf();
maker.
stackTop = 1;
maker.
make(
code,
tb);
}
}
private void
mergeMap(
TypedBlock dest, boolean
mergeStack) {
boolean[]
inputs =
dest.
inputs;
int
n =
inputs.length;
for (int
i = 0;
i <
n;
i++)
if (
inputs[
i])
merge(
localsTypes[
i],
dest.
localsTypes[
i]);
if (
mergeStack) {
n =
stackTop;
for (int
i = 0;
i <
n;
i++)
merge(
stackTypes[
i],
dest.
stackTypes[
i]);
}
}
private void
merge(
TypeData td,
TypeData target) {
boolean
tdIsObj = false;
boolean
targetIsObj = false;
// td or target is null if it is TOP.
if (
td !=
TOP &&
td.
isObjectType())
tdIsObj = true;
if (
target !=
TOP &&
target.
isObjectType())
targetIsObj = true;
if (
tdIsObj &&
targetIsObj)
target.
merge(
td);
}
private void
recordStackMap(
TypedBlock target)
throws
BadBytecode
{
TypeData[]
tStackTypes = new
TypeData[
stackTypes.length];
int
st =
stackTop;
copyFrom(
st,
stackTypes,
tStackTypes);
recordStackMap0(
target,
st,
tStackTypes);
}
private void
recordStackMap(
TypedBlock target, int
exceptionType)
throws
BadBytecode
{
String type;
if (
exceptionType == 0)
type = "java.lang.Throwable";
else
type =
cpool.
getClassInfo(
exceptionType);
TypeData[]
tStackTypes = new
TypeData[
stackTypes.length];
tStackTypes[0] = new
TypeData.
ClassName(
type);
recordStackMap0(
target, 1,
tStackTypes);
}
private void
recordStackMap0(
TypedBlock target, int
st,
TypeData[]
tStackTypes)
throws
BadBytecode
{
int
n =
localsTypes.length;
TypeData[]
tLocalsTypes = new
TypeData[
n];
int
k =
copyFrom(
n,
localsTypes,
tLocalsTypes);
boolean[]
inputs =
target.
inputs;
for (int
i = 0;
i <
n;
i++)
if (!
inputs[
i])
tLocalsTypes[
i] =
TOP;
target.
setStackMap(
st,
tStackTypes,
k,
tLocalsTypes);
}
// Phase 2
void
evalExpected(
TypedBlock target) throws
BadBytecode {
ClassPool cp =
classPool;
evalExpected(
cp,
target.
stackTop,
target.
stackTypes);
TypeData[]
types =
target.
localsTypes;
if (
types != null) // unless this block is dead code
evalExpected(
cp,
types.length,
types);
}
private static void
evalExpected(
ClassPool cp, int
n,
TypeData[]
types)
throws
BadBytecode
{
for (int
i = 0;
i <
n;
i++) {
TypeData td =
types[
i];
if (
td != null)
td.
evalExpectedType(
cp);
}
}
// Phase 3
public
StackMapTable toStackMap(
TypedBlock[]
blocks) {
StackMapTable.
Writer writer = new
StackMapTable.
Writer(32);
int
n =
blocks.length;
TypedBlock prev =
blocks[0];
int
offsetDelta =
prev.
length;
if (
prev.
incoming > 0) { // the first instruction is a branch target.
writer.
sameFrame(0);
offsetDelta--;
}
for (int
i = 1;
i <
n;
i++) {
TypedBlock bb =
blocks[
i];
if (
isTarget(
bb,
blocks[
i - 1])) {
bb.
resetNumLocals();
int
diffL =
stackMapDiff(
prev.
numLocals,
prev.
localsTypes,
bb.
numLocals,
bb.
localsTypes);
toStackMapBody(
writer,
bb,
diffL,
offsetDelta,
prev);
offsetDelta =
bb.
length - 1;
prev =
bb;
}
else
offsetDelta +=
bb.
length;
}
return
writer.
toStackMapTable(
cpool);
}
/**
* Returns true if cur is a branch target.
*/
private boolean
isTarget(
TypedBlock cur,
TypedBlock prev) {
int
in =
cur.
incoming;
if (
in > 1)
return true;
else if (
in < 1)
return false;
return
prev.
stop;
}
private void
toStackMapBody(
StackMapTable.
Writer writer,
TypedBlock bb,
int
diffL, int
offsetDelta,
TypedBlock prev) {
// if diffL is -100, two TypeData arrays do not share
// any elements.
int
stackTop =
bb.
stackTop;
if (
stackTop == 0) {
if (
diffL == 0) {
writer.
sameFrame(
offsetDelta);
return;
}
else if (0 >
diffL &&
diffL >= -3) {
writer.
chopFrame(
offsetDelta, -
diffL);
return;
}
else if (0 <
diffL &&
diffL <= 3) {
int[]
data = new int[
diffL];
int[]
tags =
fillStackMap(
bb.
numLocals -
prev.
numLocals,
prev.
numLocals,
data,
bb.
localsTypes);
writer.
appendFrame(
offsetDelta,
tags,
data);
return;
}
}
else if (
stackTop == 1 &&
diffL == 0) {
TypeData td =
bb.
stackTypes[0];
if (
td ==
TOP)
writer.
sameLocals(
offsetDelta,
StackMapTable.
TOP, 0);
else
writer.
sameLocals(
offsetDelta,
td.
getTypeTag(),
td.
getTypeData(
cpool));
return;
}
else if (
stackTop == 2 &&
diffL == 0) {
TypeData td =
bb.
stackTypes[0];
if (
td !=
TOP &&
td.
is2WordType()) {
// bb.stackTypes[1] must be TOP.
writer.
sameLocals(
offsetDelta,
td.
getTypeTag(),
td.
getTypeData(
cpool));
return;
}
}
int[]
sdata = new int[
stackTop];
int[]
stags =
fillStackMap(
stackTop, 0,
sdata,
bb.
stackTypes);
int[]
ldata = new int[
bb.
numLocals];
int[]
ltags =
fillStackMap(
bb.
numLocals, 0,
ldata,
bb.
localsTypes);
writer.
fullFrame(
offsetDelta,
ltags,
ldata,
stags,
sdata);
}
private int[]
fillStackMap(int
num, int
offset, int[]
data,
TypeData[]
types) {
int
realNum =
diffSize(
types,
offset,
offset +
num);
ConstPool cp =
cpool;
int[]
tags = new int[
realNum];
int
j = 0;
for (int
i = 0;
i <
num;
i++) {
TypeData td =
types[
offset +
i];
if (
td ==
TOP) {
tags[
j] =
StackMapTable.
TOP;
data[
j] = 0;
}
else {
tags[
j] =
td.
getTypeTag();
data[
j] =
td.
getTypeData(
cp);
if (
td.
is2WordType())
i++;
}
j++;
}
return
tags;
}
private static int
stackMapDiff(int
oldTdLen,
TypeData[]
oldTd,
int
newTdLen,
TypeData[]
newTd)
{
int
diff =
newTdLen -
oldTdLen;
int
len;
if (
diff > 0)
len =
oldTdLen;
else
len =
newTdLen;
if (
stackMapEq(
oldTd,
newTd,
len))
if (
diff > 0)
return
diffSize(
newTd,
len,
newTdLen);
else
return -
diffSize(
oldTd,
len,
oldTdLen);
else
return -100;
}
private static boolean
stackMapEq(
TypeData[]
oldTd,
TypeData[]
newTd, int
len) {
for (int
i = 0;
i <
len;
i++) {
TypeData td =
oldTd[
i];
if (
td ==
TOP) { // the next element to LONG/DOUBLE is TOP.
if (
newTd[
i] !=
TOP)
return false;
}
else
if (!
oldTd[
i].
equals(
newTd[
i]))
return false;
}
return true;
}
private static int
diffSize(
TypeData[]
types, int
offset, int
len) {
int
num = 0;
while (
offset <
len) {
TypeData td =
types[
offset++];
num++;
if (
td !=
TOP &&
td.
is2WordType())
offset++;
}
return
num;
}
}