--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+<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>