/*
* 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
TypedBlock extends
BasicBlock {
public int
stackTop,
numLocals;
public
TypeData[]
stackTypes,
localsTypes;
// set by a Liveness object.
// inputs[i] is true if the i-th variable is used within this block.
public boolean[]
inputs;
// working area for Liveness class.
public boolean
updating;
public int
status;
public byte[]
localsUsage;
/**
* Divides the method body into basic blocks.
* The type information of the first block is initialized.
*
* @param optmize if it is true and the method does not include
* branches, this method returns null.
*/
public static
TypedBlock[]
makeBlocks(
MethodInfo minfo,
CodeAttribute ca,
boolean
optimize)
throws
BadBytecode
{
TypedBlock[]
blocks = (
TypedBlock[])new
Maker().
make(
minfo);
if (
optimize &&
blocks.length < 2)
if (
blocks.length == 0 ||
blocks[0].
incoming == 0)
return null;
ConstPool pool =
minfo.
getConstPool();
boolean
isStatic = (
minfo.
getAccessFlags() &
AccessFlag.
STATIC) != 0;
blocks[0].
initFirstBlock(
ca.
getMaxStack(),
ca.
getMaxLocals(),
pool.
getClassName(),
minfo.
getDescriptor(),
isStatic,
minfo.
isConstructor());
new
Liveness().
compute(
ca.
iterator(),
blocks,
ca.
getMaxLocals(),
blocks[0].
localsTypes);
return
blocks;
}
protected
TypedBlock(int
pos) {
super(
pos);
localsTypes = null;
inputs = null;
updating = false;
}
protected void
toString2(
StringBuffer sbuf) {
super.toString2(
sbuf);
sbuf.
append(",\n stack={");
printTypes(
sbuf,
stackTop,
stackTypes);
sbuf.
append("}, locals={");
printTypes(
sbuf,
numLocals,
localsTypes);
sbuf.
append("}, inputs={");
if (
inputs != null)
for (int
i = 0;
i <
inputs.length;
i++)
sbuf.
append(
inputs[
i] ? "1, " : "0, ");
sbuf.
append('}');
}
private void
printTypes(
StringBuffer sbuf, int
size,
TypeData[]
types) {
if (
types == null)
return;
for (int
i = 0;
i <
size;
i++) {
if (
i > 0)
sbuf.
append(", ");
TypeData td =
types[
i];
sbuf.
append(
td == null ? "<>" :
td.
toString());
}
}
public boolean
alreadySet() {
return
localsTypes != null;
}
public void
setStackMap(int
st,
TypeData[]
stack, int
nl,
TypeData[]
locals)
throws
BadBytecode
{
stackTop =
st;
stackTypes =
stack;
numLocals =
nl;
localsTypes =
locals;
}
/*
* Computes the correct value of numLocals.
*/
public void
resetNumLocals() {
if (
localsTypes != null) {
int
nl =
localsTypes.length;
while (
nl > 0 &&
localsTypes[
nl - 1] ==
TypeTag.
TOP) {
if (
nl > 1) {
TypeData td =
localsTypes[
nl - 2];
if (
td ==
TypeTag.
LONG ||
td ==
TypeTag.
DOUBLE)
break;
}
--
nl;
}
numLocals =
nl;
}
}
public static class
Maker extends
BasicBlock.
Maker {
protected
BasicBlock makeBlock(int
pos) {
return new
TypedBlock(
pos);
}
protected
BasicBlock[]
makeArray(int
size) {
return new
TypedBlock[
size];
}
}
/**
* Initializes the first block by the given method descriptor.
*
* @param block the first basic block that this method initializes.
* @param className a dot-separated fully qualified class name.
* For example, <code>javassist.bytecode.stackmap.BasicBlock</code>.
* @param methodDesc method descriptor.
* @param isStatic true if the method is a static method.
* @param isConstructor true if the method is a constructor.
*/
void
initFirstBlock(int
maxStack, int
maxLocals,
String className,
String methodDesc, boolean
isStatic, boolean
isConstructor)
throws
BadBytecode
{
if (
methodDesc.
charAt(0) != '(')
throw new
BadBytecode("no method descriptor: " +
methodDesc);
stackTop = 0;
stackTypes = new
TypeData[
maxStack];
TypeData[]
locals = new
TypeData[
maxLocals];
if (
isConstructor)
locals[0] = new
TypeData.
UninitThis(
className);
else if (!
isStatic)
locals[0] = new
TypeData.
ClassName(
className);
int
n =
isStatic ? -1 : 0;
int
i = 1;
try {
while ((
i =
descToTag(
methodDesc,
i, ++
n,
locals)) > 0)
if (
locals[
n].
is2WordType())
locals[++
n] =
TypeTag.
TOP;
}
catch (
StringIndexOutOfBoundsException e) {
throw new
BadBytecode("bad method descriptor: "
+
methodDesc);
}
numLocals =
n;
localsTypes =
locals;
}
private static int
descToTag(
String desc, int
i,
int
n,
TypeData[]
types)
throws
BadBytecode
{
int
i0 =
i;
int
arrayDim = 0;
char
c =
desc.
charAt(
i);
if (
c == ')')
return 0;
while (
c == '[') {
++
arrayDim;
c =
desc.
charAt(++
i);
}
if (
c == 'L') {
int
i2 =
desc.
indexOf(';', ++
i);
if (
arrayDim > 0)
types[
n] = new
TypeData.
ClassName(
desc.
substring(
i0, ++
i2));
else
types[
n] = new
TypeData.
ClassName(
desc.
substring(
i0 + 1, ++
i2 - 1)
.
replace('/', '.'));
return
i2;
}
else if (
arrayDim > 0) {
types[
n] = new
TypeData.
ClassName(
desc.
substring(
i0, ++
i));
return
i;
}
else {
TypeData t =
toPrimitiveTag(
c);
if (
t == null)
throw new
BadBytecode("bad method descriptor: " +
desc);
types[
n] =
t;
return
i + 1;
}
}
private static
TypeData toPrimitiveTag(char
c) {
switch (
c) {
case 'Z' :
case 'C' :
case 'B' :
case 'S' :
case 'I' :
return
TypeTag.
INTEGER;
case 'J' :
return
TypeTag.
LONG;
case 'F' :
return
TypeTag.
FLOAT;
case 'D' :
return
TypeTag.
DOUBLE;
case 'V' :
default :
return null;
}
}
public static
String getRetType(
String desc) {
int
i =
desc.
indexOf(')');
if (
i < 0)
return "java.lang.Object";
char
c =
desc.
charAt(
i + 1);
if (
c == '[')
return
desc.
substring(
i + 1);
else if (
c == 'L')
return
desc.
substring(
i + 2,
desc.
length() - 1).
replace('/', '.');
else
return "java.lang.Object";
}
}