From: chiba Date: Wed, 12 Jan 2005 16:09:47 +0000 (+0000) Subject: implemented removeMethod() etc. X-Git-Tag: rel_3_17_1_ga~465 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e793a77000f14fd8272388f7b2878a54e2d628ca;p=javassist.git implemented removeMethod() etc. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@151 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/License.html b/License.html index f8d626da..7a734c3a 100644 --- a/License.html +++ b/License.html @@ -353,7 +353,7 @@ MISCELLANEOUS.

The Original Code is Javassist.

The Initial Developer of the Original Code is Shigeru Chiba. Portions created by the Initial Developer are
  - Copyright (C) 1999-2004 Shigeru Chiba. All Rights Reserved. + Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.

Contributor(s): ______________________________________.

Alternatively, the contents of this file may be used under the terms of diff --git a/Readme.html b/Readme.html index 61572855..910429cf 100644 --- a/Readme.html +++ b/Readme.html @@ -7,7 +7,7 @@

Javassist version 3

-

Copyright (C) 2000-2004 by Shigeru Chiba, All rights reserved.

+

Copyright (C) 2000-2005 by Shigeru Chiba, All rights reserved.


@@ -255,10 +255,12 @@ see javassist.Dump.

Changes

-

- version 3.0 +

- version 3.0 in January, 2005

- version 3.0 RC1 in September 13, 2004. diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 29512ace..78b40150 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -44,7 +44,7 @@ public abstract class CtClass { /** * The version number of this release. */ - public static final String version = "3.0 RC1"; + public static final String version = "3.0"; /** * Prints the version number and the copyright notice. @@ -55,7 +55,7 @@ public abstract class CtClass { */ public static void main(String[] args) { System.out.println("Javassist version " + CtClass.version); - System.out.println("Copyright (C) 1999-2004 Shigeru Chiba." + System.out.println("Copyright (C) 1999-2005 Shigeru Chiba." + " All Rights Reserved."); } @@ -746,6 +746,16 @@ public abstract class CtClass { checkModify(); } + /** + * Removes a constructor declared in this class. + * + * @param c removed constructor. + * @throws NotFoundException if the constructor is not found. + */ + public void removeConstructor(CtConstructor c) throws NotFoundException { + checkModify(); + } + /** * Adds a method. */ @@ -753,6 +763,16 @@ public abstract class CtClass { checkModify(); } + /** + * Removes a method declared in this class. + * + * @param m removed method. + * @throws NotFoundException if the method is not found. + */ + public void removeMethod(CtMethod m) throws NotFoundException { + checkModify(); + } + /** * Adds a field. * @@ -825,6 +845,16 @@ public abstract class CtClass { checkModify(); } + /** + * Removes a field declared in this class. + * + * @param f removed field. + * @throws NotFoundException if the field is not found. + */ + public void removeField(CtField f) throws NotFoundException { + checkModify(); + } + /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 5b249a42..d567a9b9 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -52,12 +52,13 @@ class CtClassType extends CtClass { boolean wasChanged; private boolean wasFrozen; boolean wasPruned; + boolean memberRemoved; ClassFile classfile; - private CtField fieldsCache; - private CtConstructor constructorsCache; + private CtMember fieldsCache; + private CtMember methodsCache; + private CtMember constructorsCache; private CtConstructor classInitializerCache; - private CtMethod methodsCache; private AccessorMaker accessors; @@ -73,7 +74,7 @@ class CtClassType extends CtClass { CtClassType(String name, ClassPool cp) { super(name); classPool = cp; - wasChanged = wasFrozen = wasPruned = false; + wasChanged = wasFrozen = wasPruned = memberRemoved = false; classfile = null; accessors = null; fieldInitializers = null; @@ -129,7 +130,7 @@ class CtClassType extends CtClass { buffer.append(" extends ??"); } - CtField field = getFieldsCache(); + CtMember field = getFieldsCache(); buffer.append(" fields="); while (field != null) { buffer.append(field); @@ -137,7 +138,7 @@ class CtClassType extends CtClass { field = field.next; } - CtConstructor c = getConstructorsCache(); + CtMember c = getConstructorsCache(); buffer.append(" constructors="); while (c != null) { buffer.append(c); @@ -145,7 +146,7 @@ class CtClassType extends CtClass { c = c.next; } - CtMethod m = getMethodsCache(); + CtMember m = getMethodsCache(); buffer.append(" methods="); while (m != null) { buffer.append(m); @@ -471,7 +472,7 @@ class CtClassType extends CtClass { } catch (NotFoundException e) {} - CtField cf = ((CtClassType)cc).getFieldsCache(); + CtMember cf = ((CtClassType)cc).getFieldsCache(); while (cf != null) { if (Modifier.isPublic(cf.getModifiers())) alist.add(cf); @@ -511,25 +512,25 @@ class CtClassType extends CtClass { } public CtField[] getDeclaredFields() { - CtField cf = getFieldsCache(); + CtMember cf = getFieldsCache(); int num = CtField.count(cf); CtField[] cfs = new CtField[num]; int i = 0; while (cf != null) { - cfs[i++] = cf; + cfs[i++] = (CtField)cf; cf = cf.next; } return cfs; } - protected CtField getFieldsCache() { + protected CtMember getFieldsCache() { if (fieldsCache == null) { List list = getClassFile2().getFields(); int n = list.size(); for (int i = 0; i < n; ++i) { FieldInfo finfo = (FieldInfo)list.get(i); - fieldsCache = CtField.append(fieldsCache, + fieldsCache = CtMember.append(fieldsCache, new CtField(finfo, this)); } } @@ -546,10 +547,10 @@ class CtClassType extends CtClass { } private CtField getDeclaredField2(String name) { - CtField cf = getFieldsCache(); + CtMember cf = getFieldsCache(); while (cf != null) { if (cf.getName().equals(name)) - return cf; + return (CtField)cf; cf = cf.next; } @@ -558,18 +559,18 @@ class CtClassType extends CtClass { } public CtBehavior[] getDeclaredBehaviors() { - CtConstructor cc = getConstructorsCache(); - CtMethod cm = getMethodsCache(); - int num = CtMethod.count(cm) + CtConstructor.count(cc); + CtMember cc = getConstructorsCache(); + CtMember cm = getMethodsCache(); + int num = CtMember.count(cm) + CtMember.count(cc); CtBehavior[] cb = new CtBehavior[num]; int i = 0; while (cc != null) { - cb[i++] = cc; + cb[i++] = (CtBehavior)cc; cc = cc.next; } while (cm != null) { - cb[i++] = cm; + cb[i++] = (CtBehavior)cm; cm = cm.next; } @@ -602,31 +603,31 @@ class CtClassType extends CtClass { public CtConstructor getConstructor(String desc) throws NotFoundException { - CtConstructor cc = getConstructorsCache(); + CtConstructor cc = (CtConstructor)getConstructorsCache(); while (cc != null) { if (cc.getMethodInfo2().getDescriptor().equals(desc)) return cc; - cc = cc.next; + cc = (CtConstructor)cc.next; } return super.getConstructor(desc); } public CtConstructor[] getDeclaredConstructors() { - CtConstructor cc = getConstructorsCache(); - int num = CtConstructor.count(cc); + CtMember cc = getConstructorsCache(); + int num = CtMember.count(cc); CtConstructor[] ccs = new CtConstructor[num]; int i = 0; while (cc != null) { - ccs[i++] = cc; + ccs[i++] = (CtConstructor)cc; cc = cc.next; } return ccs; } - protected CtConstructor getConstructorsCache() { + protected CtMember getConstructorsCache() { if (constructorsCache == null) { List list = getClassFile2().getMethods(); int n = list.size(); @@ -634,8 +635,8 @@ class CtClassType extends CtClass { MethodInfo minfo = (MethodInfo)list.get(i); if (minfo.isConstructor()) constructorsCache - = CtConstructor.append(constructorsCache, - new CtConstructor(minfo, this)); + = CtMember.append(constructorsCache, + new CtConstructor(minfo, this)); } } @@ -675,10 +676,10 @@ class CtClassType extends CtClass { catch (NotFoundException e) {} if (cc instanceof CtClassType) { - CtMethod cm = ((CtClassType)cc).getMethodsCache(); + CtMember cm = ((CtClassType)cc).getMethodsCache(); while (cm != null) { if (Modifier.isPublic(cm.getModifiers())) - h.put(cm.getStringRep(), cm); + h.put(((CtMethod)cm).getStringRep(), cm); cm = cm.next; } @@ -699,13 +700,13 @@ class CtClassType extends CtClass { private static CtMethod getMethod0(CtClass cc, String name, String desc) { if (cc instanceof CtClassType) { - CtMethod cm = ((CtClassType)cc).getMethodsCache(); + CtMethod cm = (CtMethod)((CtClassType)cc).getMethodsCache(); while (cm != null) { if (cm.getName().equals(name) && cm.getMethodInfo2().getDescriptor().equals(desc)) return cm; - cm = cm.next; + cm = (CtMethod)cm.next; } } @@ -733,12 +734,12 @@ class CtClassType extends CtClass { } public CtMethod[] getDeclaredMethods() { - CtMethod cm = getMethodsCache(); - int num = CtMethod.count(cm); + CtMember cm = getMethodsCache(); + int num = CtMember.count(cm); CtMethod[] cms = new CtMethod[num]; int i = 0; while (cm != null) { - cms[i++] = cm; + cms[i++] = (CtMethod)cm; cm = cm.next; } @@ -746,10 +747,10 @@ class CtClassType extends CtClass { } public CtMethod getDeclaredMethod(String name) throws NotFoundException { - CtMethod m = getMethodsCache(); + CtMember m = getMethodsCache(); while (m != null) { if (m.getName().equals(name)) - return m; + return (CtMethod)m; m = m.next; } @@ -762,28 +763,28 @@ class CtClassType extends CtClass { throws NotFoundException { String desc = Descriptor.ofParameters(params); - CtMethod m = getMethodsCache(); + CtMethod m = (CtMethod)getMethodsCache(); while (m != null) { if (m.getName().equals(name) && m.getMethodInfo2().getDescriptor().startsWith(desc)) return m; - m = m.next; + m = (CtMethod)m.next; } throw new NotFoundException(name + "(..) is not found in " + getName()); } - protected CtMethod getMethodsCache() { + protected CtMember getMethodsCache() { if (methodsCache == null) { List list = getClassFile2().getMethods(); int n = list.size(); for (int i = 0; i < n; ++i) { MethodInfo minfo = (MethodInfo)list.get(i); if (minfo.isMethod()) - methodsCache = CtMethod.append(methodsCache, - new CtMethod(minfo, this)); + methodsCache = CtMember.append(methodsCache, + new CtMethod(minfo, this)); } } @@ -824,6 +825,18 @@ class CtClassType extends CtClass { } } + public void removeField(CtField f) throws NotFoundException { + checkModify(); + FieldInfo fi = f.getFieldInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getFields().remove(fi)) { + fieldsCache = CtMember.remove(fieldsCache, f); + memberRemoved = true; + } + else + throw new NotFoundException(f.toString()); + } + public CtConstructor makeClassInitializer() throws CannotCompileException { @@ -846,22 +859,46 @@ class CtClassType extends CtClass { throw new CannotCompileException("cannot add"); getConstructorsCache(); - constructorsCache = CtConstructor.append(constructorsCache, c); + constructorsCache = (CtConstructor)CtMember.append(constructorsCache, c); getClassFile2().addMethod(c.getMethodInfo2()); } + public void removeConstructor(CtConstructor m) throws NotFoundException { + checkModify(); + MethodInfo mi = m.getMethodInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getMethods().remove(mi)) { + constructorsCache = CtMember.remove(constructorsCache, m); + memberRemoved = true; + } + else + throw new NotFoundException(m.toString()); + } + public void addMethod(CtMethod m) throws CannotCompileException { checkModify(); if (m.getDeclaringClass() != this) throw new CannotCompileException("cannot add"); getMethodsCache(); - methodsCache = CtMethod.append(methodsCache, m); + methodsCache = CtMember.append(methodsCache, m); getClassFile2().addMethod(m.getMethodInfo2()); if ((m.getModifiers() & Modifier.ABSTRACT) != 0) setModifiers(getModifiers() | Modifier.ABSTRACT); } + public void removeMethod(CtMethod m) throws NotFoundException { + checkModify(); + MethodInfo mi = m.getMethodInfo2(); + ClassFile cf = getClassFile2(); + if (cf.getMethods().remove(mi)) { + methodsCache = CtMember.remove(methodsCache, m); + memberRemoved = true; + } + else + throw new NotFoundException(m.toString()); + } + public byte[] getAttribute(String name) { AttributeInfo ai = getClassFile2().getAttribute(name); if (ai == null) @@ -910,6 +947,11 @@ class CtClassType extends CtClass { if (isModified()) { checkPruned("toBytecode"); ClassFile cf = getClassFile2(); + if (memberRemoved) { + cf.compact(); + memberRemoved = false; + } + modifyClassConstructor(cf); modifyConstructors(cf); cf.write(out); diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java index cac15b36..707d6863 100644 --- a/src/main/javassist/CtConstructor.java +++ b/src/main/javassist/CtConstructor.java @@ -33,8 +33,6 @@ import javassist.compiler.CompileError; * @see CtNewConstructor */ public final class CtConstructor extends CtBehavior { - protected CtConstructor next; - protected CtConstructor(MethodInfo minfo, CtClass declaring) { super(declaring, minfo); next = null; @@ -132,30 +130,6 @@ public final class CtConstructor extends CtBehavior { } } - static CtConstructor append(CtConstructor list, CtConstructor tail) { - tail.next = null; - if (list == null) - return tail; - else { - CtConstructor lst = list; - while (lst.next != null) - lst = lst.next; - - lst.next = tail; - return list; - } - } - - static int count(CtConstructor m) { - int n = 0; - while (m != null) { - ++n; - m = m.next; - } - - return n; - } - /** * Returns true if this object represents a constructor. */ diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java index c537ed58..cfda5a22 100644 --- a/src/main/javassist/CtField.java +++ b/src/main/javassist/CtField.java @@ -27,7 +27,6 @@ import javassist.compiler.ast.ASTree; */ public class CtField extends CtMember { protected FieldInfo fieldInfo; - CtField next; /** * Creates a CtField object. @@ -98,6 +97,14 @@ public class CtField extends CtMember { next = null; } + /** + * Returns a String representation of the object. + */ + public String toString() { + return getDeclaringClass().getName() + "." + getName() + + ":" + fieldInfo.getDescriptor(); + } + protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); @@ -149,30 +156,6 @@ public class CtField extends CtMember { throw new CannotCompileException("not a field"); } - static CtField append(CtField list, CtField tail) { - tail.next = null; - if (list == null) - return tail; - else { - CtField lst = list; - while (lst.next != null) - lst = lst.next; - - lst.next = tail; - return list; - } - } - - static int count(CtField f) { - int n = 0; - while (f != null) { - ++n; - f = f.next; - } - - return n; - } - /** * Returns the FieldInfo representing the field in the class file. */ diff --git a/src/main/javassist/CtMember.java b/src/main/javassist/CtMember.java index 05d02bf8..59741352 100644 --- a/src/main/javassist/CtMember.java +++ b/src/main/javassist/CtMember.java @@ -20,10 +20,54 @@ package javassist; * or a method. */ public abstract class CtMember { + protected CtMember next; // for internal use protected CtClass declaringClass; protected CtMember(CtClass clazz) { declaringClass = clazz; } + static CtMember append(CtMember list, CtMember tail) { + tail.next = null; + if (list == null) + return tail; + else { + CtMember lst = list; + while (lst.next != null) + lst = lst.next; + + lst.next = tail; + return list; + } + } + + static int count(CtMember f) { + int n = 0; + while (f != null) { + ++n; + f = f.next; + } + + return n; + } + + static CtMember remove(CtMember list, CtMember m) { + CtMember top = list; + if (list == null) + return null; + else if (list == m) + return list.next; + else + while (list.next != null) { + if (list.next == m) { + list.next = list.next.next; + break; + } + + list = list.next; + } + + return top; + } + public String toString() { StringBuffer buffer = new StringBuffer(getClass().getName()); buffer.append("@"); diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java index 9d9416b3..8195e9b4 100644 --- a/src/main/javassist/CtMethod.java +++ b/src/main/javassist/CtMethod.java @@ -27,7 +27,6 @@ import javassist.bytecode.*; * @see CtNewMethod */ public final class CtMethod extends CtBehavior { - protected CtMethod next; protected String cachedStringRep; CtMethod(MethodInfo minfo, CtClass declaring) { @@ -133,30 +132,6 @@ public final class CtMethod extends CtBehavior { } } - static CtMethod append(CtMethod list, CtMethod tail) { - tail.next = null; - if (list == null) - return tail; - else { - CtMethod lst = list; - while (lst.next != null) - lst = lst.next; - - lst.next = tail; - return list; - } - } - - static int count(CtMethod m) { - int n = 0; - while (m != null) { - ++n; - m = m.next; - } - - return n; - } - /** * Returns a hash code value for the method. * If two methods have the same name and signature, then diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java index 28b01846..06d89904 100644 --- a/src/main/javassist/bytecode/AttributeInfo.java +++ b/src/main/javassist/bytecode/AttributeInfo.java @@ -222,4 +222,18 @@ public class AttributeInfo { attr.write(out); } } + + static LinkedList copyAll(LinkedList list, ConstPool cp) { + if (list == null) + return null; + + LinkedList newList = new LinkedList(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + AttributeInfo attr = (AttributeInfo)list.get(i); + newList.add(attr.copy(cp, null)); + } + + return newList; + } } diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index 4f85f667..5c4bc1cc 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -99,13 +99,33 @@ public final class ClassFile { } /** - * Discards all attributes, associated with both the class file and - * the members such as a code attribute and exceptions attribute. - * The unused constant pool entries are also discarded (a new packed - * constant pool is constructed). + * Eliminates dead constant pool items. If a method or a field is removed, + * the constant pool items used by that method/field become dead items. + * This method recreates a constant pool. */ - public void prune() { + public void compact() { + ConstPool cp = compact0(); + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.compact(cp); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.compact(cp); + } + + attributes = AttributeInfo.copyAll(attributes, cp); + constPool = cp; + } + + private ConstPool compact0() { ConstPool cp = new ConstPool(thisclassname); + thisClass = cp.getThisClassInfo(); superClass = cp.addClassInfo(getSuperclass()); if (interfaces != null) { @@ -115,6 +135,18 @@ public final class ClassFile { = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); } + return cp; + } + + /** + * Discards all attributes, associated with both the class file and + * the members such as a code attribute and exceptions attribute. + * The unused constant pool entries are also discarded (a new packed + * constant pool is constructed). + */ + public void prune() { + ConstPool cp = compact0(); + ArrayList list = methods; int n = list.size(); for (int i = 0; i < n; ++i) { diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index 4c7e27bb..97742d8a 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -82,19 +82,14 @@ public class CodeAttribute extends AttributeInfo implements Opcode { maxLocals = src.getMaxLocals(); exceptions = src.getExceptionTable().copy(cp, classnames); info = src.copyCode(cp, classnames, exceptions, this); - attributes = new LinkedList(); - /* Since an index into the source constant pool table may not - be translated, we don't copy the attributes. - */ - /* + attributes = new LinkedList(); List src_attr = src.getAttributes(); int num = src_attr.size(); for (int i = 0; i < num; ++i) { AttributeInfo ai = (AttributeInfo)src_attr.get(i); attributes.add(ai.copy(cp, classnames)); } - */ } CodeAttribute(ConstPool cp, int name_id, DataInputStream in) diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java index ff45f1ba..8c2ed0a1 100644 --- a/src/main/javassist/bytecode/FieldInfo.java +++ b/src/main/javassist/bytecode/FieldInfo.java @@ -59,6 +59,21 @@ public final class FieldInfo { read(in); } + /** + * Copies all constant pool items to a given new constant pool + * and replaces the original items with the new ones. + * This is used for garbage collecting the items of removed fields + * and methods. + * + * @param cp the destination + */ + void compact(ConstPool cp) { + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + attribute = AttributeInfo.copyAll(attribute, cp); + constPool = cp; + } + void prune(ConstPool cp) { int index = getConstantValue(); if (index == 0) diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index 3f3d93ef..3889fe54 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -30,13 +30,9 @@ import java.util.Map; */ public final class MethodInfo { ConstPool constPool; - int accessFlags; - int name; - int descriptor; - LinkedList attribute; // may be null // Bill, do you really need this? @@ -115,6 +111,21 @@ public final class MethodInfo { + constPool.getUtf8Info(descriptor); } + /** + * Copies all constant pool items to a given new constant pool + * and replaces the original items with the new ones. + * This is used for garbage collecting the items of removed fields + * and methods. + * + * @param cp the destination + */ + void compact(ConstPool cp) { + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + attribute = AttributeInfo.copyAll(attribute, cp); + constPool = cp; + } + void prune(ConstPool cp) { attribute = null; name = cp.addUtf8Info(getName()); diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 7ef1fedb..d8d7381d 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -1,1444 +1,1464 @@ - - - - Javassist Tutorial - - - - - -

Getting Started with Javassist
- -
Previous page
-
Next page
- -

-4. Introspection and customization -

- -


- - -

4. Introspection and customization

- -

CtClass provides methods for introspection. The -introspective ability of Javassist is compatible with that of -the Java reflection API. CtClass provides -getName(), getSuperclass(), -getMethods(), and so on. -CtClass also provides methods for modifying a class -definition. It allows to add a new field, constructor, and method. -Instrumenting a method body is also possible. - -

-Methods are represented by CtMethod objects. -CtMethod provides several methods for modifying -the definition of the method. Note that if a method is inherited -from a super class, then -the same CtMethod object -that represents the inherited method represents the method declared -in that super class. -A CtMethod object corresponds to every method declaration. - -

-For example, if class Point declares method move() -and a subclass ColorPoint of Point does -not override move(), the two move() methods -declared in Point and inherited in ColorPoint -are represented by the identical CtMethod object. -If the method definition represented by this -CtMethod object is modified, the modification is -reflected on both the methods. -If you want to modify only the move() method in -ColorPoint, you first have to add to ColorPoint -a copy of the CtMethod object representing move() -in Point. A copy of the the CtMethod object -can be obtained by CtNewMethod.copy(). - - -


- - - -


- -

Javassist also provides low-level API for directly editing a raw -class file. For example, getClassFile() in -CtClass returns a ClassFile object -representing a raw class file. getMethodInfo() in -CtMethod returns a MethodInfo object -representing a method_info structure included in a class -file. The low-level API uses the vocabulary from the Java Virtual -machine specification. The users must have the knowledge about class -files and bytecode. For more details, the users should see the -javassist.bytecode package. - -

The class files modified by Javassist requires the -javassist.runtime package for runtime support -only if some special identifiers starting with $ -are used. Those special identifiers are described below. -The class files modified without those special identifiers -do not need the javassist.runtime package or any -other Javassist packages at runtime. -For more details, see the API documentation -of the javassist.runtime package. - -


- -
-

4.1 Inserting source text at the beginning/end of a method body

- -

CtMethod and CtConstructor provide -methods insertBefore(), insertAfter(), and -addCatch(). They are used for inserting a code fragment -into the body of an existing method. The users can specify those code -fragments with source text written in Java. -Javassist includes a simple Java compiler for processing source -text. It receives source text -written in Java and compiles it into Java bytecode, which will be -inlined into a method body. - -

-Inserting a code fragment at the position specified by a line number -is also possible -(if the line number table is contained in the class file). -insertAt() in CtMethod and -CtConstructor takes source text and a line number in the source -file of the original class definition. -It compiles the source text and inserts the compiled code at the line number. - -

The methods insertBefore(), insertAfter(), -addCatch(), and insertAt() -receive a String object representing -a statement or a block. A statement is a single control structure like -if and while or an expression ending with -a semi colon (;). A block is a set of -statements surrounded with braces {}. -Hence each of the following lines is an example of valid statement or block: - -

- -

The statement and the block can refer to fields and methods. -They can also refer to the parameters -to the method that they are inserted into -if that method was compiled with the -g option -(to include a local variable attribute in the class file). -Otherwise, they must access the method parameters through the special -variables $0, $1, $2, ... described -below. -Accessing local variables declared in the method is not allowed -although declaring a new local variable in the block is allowed. -However, insertAt() allows the statement and the block -to access local variables -if these variables are available at the specified line number -and the target method was compiled with the -g option. - - - - -

The String object passed to the methods -insertBefore(), insertAfter(), -addCatch(), and insertAt() are compiled by -the compiler included in Javassist. -Since the compiler supports language extensions, -several identifiers starting with $ -have special meaning: - -

- -

$0, $1, $2, ...

- -

The parameters passed to the target method -are accessible with -$0, $1, $2, ... instead of -the original parameter names. -$1 represents the -first parameter, $2 represents the second parameter, and -so on. The types of those variables are identical to the parameter -types. -$0 is -equivalent to this. If the method is static, -$0 is not available. - -

These variables are used as following. Suppose that a class -Point: - -

    class Point { - int x, y; - void move(int dx, int dy) { x += dx; y += dy; } -} -
- -

To print the values of dx and dy -whenever the method move() is called, execute this -program: - -

- -

Note that the source text passed to insertBefore() is -surrounded with braces {}. -insertBefore() accepts only a single statement or a block -surrounded with braces. - -

The definition of the class Point after the -modification is like this: - -

    class Point { - int x, y; - void move(int dx, int dy) { - { System.out.println(dx); System.out.println(dy); } - x += dx; y += dy; - } -} -
- -

$1 and $2 are replaced with -dx and dy, respectively. - -

$1, $2, $3 ... are -updatable. If a new value is assigend to one of those variables, -then the value of the parameter represented by that variable is -also updated. - - -

$args

- -

The variable $args represents an array of all the -parameters. The type of that variable is an array of class -Object. If a parameter type is a primitive type such as -int, then the parameter value is converted into a wrapper -object such as java.lang.Integer to store in -$args. Thus, $args[0] is equivalent to -$1 unless the type of the first parameter is a primitive -type. Note that $args[0] is not equivalent to -$0; $0 represents this. - -

If an array of Object is assigned to -$args, then each element of that array is -assigned to each parameter. If a parameter type is a primitive -type, the type of the corresponding element must be a wrapper type. -The value is converted from the wrapper type to the primitive type -before it is assigned to the parameter. - -

$$

- -

The variable $$ is abbreviation of a list of -all the parameters separated by commas. -For example, if the number of the parameters -to method move() is three, then - -

- -

is equivalent to this: - -

- -

If move() does not take any parameters, -then move($$) is -equivalent to move(). - -

$$ can be used with another method. -If you write an expression: - -

- -

then this expression is equivalent to: - -

- -

Note that $$ enables generic notation of method call -with respect to the number of parameters. -It is typically used with $proceed shown later. - -

$cflow

- -

$cflow means "control flow". -This read-only variable returns the depth of the recursive calls -to a specific method. - -

Suppose that the method shown below is represented by a -CtMethod object cm: - -

- -

To use $cflow, first declare that $cflow -is used for monitoring calls to the method fact(): - -

- -

The parameter to useCflow() is the identifier of the -declared $cflow variable. Any valid Java name can be -used as the identifier. Since the identifier can also include -. (dot), for example, "my.Test.fact" -is a valid identifier. - -

Then, $cflow(fact) represents the depth of the -recursive calls to the method specified by cm. The value -of $cflow(fact) is 0 (zero) when the method is -first called whereas it is 1 when the method is recursively called -within the method. For example, - -

- -

translates the method fact() so that it shows the -parameter. Since the value of $cflow(fact) is checked, -the method fact() does not show the parameter if it is -recursively called within fact(). - -

The value of $cflow is the number of stack frames -associated with the specified method cm -under the current topmost -stack frame for the current thread. $cflow is also -accessible within a method different from the specified method -cm. - -

$r

- -

$r represents the result type (return type) of the method. -It must be used as the cast type in a cast expression. -For example, this is a typical use: - -

- -

If the result type is a primitive type, then ($r) -follows special semantics. First, if the operand type of the cast -expression is a primitive type, ($r) works as a normal -cast operator to the result type. -On the other hand, if the operand type is a wrapper type, -($r) converts from the wrapper type to the result type. -For example, if the result type is int, then -($r) converts from java.lang.Integer to -int. - -

If the result type is void, then -($r) does not convert a type; it does nothing. -However, if the operand is a call to a void method, -then ($r) results in null. For example, -if the result type is void and -foo() is a void method, then - -

- -

is a valid statement. - -

The cast operator ($r) is also useful in a -return statement. Even if the result type is -void, the following return statement is valid: - -

- -

Here, result is some local variable. -Since ($r) is specified, the resulting value is -discarded. -This return statement is regarded as the equivalent -of the return statement without a resulting value: - -

- -

$w

- -

$w represents a wrapper type. -It must be used as the cast type in a cast expression. -($w) converts from a primitive type to the corresponding -wrapper type. - -The following code is an example: - -

- -

The selected wrapper type depends on the type of the expression -following ($w). If the type of the expression is -double, then the wrapper type is java.lang.Double. - -

If the type of the expression following ($w) is not -a primitive type, then ($w) does nothing. - -

$_

- -

insertAfter() in CtMethod and -CtConstructor inserts the -compiled code at the end of the method. In the statement given to -insertAfter(), not only the variables shown above such as -$0, $1, ... but also $_ is -available. - -

The variable $_ represents the resulting value of the -method. The type of that variable is the type of the result type (the -return type) of the method. If the result type is void, -then the type of $_ is Object and the value -of $_ is null. - -

Although the compiled code inserted by insertAfter() -is executed just before the control normally returns from the method, -it can be also executed when an exception is thrown from the method. -To execute it when an exception is thrown, the second parameter -asFinally to insertAfter() must be -true. - -

If an exception is thrown, the compiled code inserted by -insertAfter() is executed as a finally -clause. The value of $_ is 0 or -null in the compiled code. After the execution of the -compiled code terminates, the exception originally thrown is re-thrown -to the caller. Note that the value of $_ is never thrown -to the caller; it is rather discarded. - -

$sig

- -

The value of $sig is an array of -java.lang.Class objects that represent the formal -parameter types in declaration order. - -

$type

- -

The value of $type is an java.lang.Class -object representing the formal type of the result value. This -variable is available only in insertAfter() in -CtMethod and CtConstructor. - -

$class

- -

The value of $class is an java.lang.Class -object representing the class in which the edited method is declared. -This represents the type of $0. - -

addCatch()

- -

addCatch() inserts a code fragment into a method body -so that the code fragment is executed when the method body throws -an exception and the control returns to the caller. In the source -text representing the inserted code fragment, the exception value -is referred to with the special variable $e. - -

For example, this program: - -

- -

translates the method body represented by m into -something like this: - -

- -

Note that the inserted code fragment must end with a -throw or return statement. - -


- -
-

4.2 Altering a method body

- -

CtMethod and CtConstructor provide -setBody() for substituting a whole -method body. They compile the given source text into Java bytecode -and substitutes it for the original method body. If the given source -text is null, the substituted body includes only a -return statement, which returns zero or null unless the -result type is void. - -

In the source text given to setBody(), the identifiers -starting with $ have special meaning - -

- -Note that $_ is not available. - -

Substituting source text for an existing expression

- -

Javassist allows modifying only an expression included in a method body. -javassist.expr.ExprEditor is a class -for replacing an expression in a method body. -The users can define a subclass of ExprEditor -to specify how an expression is modified. - -

To run an ExprEditor object, the users must -call instrument() in CtMethod or -CtClass. - -For example, - -

- -

searches the method body represented by cm and -replaces all calls to move() in class Point -with a block: - -

- -

so that the first parameter to move() is always 0. -Note that the substituted code is not an expression but -a statement or a block. - -

The method instrument() searches a method body. -If it finds an expression such as a method call, field access, and object -creation, then it calls edit() on the given -ExprEditor object. The parameter to edit() -is an object representing the found expression. The edit() -method can inspect and replace the expression through that object. - -

Calling replace() on the parameter to edit() -substitutes the given statement or block for the expression. If the given -block is an empty block, that is, if replace("{}") -is executed, then the expression is removed from the method body. - -If you want to insert a statement (or a block) before/after the -expression, a block like the following should be passed to -replace(): - -

- -

whichever the expression is either a method call, field access, -object creation, or others. The second statement could be: - -

- -

if the expression is read access, or - -

- -

if the expression is write access. - -

Local variables available in the target expression is -also available in the source text passed to replace() -if the method searched by instrument() was compiled -with the -g option (the class file includes a local variable -attribute). - -

javassist.expr.MethodCall

- -

A MethodCall object represents a method call. -The method replace() in -MethodCall substitutes a statement or -a block for the method call. -It receives source text representing the substitued statement or -block, in which the identifiers starting with $ -have special meaning as in the source text passed to -insertBefore(). - -

- -

Here the method call means the one represented by the -MethodCall object. - -

The other identifiers such as $w, -$args and $$ -are also available. - -

Unless the result type of the method call is void, -a value must be assigned to -$_ in the source text and the type of $_ -is the result type. -If the result type is void, the type of $_ -is Object and the value assigned to $_ -is ignored. - -

$proceed is not a String value but special -syntax. It must be followed by an argument list surrounded by parentheses -( ). - -

javassist.expr.FieldAccess

- -

A FieldAccess object represents field access. -The method edit() in ExprEditor -receives this object if field access is found. -The method replace() in -FieldAccess receives -source text representing the substitued statement or -block for the field access. - -

-In the source text, the identifiers starting with $ -have special meaning: - -

- -

The other identifiers such as $w, -$args and $$ -are also available. - -

If the expression is read access, a value must be assigned to -$_ in the source text. The type of $_ -is the type of the field. - -

javassist.expr.NewExpr

- -

A NewExpr object represents object creation -with the new operator (not including array creation). -The method edit() in ExprEditor -receives this object if object creation is found. -The method replace() in -NewExpr receives -source text representing the substitued statement or -block for the object creation. - -

-In the source text, the identifiers starting with $ -have special meaning: - -

- -

The other identifiers such as $w, -$args and $$ -are also available. - -

javassist.expr.NewArray

- -

A NewArray object represents array creation -with the new operator. -The method edit() in ExprEditor -receives this object if array creation is found. -The method replace() in -NewArray receives -source text representing the substitued statement or -block for the array creation. - -

-In the source text, the identifiers starting with $ -have special meaning: - -

- -

The other identifiers such as $w, -$args and $$ -are also available. - -

For example, if the array creation is the following expression, - -

- -then the value of $1 and $2 are 3 and 4, respectively. $3 is not available. - -

If the array creation is the following expression, - -

- -then the value of $1 is 3 but $2 is not available. - -

javassist.expr.Instanceof

- -

A Instanceof object represents an instanceof -expression. -The method edit() in ExprEditor -receives this object if an instanceof expression is found. -The method replace() in -Instanceof receives -source text representing the substitued statement or -block for the expression. - -

-In the source text, the identifiers starting with $ -have special meaning: - -

- -

The other identifiers such as $w, -$args and $$ -are also available. - -

javassist.expr.Cast

- -

A Cast object represents an expression for -explicit type casting. -The method edit() in ExprEditor -receives this object if explicit type casting is found. -The method replace() in -Cast receives -source text representing the substitued statement or -block for the expression. - -

-In the source text, the identifiers starting with $ -have special meaning: - -

- -

The other identifiers such as $w, -$args and $$ -are also available. - -

javassist.expr.Handler

- -

A Handler object represents a catch -clause of try-catch statement. -The method edit() in ExprEditor -receives this object if a catch is found. -The method insertBefore() in -Handler compiles the received -source text and inserts it at the beginning of the catch clause. - -

-In the source text, the identifiers starting with $ -have meaning: - -

- -

If a new exception object is assigned to $1, -it is passed to the original catch clause as the caught -exception. - -


- -
-

4.3 Adding a new method or field

- -

Adding a method

- -

Javassist allows the users to create a new method and constructor -from scratch. CtNewMethod -and CtNewConstructor provide several factory methods, -which are static methods for creating CtMethod or -CtConstructor objects. -Especially, make() creates -a CtMethod or CtConstructor object -from the given source text. - -

For example, this program: - -

- -

adds a public method xmove() to class Point. -In this example, x is a int field in -the class Point. - -

The source text passed to make() can include the -identifiers starting with $ except $_ -as in setBody(). -It can also include -$proceed if the target object and the target method name -are also given to make(). For example, - -

- -

this program creates a method ymove() defined below: - -

- -

Note that $proceed has been replaced with -this.move. - -

Javassist provides another way to add a new method. -You can first create an abstract method and later give it a method body: - -

- -

Since Javassist makes a class abstract if an abstract method is -added to the class, you have to explicitly change the class back to a -non-abstract one after calling setBody(). - - -

Mutual recursive methods

- -

Javassist cannot compile a method if it calls another method that -has not been added to a class. (Javassist can compile a method that -calls itself recursively.) To add mutual recursive methods to a class, -you need a trick shown below. Suppose that you want to add methods -m() and n() to a class represented -by cc: - -

- -

You must first make two abstract methods and add them to the class. -Then you can give the method bodies to these methods even if the method -bodies include method calls to each other. Finally you must change the -class to a not-abstract class since addMethod() automatically -changes a class into an abstract one if an abstract method is added. - -

Adding a field

- -

Javassist also allows the users to create a new field. - -

- -

This program adds a field named z to class -Point. - -

If the initial value of the added field must be specified, -the program shown above must be modified into: - -

- -

Now, the method addField() receives the second parameter, -which is the source text representing an expression computing the initial -value. This source text can be any Java expression if the result type -of the expression matches the type of the field. Note that an expression -does not end with a semi colon (;). - -

Furthermore, the above code can be rewritten into the following -simple code: - -

- - -


- -
-

4.4 Runtime support classes

- -

In most cases, a class modified by Javassist does not require -Javassist to run. However, some kinds of bytecode generated by the -Javassist compiler need runtime support classes, which are in the -javassist.runtime package (for details, please read -the API reference of that package). Note that the -javassist.runtime package is the only package that -classes modified by Javassist may need for running. The other -Javassist classes are never used at runtime of the modified classes. - -


- -
-

4.5 Limitations

- -

In the current implementation, the Java compiler included in Javassist -has several limitations with respect to the language that the compiler can -accept. Those limitations are: - -

  • All the class names must be fully qualified (they must include -package names). This is because the compiler does not support -import -declarations. However, the java.lang package is an -exception; for example, the compiler accepts Object as -well as java.lang.Object. - -

  • Array initializers, a comma-separated list of expressions -enclosed by braces { and }, are not -supported. - -

  • Inner classes or anonymous classes are not supported. - -

  • Labeled continue and break statements -are not supported. - -

  • The finally clause following -try and catch clauses is not supported. - -

  • The compiler does not correctly implement the Java method dispatch -algorithm. The compiler may confuse if methods defined in a class -have the same name but take different parameter lists. - -

  • The users are recommended to use # as the separator -between a class name and a static method or field name. -For example, in regular Java, - -
      javassist.CtClass.intType.getName()
    - -

    calls a method getName() on -the object indicated by the static field intType -in javassist.CtClass. In Javassist, the users can -write the expression shown above but they are recommended to -write: - -

      javassist.CtClass#intType.getName()
    - -

    so that the compiler can quickly parse the expression. - - -


    - -
    Previous page -   Next page - -


    -Java(TM) is a trademark of Sun Microsystems, Inc.
    -Copyright (C) 2000-2004 by Shigeru Chiba, All rights reserved. - - + + + + Javassist Tutorial + + + + + +
    Getting Started with Javassist
    + +
    Previous page
    +
    Next page
    + +

    +4. Introspection and customization +

    + +


    + + +

    4. Introspection and customization

    + +

    CtClass provides methods for introspection. The +introspective ability of Javassist is compatible with that of +the Java reflection API. CtClass provides +getName(), getSuperclass(), +getMethods(), and so on. +CtClass also provides methods for modifying a class +definition. It allows to add a new field, constructor, and method. +Instrumenting a method body is also possible. + +

    +Methods are represented by CtMethod objects. +CtMethod provides several methods for modifying +the definition of the method. Note that if a method is inherited +from a super class, then +the same CtMethod object +that represents the inherited method represents the method declared +in that super class. +A CtMethod object corresponds to every method declaration. + +

    +For example, if class Point declares method move() +and a subclass ColorPoint of Point does +not override move(), the two move() methods +declared in Point and inherited in ColorPoint +are represented by the identical CtMethod object. +If the method definition represented by this +CtMethod object is modified, the modification is +reflected on both the methods. +If you want to modify only the move() method in +ColorPoint, you first have to add to ColorPoint +a copy of the CtMethod object representing move() +in Point. A copy of the the CtMethod object +can be obtained by CtNewMethod.copy(). + + +


    + +
      +Javassist does not allow to remove a method or field, but it allows +to change the name. So if a method is not necessary any more, it should be +renamed and changed to be a private method by calling +setName() +and setModifiers() declared in CtMethod. + +

      Javassist does not allow to add an extra parameter to an existing +method, either. Instead of doing that, a new method receiving the +extra parameter as well as the other parameters should be added to the +same class. For example, if you want to add an extra int +parameter newZ to a method: + +

        void move(int newX, int newY) { x = newX; y = newY; }
      + +

      in a Point class, then you should add the following +method to the Point class: + +

        void move(int newX, int newY, int newZ) {
        +    // do what you want with newZ.
        +    move(newX, newY);
        +}
      + +
    + +


    + +

    Javassist also provides low-level API for directly editing a raw +class file. For example, getClassFile() in +CtClass returns a ClassFile object +representing a raw class file. getMethodInfo() in +CtMethod returns a MethodInfo object +representing a method_info structure included in a class +file. The low-level API uses the vocabulary from the Java Virtual +machine specification. The users must have the knowledge about class +files and bytecode. For more details, the users should see the +javassist.bytecode package. + +

    The class files modified by Javassist requires the +javassist.runtime package for runtime support +only if some special identifiers starting with $ +are used. Those special identifiers are described below. +The class files modified without those special identifiers +do not need the javassist.runtime package or any +other Javassist packages at runtime. +For more details, see the API documentation +of the javassist.runtime package. + +


    + +
    +

    4.1 Inserting source text at the beginning/end of a method body

    + +

    CtMethod and CtConstructor provide +methods insertBefore(), insertAfter(), and +addCatch(). They are used for inserting a code fragment +into the body of an existing method. The users can specify those code +fragments with source text written in Java. +Javassist includes a simple Java compiler for processing source +text. It receives source text +written in Java and compiles it into Java bytecode, which will be +inlined into a method body. + +

    +Inserting a code fragment at the position specified by a line number +is also possible +(if the line number table is contained in the class file). +insertAt() in CtMethod and +CtConstructor takes source text and a line number in the source +file of the original class definition. +It compiles the source text and inserts the compiled code at the line number. + +

    The methods insertBefore(), insertAfter(), +addCatch(), and insertAt() +receive a String object representing +a statement or a block. A statement is a single control structure like +if and while or an expression ending with +a semi colon (;). A block is a set of +statements surrounded with braces {}. +Hence each of the following lines is an example of valid statement or block: + +

      System.out.println("Hello");
      +{ System.out.println("Hello"); }
      +if (i < 0) { i = -i; }
      +
    + +

    The statement and the block can refer to fields and methods. +They can also refer to the parameters +to the method that they are inserted into +if that method was compiled with the -g option +(to include a local variable attribute in the class file). +Otherwise, they must access the method parameters through the special +variables $0, $1, $2, ... described +below. +Accessing local variables declared in the method is not allowed +although declaring a new local variable in the block is allowed. +However, insertAt() allows the statement and the block +to access local variables +if these variables are available at the specified line number +and the target method was compiled with the -g option. + + + + +

    The String object passed to the methods +insertBefore(), insertAfter(), +addCatch(), and insertAt() are compiled by +the compiler included in Javassist. +Since the compiler supports language extensions, +several identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0, $1, $2, ...    Actual parameters
      $argsAn array of parameters. +The type of $args is Object[]. +
      $$All actual parameters.
      +For example, m($$) is equivalent to +m($1,$2,...)
       
      $cflow(...)cflow variable
      $rThe result type. It is used in a cast expression.
      $wThe wrapper type. It is used in a cast expression.
      $_The resulting value
      $sigAn array of java.lang.Class objects representing +the formal parameter types. +
      $typeA java.lang.Class object representing +the formal result type.
      $classA java.lang.Class object representing +the class currently edited.
      +
    + +

    $0, $1, $2, ...

    + +

    The parameters passed to the target method +are accessible with +$0, $1, $2, ... instead of +the original parameter names. +$1 represents the +first parameter, $2 represents the second parameter, and +so on. The types of those variables are identical to the parameter +types. +$0 is +equivalent to this. If the method is static, +$0 is not available. + +

    These variables are used as following. Suppose that a class +Point: + +

      class Point { + int x, y; + void move(int dx, int dy) { x += dx; y += dy; } +} +
    + +

    To print the values of dx and dy +whenever the method move() is called, execute this +program: + +

      ClassPool pool = ClassPool.getDefault();
      +CtClass cc = pool.get("Point");
      +CtMethod m = cc.getDeclaredMethod("move");
      +m.insertBefore("{ System.out.println($1); System.out.println($2); }");
      +cc.writeFile();
      +
    + +

    Note that the source text passed to insertBefore() is +surrounded with braces {}. +insertBefore() accepts only a single statement or a block +surrounded with braces. + +

    The definition of the class Point after the +modification is like this: + +

      class Point { + int x, y; + void move(int dx, int dy) { + { System.out.println(dx); System.out.println(dy); } + x += dx; y += dy; + } +} +
    + +

    $1 and $2 are replaced with +dx and dy, respectively. + +

    $1, $2, $3 ... are +updatable. If a new value is assigend to one of those variables, +then the value of the parameter represented by that variable is +also updated. + + +

    $args

    + +

    The variable $args represents an array of all the +parameters. The type of that variable is an array of class +Object. If a parameter type is a primitive type such as +int, then the parameter value is converted into a wrapper +object such as java.lang.Integer to store in +$args. Thus, $args[0] is equivalent to +$1 unless the type of the first parameter is a primitive +type. Note that $args[0] is not equivalent to +$0; $0 represents this. + +

    If an array of Object is assigned to +$args, then each element of that array is +assigned to each parameter. If a parameter type is a primitive +type, the type of the corresponding element must be a wrapper type. +The value is converted from the wrapper type to the primitive type +before it is assigned to the parameter. + +

    $$

    + +

    The variable $$ is abbreviation of a list of +all the parameters separated by commas. +For example, if the number of the parameters +to method move() is three, then + +

      move($$)
    + +

    is equivalent to this: + +

      move($1, $2, $3)
    + +

    If move() does not take any parameters, +then move($$) is +equivalent to move(). + +

    $$ can be used with another method. +If you write an expression: + +

      exMove($$, context)
    + +

    then this expression is equivalent to: + +

      exMove($1, $2, $3, context)
    + +

    Note that $$ enables generic notation of method call +with respect to the number of parameters. +It is typically used with $proceed shown later. + +

    $cflow

    + +

    $cflow means "control flow". +This read-only variable returns the depth of the recursive calls +to a specific method. + +

    Suppose that the method shown below is represented by a +CtMethod object cm: + +

      int fact(int n) {
      +    if (n <= 1)
      +        return n;
      +    else
      +        return n * fact(n - 1);
      +}
    + +

    To use $cflow, first declare that $cflow +is used for monitoring calls to the method fact(): + +

      CtMethod cm = ...;
      +cm.useCflow("fact");
    + +

    The parameter to useCflow() is the identifier of the +declared $cflow variable. Any valid Java name can be +used as the identifier. Since the identifier can also include +. (dot), for example, "my.Test.fact" +is a valid identifier. + +

    Then, $cflow(fact) represents the depth of the +recursive calls to the method specified by cm. The value +of $cflow(fact) is 0 (zero) when the method is +first called whereas it is 1 when the method is recursively called +within the method. For example, + +

      +cm.insertBefore("if ($cflow(fact) == 0)"
      +              + "    System.out.println(\"fact \" + $1);");
      +
    + +

    translates the method fact() so that it shows the +parameter. Since the value of $cflow(fact) is checked, +the method fact() does not show the parameter if it is +recursively called within fact(). + +

    The value of $cflow is the number of stack frames +associated with the specified method cm +under the current topmost +stack frame for the current thread. $cflow is also +accessible within a method different from the specified method +cm. + +

    $r

    + +

    $r represents the result type (return type) of the method. +It must be used as the cast type in a cast expression. +For example, this is a typical use: + +

      Object result = ... ;
      +$_ = ($r)result;
    + +

    If the result type is a primitive type, then ($r) +follows special semantics. First, if the operand type of the cast +expression is a primitive type, ($r) works as a normal +cast operator to the result type. +On the other hand, if the operand type is a wrapper type, +($r) converts from the wrapper type to the result type. +For example, if the result type is int, then +($r) converts from java.lang.Integer to +int. + +

    If the result type is void, then +($r) does not convert a type; it does nothing. +However, if the operand is a call to a void method, +then ($r) results in null. For example, +if the result type is void and +foo() is a void method, then + +

      $_ = ($r)foo();
    + +

    is a valid statement. + +

    The cast operator ($r) is also useful in a +return statement. Even if the result type is +void, the following return statement is valid: + +

      return ($r)result;
    + +

    Here, result is some local variable. +Since ($r) is specified, the resulting value is +discarded. +This return statement is regarded as the equivalent +of the return statement without a resulting value: + +

      return;
    + +

    $w

    + +

    $w represents a wrapper type. +It must be used as the cast type in a cast expression. +($w) converts from a primitive type to the corresponding +wrapper type. + +The following code is an example: + +

      Integer i = ($w)5;
    + +

    The selected wrapper type depends on the type of the expression +following ($w). If the type of the expression is +double, then the wrapper type is java.lang.Double. + +

    If the type of the expression following ($w) is not +a primitive type, then ($w) does nothing. + +

    $_

    + +

    insertAfter() in CtMethod and +CtConstructor inserts the +compiled code at the end of the method. In the statement given to +insertAfter(), not only the variables shown above such as +$0, $1, ... but also $_ is +available. + +

    The variable $_ represents the resulting value of the +method. The type of that variable is the type of the result type (the +return type) of the method. If the result type is void, +then the type of $_ is Object and the value +of $_ is null. + +

    Although the compiled code inserted by insertAfter() +is executed just before the control normally returns from the method, +it can be also executed when an exception is thrown from the method. +To execute it when an exception is thrown, the second parameter +asFinally to insertAfter() must be +true. + +

    If an exception is thrown, the compiled code inserted by +insertAfter() is executed as a finally +clause. The value of $_ is 0 or +null in the compiled code. After the execution of the +compiled code terminates, the exception originally thrown is re-thrown +to the caller. Note that the value of $_ is never thrown +to the caller; it is rather discarded. + +

    $sig

    + +

    The value of $sig is an array of +java.lang.Class objects that represent the formal +parameter types in declaration order. + +

    $type

    + +

    The value of $type is an java.lang.Class +object representing the formal type of the result value. This +variable is available only in insertAfter() in +CtMethod and CtConstructor. + +

    $class

    + +

    The value of $class is an java.lang.Class +object representing the class in which the edited method is declared. +This represents the type of $0. + +

    addCatch()

    + +

    addCatch() inserts a code fragment into a method body +so that the code fragment is executed when the method body throws +an exception and the control returns to the caller. In the source +text representing the inserted code fragment, the exception value +is referred to with the special variable $e. + +

    For example, this program: + +

      +CtMethod m = ...;
      +CtClass etype = ClassPool.getDefault().get("java.io.IOException");
      +m.addCatch("{ System.out.println($e); throw $e; }", etype);
      +
    + +

    translates the method body represented by m into +something like this: + +

      +try {
      +    the original method body
      +}
      +catch (java.io.IOException e) {
      +    System.out.println(e);
      +    throw e;
      +}
      +
    + +

    Note that the inserted code fragment must end with a +throw or return statement. + +


    + +
    +

    4.2 Altering a method body

    + +

    CtMethod and CtConstructor provide +setBody() for substituting a whole +method body. They compile the given source text into Java bytecode +and substitutes it for the original method body. If the given source +text is null, the substituted body includes only a +return statement, which returns zero or null unless the +result type is void. + +

    In the source text given to setBody(), the identifiers +starting with $ have special meaning + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0, $1, $2, ...    Actual parameters
      $argsAn array of parameters. +The type of $args is Object[]. +
      $$All actual parameters.
      +
      $cflow(...)cflow variable
      $rThe result type. It is used in a cast expression.
      $wThe wrapper type. It is used in a cast expression.
      $sigAn array of java.lang.Class objects representing +the formal parameter types. +
      $typeA java.lang.Class object representing +the formal result type.
      $classA java.lang.Class object representing +the class that declares the method
      +currently edited (the type of $0).
       
      +
    + +Note that $_ is not available. + +

    Substituting source text for an existing expression

    + +

    Javassist allows modifying only an expression included in a method body. +javassist.expr.ExprEditor is a class +for replacing an expression in a method body. +The users can define a subclass of ExprEditor +to specify how an expression is modified. + +

    To run an ExprEditor object, the users must +call instrument() in CtMethod or +CtClass. + +For example, + +

      +CtMethod cm = ... ;
      +cm.instrument(
      +    new ExprEditor() {
      +        public void edit(MethodCall m)
      +                      throws CannotCompileException
      +        {
      +            if (m.getClassName().equals("Point")
      +                          && m.getMethodName().equals("move"))
      +                m.replace("{ $1 = 0; $_ = $proceed($$); }");
      +        }
      +    });
      +
    + +

    searches the method body represented by cm and +replaces all calls to move() in class Point +with a block: + +

      { $1 = 0; $_ = $proceed($$); }
      +
    + +

    so that the first parameter to move() is always 0. +Note that the substituted code is not an expression but +a statement or a block. + +

    The method instrument() searches a method body. +If it finds an expression such as a method call, field access, and object +creation, then it calls edit() on the given +ExprEditor object. The parameter to edit() +is an object representing the found expression. The edit() +method can inspect and replace the expression through that object. + +

    Calling replace() on the parameter to edit() +substitutes the given statement or block for the expression. If the given +block is an empty block, that is, if replace("{}") +is executed, then the expression is removed from the method body. + +If you want to insert a statement (or a block) before/after the +expression, a block like the following should be passed to +replace(): + +

      +{ before-statements;
      +  $_ = $proceed($$);
      +  after-statements; }
      +
    + +

    whichever the expression is either a method call, field access, +object creation, or others. The second statement could be: + +

      $_ = $proceed();
    + +

    if the expression is read access, or + +

      $proceed($$);
    + +

    if the expression is write access. + +

    Local variables available in the target expression is +also available in the source text passed to replace() +if the method searched by instrument() was compiled +with the -g option (the class file includes a local variable +attribute). + +

    javassist.expr.MethodCall

    + +

    A MethodCall object represents a method call. +The method replace() in +MethodCall substitutes a statement or +a block for the method call. +It receives source text representing the substitued statement or +block, in which the identifiers starting with $ +have special meaning as in the source text passed to +insertBefore(). + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +The target object of the method call.
      +This is not equivalent to this, which represents +the caller-side this object.
      +$0 is null if the method is static. +
       
       
      $1, $2, ...     +The parameters of the method call. +
      +$_The resulting value of the method call.
      $rThe result type of the method call.
      $class    A java.lang.Class object representing +the class declaring the method. +
      $sig    An array of java.lang.Class objects representing +the formal parameter types.
      $type    A java.lang.Class object representing +the formal result type.
      $proceed    The name of the method originally called +in the expression.
      +
    + +

    Here the method call means the one represented by the +MethodCall object. + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    Unless the result type of the method call is void, +a value must be assigned to +$_ in the source text and the type of $_ +is the result type. +If the result type is void, the type of $_ +is Object and the value assigned to $_ +is ignored. + +

    $proceed is not a String value but special +syntax. It must be followed by an argument list surrounded by parentheses +( ). + +

    javassist.expr.FieldAccess

    + +

    A FieldAccess object represents field access. +The method edit() in ExprEditor +receives this object if field access is found. +The method replace() in +FieldAccess receives +source text representing the substitued statement or +block for the field access. + +

    +In the source text, the identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +The object containing the field accessed by the expression. +This is not equivalent to this.
      +this represents the object that the method including the +expression is invoked on.
      +$0 is null if the field is static. +
       
       
      $1 +The value that would be stored in the field +if the expression is write access. +
      Otherwise, $1 is not available. +
       
      $_ +The resulting value of the field access +if the expression is read access. +
      Otherwise, the value stored in $_ is discarded. +
       
      $r +The type of the field if the expression is read access. +
      Otherwise, $r is void. +
       
      $class    A java.lang.Class object representing +the class declaring the field. +
      $typeA java.lang.Class object representing +the field type.
      $proceed    The name of a virtual method executing the original +field access. +.
      +
    + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    If the expression is read access, a value must be assigned to +$_ in the source text. The type of $_ +is the type of the field. + +

    javassist.expr.NewExpr

    + +

    A NewExpr object represents object creation +with the new operator (not including array creation). +The method edit() in ExprEditor +receives this object if object creation is found. +The method replace() in +NewExpr receives +source text representing the substitued statement or +block for the object creation. + +

    +In the source text, the identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +null. +
      $1, $2, ...     +The parameters to the constructor. +
      $_ +The resulting value of the object creation. +
      A newly created object must be stored in this variable. +
       
      $r +The type of the created object. +
      $sig    An array of java.lang.Class objects representing +the formal parameter types.
      $type    A java.lang.Class object representing +the class of the created object. +
      $proceed    The name of a virtual method executing the original +object creation. +.
      +
    + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    javassist.expr.NewArray

    + +

    A NewArray object represents array creation +with the new operator. +The method edit() in ExprEditor +receives this object if array creation is found. +The method replace() in +NewArray receives +source text representing the substitued statement or +block for the array creation. + +

    +In the source text, the identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +null. +
      $1, $2, ...     +The size of each dimension. +
      $_ +The resulting value of the array creation. +
      A newly created array must be stored in this variable. +
       
      $r +The type of the created array. +
      $type    A java.lang.Class object representing +the class of the created array. +
      $proceed    The name of a virtual method executing the original +array creation. +.
      +
    + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    For example, if the array creation is the following expression, + +

      +String[][] s = new String[3][4];
      +
    + +then the value of $1 and $2 are 3 and 4, respectively. $3 is not available. + +

    If the array creation is the following expression, + +

      +String[][] s = new String[3][];
      +
    + +then the value of $1 is 3 but $2 is not available. + +

    javassist.expr.Instanceof

    + +

    A Instanceof object represents an instanceof +expression. +The method edit() in ExprEditor +receives this object if an instanceof expression is found. +The method replace() in +Instanceof receives +source text representing the substitued statement or +block for the expression. + +

    +In the source text, the identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +null. +
      $1 +The value on the left hand side of the original +instanceof operator. +
      $_ +The resulting value of the expression. +The type of $_ is boolean. +
      $r +The type on the right hand side of the instanceof operator. +
      $typeA java.lang.Class object representing +the type on the right hand side of the instanceof operator. +
      $proceed    The name of a virtual method executing the original +instanceof expression. +
      It takes one parameter (the type is java.lang.Object) +and returns true +
      if the parameter value is an instance of the type on the right +hand side of +
      the original instanceof operator. +Otherwise, it returns false. +
       
       
       
      +
    + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    javassist.expr.Cast

    + +

    A Cast object represents an expression for +explicit type casting. +The method edit() in ExprEditor +receives this object if explicit type casting is found. +The method replace() in +Cast receives +source text representing the substitued statement or +block for the expression. + +

    +In the source text, the identifiers starting with $ +have special meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      $0 +null. +
      $1 +The value the type of which is explicitly cast. +
      $_ +The resulting value of the expression. +The type of $_ is the same as the type +
      after the explicit casting, that is, the type surrounded +by ( ). +
       
      $rthe type after the explicit casting, or the type surrounded +by ( ). +
      $typeA java.lang.Class object representing +the same type as $r. +
      $proceed    The name of a virtual method executing the original +type casting. +
      It takes one parameter of the type java.lang.Object +and returns it after +
      the explicit type casting specified by the original expression. + +
       
       
      +
    + +

    The other identifiers such as $w, +$args and $$ +are also available. + +

    javassist.expr.Handler

    + +

    A Handler object represents a catch +clause of try-catch statement. +The method edit() in ExprEditor +receives this object if a catch is found. +The method insertBefore() in +Handler compiles the received +source text and inserts it at the beginning of the catch clause. + +

    +In the source text, the identifiers starting with $ +have meaning: + +

      + + + + + + + + + + + + + + + + + + + + + + +
      $1 +The exception object caught by the catch clause. +
      $rthe type of the exception caught by the catch clause. +It is used in a cast expression. +
      $wThe wrapper type. It is used in a cast expression. +
      $type     +A java.lang.Class object representing +
      the type of the exception caught by the catch clause. +
       
      +
    + +

    If a new exception object is assigned to $1, +it is passed to the original catch clause as the caught +exception. + +


    + +
    +

    4.3 Adding a new method or field

    + +

    Adding a method

    + +

    Javassist allows the users to create a new method and constructor +from scratch. CtNewMethod +and CtNewConstructor provide several factory methods, +which are static methods for creating CtMethod or +CtConstructor objects. +Especially, make() creates +a CtMethod or CtConstructor object +from the given source text. + +

    For example, this program: + +

      +CtClass point = ClassPool.getDefault().get("Point");
      +CtMethod m = CtNewMethod.make(
      +                 "public int xmove(int dx) { x += dx; }",
      +                 point);
      +point.addMethod(m);
      +
    + +

    adds a public method xmove() to class Point. +In this example, x is a int field in +the class Point. + +

    The source text passed to make() can include the +identifiers starting with $ except $_ +as in setBody(). +It can also include +$proceed if the target object and the target method name +are also given to make(). For example, + +

      +CtClass point = ClassPool.getDefault().get("Point");
      +CtMethod m = CtNewMethod.make(
      +                 "public int ymove(int dy) { $proceed(0, dy); }",
      +                 point, "this", "move");
      +
    + +

    this program creates a method ymove() defined below: + +

      +public int ymove(int dy) { this.move(0, dy); }
      +
    + +

    Note that $proceed has been replaced with +this.move. + +

    Javassist provides another way to add a new method. +You can first create an abstract method and later give it a method body: + +

      +CtClass cc = ... ;
      +CtMethod m = new CtMethod(CtClass.intType, "move",
      +                          new CtClass[] { CtClass.intType }, cc);
      +cc.addMethod(m);
      +m.setBody("{ x += $1; }");
      +cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
      +
    + +

    Since Javassist makes a class abstract if an abstract method is +added to the class, you have to explicitly change the class back to a +non-abstract one after calling setBody(). + + +

    Mutual recursive methods

    + +

    Javassist cannot compile a method if it calls another method that +has not been added to a class. (Javassist can compile a method that +calls itself recursively.) To add mutual recursive methods to a class, +you need a trick shown below. Suppose that you want to add methods +m() and n() to a class represented +by cc: + +

      +CtClass cc = ... ;
      +CtMethod m = CtNewMethod.make("public abstract int m(int i);", cc);
      +CtMethod n = CtNewMethod.make("public abstract int n(int i);", cc);
      +cc.addMethod(m);
      +cc.addMethod(n);
      +m.setBody("{ return ($1 <= 0) ? 1 : (n($1 - 1) * $1); }");
      +n.setBody("{ return m($1); }");
      +cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
      +
    + +

    You must first make two abstract methods and add them to the class. +Then you can give the method bodies to these methods even if the method +bodies include method calls to each other. Finally you must change the +class to a not-abstract class since addMethod() automatically +changes a class into an abstract one if an abstract method is added. + +

    Adding a field

    + +

    Javassist also allows the users to create a new field. + +

      +CtClass point = ClassPool.getDefault().get("Point");
      +CtField f = new CtField(CtClass.intType, "z", point);
      +point.addField(f);
      +
    + +

    This program adds a field named z to class +Point. + +

    If the initial value of the added field must be specified, +the program shown above must be modified into: + +

      +CtClass point = ClassPool.getDefault().get("Point");
      +CtField f = new CtField(CtClass.intType, "z", point);
      +point.addField(f, "0");    // initial value is 0.
      +
    + +

    Now, the method addField() receives the second parameter, +which is the source text representing an expression computing the initial +value. This source text can be any Java expression if the result type +of the expression matches the type of the field. Note that an expression +does not end with a semi colon (;). + +

    Furthermore, the above code can be rewritten into the following +simple code: + +

      +CtClass point = ClassPool.getDefault().get("Point");
      +CtField f = CtField.make("public int z = 0;", point);
      +point.addField(f);
      +
    + + +


    + +
    +

    4.4 Runtime support classes

    + +

    In most cases, a class modified by Javassist does not require +Javassist to run. However, some kinds of bytecode generated by the +Javassist compiler need runtime support classes, which are in the +javassist.runtime package (for details, please read +the API reference of that package). Note that the +javassist.runtime package is the only package that +classes modified by Javassist may need for running. The other +Javassist classes are never used at runtime of the modified classes. + +


    + +
    +

    4.5 Limitations

    + +

    In the current implementation, the Java compiler included in Javassist +has several limitations with respect to the language that the compiler can +accept. Those limitations are: + +

  • The new syntax introduced by J2SE 5.0 (including enums and generics) +has not been supported. Annotations are supported only by the low level +API of Javassist. +See the javassist.bytecode.annotation package. + +

  • All the class names must be fully qualified (they must include +package names). This is because the compiler does not support +import +declarations. However, the java.lang package is an +exception; for example, the compiler accepts Object as +well as java.lang.Object. + +

  • Array initializers, a comma-separated list of expressions +enclosed by braces { and }, are not +supported. + +

  • Inner classes or anonymous classes are not supported. + +

  • Labeled continue and break statements +are not supported. + +

  • The compiler does not correctly implement the Java method dispatch +algorithm. The compiler may confuse if methods defined in a class +have the same name but take different parameter lists. + +

    For example, + +

      +class A {} 
      +class B extends A {} 
      +class C extends C {} 
      +
      +class X { 
      +    void foo(A a) { .. } 
      +    void foo(B b) { .. } 
      +}
      +
    + +

    If the compiled expression is x.foo(new C()), where +x is an instance of X, the compiler may produce a call +to foo(A) although the compiler can correctly compile +foo((B)new C()). + +

  • The users are recommended to use # as the separator +between a class name and a static method or field name. +For example, in regular Java, + +
      javassist.CtClass.intType.getName()
    + +

    calls a method getName() on +the object indicated by the static field intType +in javassist.CtClass. In Javassist, the users can +write the expression shown above but they are recommended to +write: + +

      javassist.CtClass#intType.getName()
    + +

    so that the compiler can quickly parse the expression. + + +


    + +
    Previous page +   Next page + +


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