git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@105 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -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. |
@@ -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); | |||
} | |||
} |
@@ -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>. | |||
*/ |
@@ -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> |
@@ -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> | |||
<a href="tutorial3.html">Next page</a> | |||
<hr> | |||
Java(TM) is a trademark of Sun Microsystems, Inc.<br> |
@@ -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> |