]> source.dussan.org Git - javassist.git/commitdiff
added computeMaxStack() in CodeAttribute.
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 18 May 2004 03:00:48 +0000 (03:00 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Tue, 18 May 2004 03:00:48 +0000 (03:00 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@105 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/CtClass.java
src/main/javassist/bytecode/CodeAnalyzer.java [new file with mode: 0644]
src/main/javassist/bytecode/CodeAttribute.java
tutorial/tutorial.html
tutorial/tutorial2.html
tutorial/tutorial3.html [new file with mode: 0644]

index 98775f47f0056d2f99680d85785cd675b423540a..001269a856f5e029aa0702bbbf8cbe273916c1b6 100644 (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.
diff --git a/src/main/javassist/bytecode/CodeAnalyzer.java b/src/main/javassist/bytecode/CodeAnalyzer.java
new file mode 100644 (file)
index 0000000..4f034ae
--- /dev/null
@@ -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);
+    }
+}
index 4e170d60f58fc7a587f92e317238f952a90582df..ec1f9a585dd449634e34bb43d8bbd2bc4d413357 100644 (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>.
      */
index b3c779ae76ac13d800a1db97735d84ca28929c8a..ce0a40cfaf4339b455f6260342bf8d63fa44b65b 100644 (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>
index b78c1ae18a90ba0e02285a24b5884d0b589fb66b..8dc5f867a2662dcc48264657e9b382e2c3e4a710 100644 (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>
diff --git a/tutorial/tutorial3.html b/tutorial/tutorial3.html
new file mode 100644 (file)
index 0000000..89c68ae
--- /dev/null
@@ -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>