Browse Source

added computeMaxStack() in CodeAttribute.


git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@105 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
tags/rel_3_17_1_ga
chiba 20 years ago
parent
commit
e4990e08b3

+ 1
- 1
src/main/javassist/CtClass.java View File

@@ -35,7 +35,7 @@ public abstract class CtClass {
/**
* The version number of this release.
*/
public static final String version = "3.0 RC0";
public static final String version = "3.0 beta";

/**
* Prints the version number and the copyright notice.

+ 243
- 0
src/main/javassist/bytecode/CodeAnalyzer.java View File

@@ -0,0 +1,243 @@
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2004 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;

/**
* Utility for computing <code>max_stack</code>.
*/
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)) // return, ireturn, athrow, ...
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) // stackDepth is 1 if empty
return false;
else
throw new BadBytecode(
"sorry, cannot compute this data flow due to JSR");
case RET :
if (stackDepth == 1) // stackDepth is 1 if empty
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.
*/
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);
}
}

+ 12
- 0
src/main/javassist/bytecode/CodeAttribute.java View File

@@ -219,6 +219,18 @@ public class CodeAttribute extends AttributeInfo implements Opcode {
maxStack = value;
}

/**
* Computes the maximum stack size and sets <code>max_stack</code>
* to the computed size.
*
* @throws BadBytecode if this method fails in computing.
* @return the newly computed value of <code>max_stack</code>
*/
public int computeMaxStack() throws BadBytecode {
maxStack = new CodeAnalyzer(this).computeMaxStack();
return maxStack;
}

/**
* Returns <code>max_locals</code>.
*/

+ 2
- 0
tutorial/tutorial.html View File

@@ -23,6 +23,8 @@ Shigeru Chiba
<br>3. <a href="#pool">ClassPool</a>
<br>4. <a href="#load">Class loader</a>
<br>5. <a href="tutorial2.html#intro">Introspection and customization</a>
<br>6. <a href="tutorial3.html#intro">Bytecode level API</a>

</ul>

<p><br>

+ 2
- 0
tutorial/tutorial2.html View File

@@ -10,6 +10,7 @@
<div align="right">Getting Started with Javassist</div>

<div align="left"><a href="tutorial.html">Previous page</a></div>
<div align="right"><a href="tutorial3.html">Next page</a></div>

<p>
<a href="#intro">5. Introspection and customization</a>
@@ -1328,6 +1329,7 @@ write:
<p><br>

<a href="tutorial.html">Previous page</a>
&nbsp;&nbsp;&nbsp;<a href="tutorial3.html">Next page</a>

<hr>
Java(TM) is a trademark of Sun Microsystems, Inc.<br>

+ 195
- 0
tutorial/tutorial3.html View File

@@ -0,0 +1,195 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Javassist Tutorial</title>
<link rel="stylesheet" type="text/css" href="brown.css">
</head>

<body>

<div align="right">Getting Started with Javassist</div>

<div align="left"><a href="tutorial2.html">Previous page</a></div>

<p>
<a href="#intro">6. Bytecode level API</a>
<ul>
<li><a href="#classfile">Obtaining a <code>ClassFile</code> object</a>
<br><li><a href="#member">Adding and removing a member</a>
<br><li><a href="#traverse">Traversing a method body</a>
<br><li><a href="#bytecode">Producing a bytecode sequence</a>

</ul>


<p><br>

<a name="intro">
<h2>6. Bytecode level API</h2>

<p>
Javassist also provides lower-level API for directly editing
a class file. To use this level of API, you need detailed
knowledge of the Java bytecode and the class file format
while this level of API allows you any kind of modification
of class files.

<a name="classfile">
<h3>6.1 Obtaining a <code>ClassFile</code> object</h3>

<p>A <code>javassist.bytecode.ClassFile</code> object represents
a class file. To obtian this object, <code>getClassFile()</code>
in <code>CtClass</code> should be called.

<p>Otherwise, you can construct a
<code>javassist.bytecode.ClassFile</code> directly from a class file.
For example,

<ul><pre>
FileInputStream fin
= new BufferedInputStream(new FileInputStream("Point.class"));
ClassFile cf = new ClassFile(new DataInputStream(fin));
</pre></ul>

<p>
This code snippet creats a <code>ClassFile</code> object from
<code>Point.class</code>.

<p>
A <code>ClassFile</code> object can be written back to a
class file. <code>write()</code> in <code>ClassFile</code>
writes the contents of the class file to a given
<code>DataOutputStream</code>.

<p><br>

<a name="member">
<h3>6.2 Adding and removing a member</h3>

<p>
<code>ClassFile</code> provides <code>addField()</code> and
<code>addMethod()</code> for adding a field or a method (note that
a constructor is regarded as a method at the bytecode level).
It also provides <code>addAttribute()</code> for adding an attribute
to the class file.

<p>
Note that <code>FieldInfo</code>, <code>MethodInfo</code>, and
<code>AttributeInfo</code> objects include a link to a
<code>ConstPool</code> (constant pool table) object. The <code>ConstPool</code>
object must be common to the <code>ClassFile</code> object and
a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object
that is added to that <code>ClassFile</code> object.
In other words, a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object
must not be shared among different <code>ClassFile</code> objects.

<p>
To remove a field or a method, you must first obtain a <code>java.util.List</code>
object containing all the fields of the class. <code>getFields()</code>
and <code>getMethods()</code> return the lists. A field or a method can
be removed by calling <code>remove()</code> on the <code>List</code> object.

<p><br>

<a name="traverse">
<h3>6.3 Traversing a method body</h3>

<p>
To examine every bytecode instruction in a method body,
<code>CodeIterator</code> is useful. To otbain this object,
do as follows:

<ul><pre>
ClassFile cf = ... ;
MethodInfo minfo = cf.getMethod("move"); // we assume move is not overloaded.
CodeAttribute ca = minfo.getCodeAttribute();
CodeIterator i = ca.iterator();
</pre></ul>

<p>
A <code>CodeIterator</code> object allows you to visit every
bytecode instruction one by one from the beginning to the end.
The following methods are part of the methods declared in
<code>CodeIterator</code>:

<ul>
<li><code>void begin()</code><br>
Move to the first instruction.<br>
<li><code>void move(int index)</code><br>
Move to the instruction specified by the given index.<br>
<li><code>hasNext()</code><br>
Returns true if there is more instructions.<br>
<li><code>next()</code><br>
Returns the index of the next instruction.
<em>Note that it does not return the opcode of the next
instruction.</em><br>
<li><code>int byteAt(int index)</code><br>
Returns the unsigned 8bit value at the index.<br>
<li><code>int u16bitAt(int index)</code><br>
Returns the unsigned 16bit value at the index.<br>
<li><code>int write(byte[] code, int index)</code><br>
Writes a byte array at the index.<br>
<li><code>void insert(int index, byte[] code)</code><br>
Inserts a byte array at the index.
Branch offsets etc. are automatically adjusted.<br>
</ul>

<p><br>

<a name="bytecode">
<h3>6.4 Producing a bytecode sequence</h3>

<p>
A <code>Bytecode</code> object represents a sequence of bytecode
instructions. It is a growable array of bytecode.
Here is a sample code snippet:

<ul><pre>
ConstPool cp = ...; // constant pool table
Bytecode b = new Bytecode(cp, 1, 0);
b.addIconst(3);
b.addReturn(CtClass.intType);
CodeAttribute ca = b.toCodeAttribute();
</pre></ul>

<p>
This produces the code attribute representing the following sequence:

<ul><pre>
iconst_3
ireturn
</pre></ul>

<p>
You can also obtain a byte array containing this sequence by
calling <code>get()</code> in <code>Bytecode</code>. The
obtained array can be inserted in another code attribute.

<p>
While <code>Bytecode</code> provides a number of methods for adding a
specific instruction to the sequence, it provides
<code>addOpcode()</code> for adding an 8bit opcode and
<code>addIndex()</code> for adding an index.
The 8bit value of each opcode is defined in the <code>Opcode</code>
interface.

<p>
<code>addOpcode()</code> and other methods for adding a specific
instruction are automatically maintain the maximum stack depth
unless the control flow does not include a branch.
This value can be obtained by calling <code>getMaxStack()</code>
on the <code>Bytecode</code> object.
It is also reflected on the <code>CodeAttribute</code> object
constructed from the <code>Bytecode</code> object.
To recompute the maximum stack depth of a method body,
call <code>computeMaxStack()</code> in <code>CodeAttribute</code>.

<p><br>

<a href="tutorial2.html">Previous page</a>

<hr>
Java(TM) is a trademark of Sun Microsystems, Inc.<br>
Copyright (C) 2000-2004 by Shigeru Chiba, All rights reserved.
</body>
</html>

Loading…
Cancel
Save