From e4990e08b3f35cf6e8b6f8ea1bb30bad2fcae6af Mon Sep 17 00:00:00 2001 From: chiba Date: Tue, 18 May 2004 03:00:48 +0000 Subject: [PATCH] added computeMaxStack() in CodeAttribute. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@105 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/CtClass.java | 2 +- src/main/javassist/bytecode/CodeAnalyzer.java | 243 ++++++++++++++++++ .../javassist/bytecode/CodeAttribute.java | 12 + tutorial/tutorial.html | 2 + tutorial/tutorial2.html | 2 + tutorial/tutorial3.html | 195 ++++++++++++++ 6 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 src/main/javassist/bytecode/CodeAnalyzer.java create mode 100644 tutorial/tutorial3.html diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 98775f47..001269a8 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -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 index 00000000..4f034ae5 --- /dev/null +++ b/src/main/javassist/bytecode/CodeAnalyzer.java @@ -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 max_stack. + */ +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); + } +} diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index 4e170d60..ec1f9a58 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -219,6 +219,18 @@ public class CodeAttribute extends AttributeInfo implements Opcode { maxStack = value; } + /** + * Computes the maximum stack size and sets max_stack + * to the computed size. + * + * @throws BadBytecode if this method fails in computing. + * @return the newly computed value of max_stack + */ + public int computeMaxStack() throws BadBytecode { + maxStack = new CodeAnalyzer(this).computeMaxStack(); + return maxStack; + } + /** * Returns max_locals. */ diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html index b3c779ae..ce0a40cf 100644 --- a/tutorial/tutorial.html +++ b/tutorial/tutorial.html @@ -23,6 +23,8 @@ Shigeru Chiba
3. ClassPool
4. Class loader
5. Introspection and customization +
6. Bytecode level API +


diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index b78c1ae1..8dc5f867 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -10,6 +10,7 @@

Getting Started with Javassist
Previous page
+
Next page

5. Introspection and customization @@ -1328,6 +1329,7 @@ write:


Previous page +   Next page


Java(TM) is a trademark of Sun Microsystems, Inc.
diff --git a/tutorial/tutorial3.html b/tutorial/tutorial3.html new file mode 100644 index 00000000..89c68ae7 --- /dev/null +++ b/tutorial/tutorial3.html @@ -0,0 +1,195 @@ + + + + Javassist Tutorial + + + + + +
Getting Started with Javassist
+ +
Previous page
+ +

+6. Bytecode level API +

+ + +


+ + +

6. Bytecode level API

+ +

+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. + + +

6.1 Obtaining a ClassFile object

+ +

A javassist.bytecode.ClassFile object represents +a class file. To obtian this object, getClassFile() +in CtClass should be called. + +

Otherwise, you can construct a +javassist.bytecode.ClassFile directly from a class file. +For example, + +

+ +

+This code snippet creats a ClassFile object from +Point.class. + +

+A ClassFile object can be written back to a +class file. write() in ClassFile +writes the contents of the class file to a given +DataOutputStream. + +


+ +
+

6.2 Adding and removing a member

+ +

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

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

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


+ +
+

6.3 Traversing a method body

+ +

+To examine every bytecode instruction in a method body, +CodeIterator is useful. To otbain this object, +do as follows: + +

+ +

+A CodeIterator 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 +CodeIterator: + +

+ +


+ +
+

6.4 Producing a bytecode sequence

+ +

+A Bytecode object represents a sequence of bytecode +instructions. It is a growable array of bytecode. +Here is a sample code snippet: + +

+ +

+This produces the code attribute representing the following sequence: + +

+ +

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

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

+addOpcode() 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 getMaxStack() +on the Bytecode object. +It is also reflected on the CodeAttribute object +constructed from the Bytecode object. +To recompute the maximum stack depth of a method body, +call computeMaxStack() in CodeAttribute. + +


+ +
Previous page + +


+Java(TM) is a trademark of Sun Microsystems, Inc.
+Copyright (C) 2000-2004 by Shigeru Chiba, All rights reserved. + + -- 2.39.5