changed.
<li>javassist.expr.NewArray has been implemented. It enables modifying
an expression for array creation.
+ <li><code>.class</code> notation has been supported. The modified class
+ file needs javassist.runtime.DotClass at runtime.
+ <li>a bug in <code>CtClass.getMethods()</code> has been fixed.
</ul>
<p>- version 3.0 beta in May 18th, 2004.
Denis Taye, Colin Sampaleanu, Robert Bialek, Asato Shimotaki,
Howard Lewis Ship, Richard Jones, Marjan Sterjev,
Bruce McDonald, Mark Brennan, Vlad Skarzhevskyy,
-Brett Randall, and Tsuyoshi Murakami
+Brett Randall, Tsuyoshi Murakami, and Nathan Meyers
for their contributions.
<p><br>
}
public void compileExpr(ASTree expr) throws CompileError {
- expr = doTypeCheck(expr);
+ doTypeCheck(expr);
expr.accept(this);
}
public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
throws CompileError
{
- expr = doTypeCheck(expr);
+ doTypeCheck(expr);
return booleanExpr(branchIf, expr);
}
- /* This returns a different expression from the given one
- * if the given expression has been modified.
- */
- private ASTree doTypeCheck(ASTree expr) throws CompileError {
- if (typeChecker != null) {
+ public void doTypeCheck(ASTree expr) throws CompileError {
+ if (typeChecker != null)
expr.accept(typeChecker);
- ASTree expr2 = typeChecker.modifiedExpr;
- return expr2 == null ? expr : expr2;
- }
- else
- return expr;
}
public void atASTList(ASTList n) throws CompileError { fatal(); }
int op = st.getOperator();
if (op == EXPR) {
ASTree expr = st.getLeft();
- expr = doTypeCheck(expr);
+ doTypeCheck(expr);
if (expr instanceof AssignExpr)
atAssignExpr((AssignExpr)expr, false);
else if (isPlusPlusExpr(expr)) {
*/
ASTree init = d.getInitializer();
if (init != null) {
- init = doTypeCheck(init);
+ doTypeCheck(init);
atVariableAssign(null, '=', null, d, init, false);
}
}
int k = lookupBinOp(token);
if (k >= 0) {
expr.oprand1().accept(this);
+ ASTree right = expr.oprand2();
+ if (right == null)
+ return; // see TypeChecker.atBinExpr().
+
int type1 = exprType;
int dim1 = arrayDim;
String cname1 = className;
- ASTree right = expr.oprand2();
right.accept(this);
if (dim1 != arrayDim)
throw new CompileError("incompatible array types");
import javassist.CtClass;
import javassist.CtField;
-import javassist.Modifier;
import javassist.ClassPool;
import javassist.NotFoundException;
import javassist.compiler.ast.*;
import javassist.bytecode.*;
-/**
- * This class does type checking and, if needed, transformes the original
- * abstract syntax tree. The resulting tree is available from
- * this.modifiedExpr.
- */
public class TypeChecker extends Visitor implements Opcode, TokenId {
static final String javaLangObject = "java.lang.Object";
static final String jvmJavaLangObject = "java/lang/Object";
protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
protected int arrayDim;
protected String className; // JVM-internal representation
- protected ASTree modifiedExpr; // null if the given expr was not changed
protected MemberResolver resolver;
protected CtClass thisClass;
exprType = CLASS;
arrayDim = 0;
className = MemberResolver.javaToJvmName(cname);
- modifiedExpr = null;
}
}
atMultiNewArray(type, classname, size);
else {
size.head().accept(this);
- if (modifiedExpr != null)
- size.setHead(modifiedExpr);
-
exprType = type;
arrayDim = 1;
- modifiedExpr = null;
if (type == CLASS)
className = resolveClassName(classname);
else
++count;
s.accept(this);
- if (modifiedExpr != null)
- size.setHead(modifiedExpr);
}
- modifiedExpr = null;
exprType = type;
arrayDim = dim;
if (type == CLASS)
int varArray = d.getArrayDim();
String varClass = d.getClassName();
- if (op != '=') {
+ if (op != '=')
atVariable(var);
- if (modifiedExpr != null)
- expr.setOprand1(modifiedExpr);
- }
right.accept(this);
- if (modifiedExpr != null)
- expr.setOprand2(modifiedExpr);
-
exprType = varType;
arrayDim = varArray;
className = varClass;
- modifiedExpr = null;
}
private void atArrayAssign(Expr expr, int op, Expr array,
ASTree right) throws CompileError
{
- atArrayRead(array);
- if (modifiedExpr != null)
- expr.setOprand1(modifiedExpr);
-
+ atArrayRead(array.oprand1(), array.oprand2());
int aType = exprType;
int aDim = arrayDim;
String cname = className;
right.accept(this);
- if (modifiedExpr != null)
- expr.setOprand2(modifiedExpr);
-
exprType = aType;
arrayDim = aDim;
className = cname;
- modifiedExpr = null;
}
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
throws CompileError
{
- CtField f = atFieldRead(left);
+ CtField f = fieldAccess(left);
+ atFieldRead(f);
int fType = exprType;
int fDim = arrayDim;
String cname = className;
- if (modifiedExpr != null)
- expr.setOprand1(modifiedExpr);
-
- if (Modifier.isFinal(f.getModifiers()))
- throw new CompileError("assignment to a final field");
-
right.accept(this);
- if (modifiedExpr != null)
- expr.setOprand2(modifiedExpr);
-
exprType = fType;
arrayDim = fDim;
className = cname;
- modifiedExpr = null;
}
public void atCondExpr(CondExpr expr) throws CompileError {
booleanExpr(expr.condExpr());
- if (modifiedExpr != null)
- expr.setCond(modifiedExpr);
-
expr.thenExpr().accept(this);
- if (modifiedExpr != null)
- expr.setThen(modifiedExpr);
-
int type1 = exprType;
int dim1 = arrayDim;
String cname1 = className;
expr.elseExpr().accept(this);
- if (modifiedExpr != null)
- expr.setElse(modifiedExpr);
if (dim1 == 0 && dim1 == arrayDim)
if (CodeGen.rightIsStrong(type1, exprType))
expr.setElse(new CastExpr(type1, 0, expr.elseExpr()));
exprType = type1;
}
-
- modifiedExpr = null;
}
public void atBinExpr(BinExpr expr) throws CompileError {
int token = expr.getOperator();
int k = CodeGen.lookupBinOp(token);
- if (k < 0) {
- /* equation: &&, ||, ==, !=, <=, >=, <, >
- */
- booleanExpr(expr);
- }
- else {
+ if (k >= 0) {
/* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
*/
- if (token != '+')
- atNonPlusExpr(expr, token);
- else {
+ if (token == '+') {
Expr e = atPlusExpr(expr);
if (e != null) {
/* String concatenation has been translated into
* an expression using StringBuffer.
*/
e = CallExpr.makeCall(Expr.make('.', e,
- new Member("toString")), null);
+ new Member("toString")), null);
+ expr.setLeft(e);
+ expr.setOprand2(null); // <---- look at this!
className = jvmJavaLangString;
- modifiedExpr = e; // expr will be replaced with e.
}
}
+ else {
+ expr.oprand1().accept(this);
+ int type1 = exprType;
+ expr.oprand2().accept(this);
+ computeBinExprType(expr, token, type1);
+ }
}
- }
-
- private void atNonPlusExpr(BinExpr expr, int token) throws CompileError {
- ASTree left = expr.oprand1();
- ASTree right = expr.oprand2();
-
- left.accept(this);
- if (modifiedExpr != null) {
- left = modifiedExpr;
- expr.setOprand1(left);
- }
-
- int type1 = exprType;
- right.accept(this);
- if (modifiedExpr != null) {
- right = modifiedExpr;
- expr.setOprand2(right);
+ else {
+ /* equation: &&, ||, ==, !=, <=, >=, <, >
+ */
+ booleanExpr(expr);
}
-
- modifiedExpr = computeConstExpr(token, left, right);
- computeBinExprType(expr, token, type1);
}
- /* This method deals with string concatenation. It converts a +
- * expression on String such as:
- * "value:" + i + "."
- * into:
- * new StringBuffer().append("value:").append(i).append(".")
- * .toString()
- *
- * This method also inserts a cast operator for the right operand
- * if needed.
- *
- * EXPR must be a + expression.
- *
- * atPlusExpr() returns null if the expression is not a string
- * concatenation.
- */
+ // expr must be a + expression.
private Expr atPlusExpr(BinExpr expr) throws CompileError {
ASTree left = expr.oprand1();
ASTree right = expr.oprand2();
+ if (right == null) {
+ /* this expression has been already type-checked since it is
+ string concatenation.
+ see atBinExpr() above.
+ */
+ exprType = CLASS;
+ arrayDim = 0;
+ className = jvmJavaLangString;
+ return null;
+ }
if (isPlusExpr(left)) {
Expr newExpr = atPlusExpr((BinExpr)left);
if (newExpr != null) {
right.accept(this);
- if (modifiedExpr != null)
- right = modifiedExpr;
-
exprType = CLASS;
arrayDim = 0;
className = "java/lang/StringBuffer";
- modifiedExpr = null;
return makeAppendCall(newExpr, right);
}
}
int type1 = exprType;
int dim1 = arrayDim;
String cname = className;
- if (modifiedExpr != null) {
- left = modifiedExpr;
- expr.setOprand1(left);
- }
-
right.accept(this);
- if (modifiedExpr != null) {
- right = modifiedExpr;
- expr.setOprand2(right);
- }
-
- modifiedExpr = computeConstExpr('+', left, right);
if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname))
|| (exprType == CLASS && arrayDim == 0
- && jvmJavaLangString.equals(className)))
- {
+ && jvmJavaLangString.equals(className))) {
+ ASTList sbufClass = ASTList.make(new Symbol("java"),
+ new Symbol("lang"), new Symbol("StringBuffer"));
+ ASTree e = new NewExpr(sbufClass, null);
exprType = CLASS;
arrayDim = 0;
- if (modifiedExpr != null) {
- // this expression is constant.
- className = jvmJavaLangString;
- return null;
- }
- else {
- className = "java/lang/StringBuffer";
- ASTList sbufClass = ASTList.make(new Symbol("java"),
- new Symbol("lang"),
- new Symbol("StringBuffer"));
- ASTree e = new NewExpr(sbufClass, null);
- return makeAppendCall(makeAppendCall(e, left), right);
- }
+ className = "java/lang/StringBuffer";
+ return makeAppendCall(makeAppendCall(e, left), right);
}
else {
computeBinExprType(expr, '+', type1);
if (CodeGen.isP_INT(exprType))
exprType = INT; // type1 may be BYTE, ...
-
- arrayDim = 0;
- // don't change the value of modifiedExpr.
}
private void booleanExpr(ASTree expr)
throws CompileError
{
- ASTree modExpr = null;
int op = CodeGen.getCompOperator(expr);
if (op == EQ) { // ==, !=, ...
BinExpr bexpr = (BinExpr)expr;
bexpr.oprand1().accept(this);
- if (modifiedExpr != null)
- bexpr.setOprand1(modifiedExpr);
-
int type1 = exprType;
int dim1 = arrayDim;
bexpr.oprand2().accept(this);
- if (modifiedExpr != null)
- bexpr.setOprand2(modifiedExpr);
-
if (dim1 == 0 && arrayDim == 0)
insertCast(bexpr, type1, exprType);
}
- else if (op == '!') {
+ else if (op == '!')
((Expr)expr).oprand1().accept(this);
- if (modifiedExpr != null)
- ((Expr)expr).setOprand1(modifiedExpr);
- }
else if (op == ANDAND || op == OROR) {
BinExpr bexpr = (BinExpr)expr;
bexpr.oprand1().accept(this);
- if (modifiedExpr != null)
- bexpr.setOprand1(modifiedExpr);
-
bexpr.oprand2().accept(this);
- if (modifiedExpr != null)
- bexpr.setOprand2(modifiedExpr);
}
- else { // others
+ else // others
expr.accept(this);
- modExpr = modifiedExpr;
- }
exprType = BOOLEAN;
arrayDim = 0;
- modifiedExpr = modExpr;
}
private void insertCast(BinExpr expr, int type1, int type2)
exprType = type1;
}
- private ASTree computeConstExpr(int op, ASTree left, ASTree right) {
- if (left instanceof StringL && right instanceof StringL && op == '+')
- return new StringL(((StringL)left).get() + ((StringL)right).get());
- else if (left instanceof IntConst)
- return ((IntConst)left).compute(op, right);
- else if (left instanceof DoubleConst)
- return ((DoubleConst)left).compute(op, right);
- else
- return null; // not constant expression
- }
-
public void atCastExpr(CastExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
expr.getOprand().accept(this);
- if (modifiedExpr != null)
- expr.setOprand(modifiedExpr);
-
exprType = expr.getType();
arrayDim = expr.getArrayDim();
className = cname;
- modifiedExpr = null;
}
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
expr.getOprand().accept(this);
- if (modifiedExpr != null)
- expr.setOprand(modifiedExpr);
-
exprType = BOOLEAN;
arrayDim = 0;
- modifiedExpr = null;
}
public void atExpr(Expr expr) throws CompileError {
atFieldRead(expr);
}
else if (token == ARRAY)
- atArrayRead(expr);
+ atArrayRead(oprand, expr.oprand2());
else if (token == PLUSPLUS || token == MINUSMINUS)
atPlusPlus(token, oprand, expr);
else if (token == '!')
else if (token == CALL) // method call
fatal();
else {
- oprand.accept(this);
- if (modifiedExpr != null) {
- oprand = modifiedExpr;
- expr.setOprand1(oprand);
- }
-
- modifiedExpr = computeConstExpr(token, oprand);
- if (token == '-' || token == '~') {
+ expr.oprand1().accept(this);
+ if (token == '-' || token == '~')
if (CodeGen.isP_INT(exprType))
exprType = INT; // type may be BYTE, ...
- }
}
}
- private ASTree computeConstExpr(int op, ASTree oprand) {
- if (oprand instanceof IntConst) {
- IntConst c = (IntConst)oprand;
- long v = c.get();
- if (op == '-')
- v = -v;
- else if (op == '~')
- v = ~v;
-
- c.set(v);
- }
- else if (oprand instanceof DoubleConst) {
- DoubleConst c = (DoubleConst)oprand;
- if (op == '-')
- c.set(-c.get());
- }
-
- return null;
- }
-
public void atCallExpr(CallExpr expr) throws CompileError {
String mname = null;
CtClass targetClass = null;
ASTree target = e.oprand1();
try {
target.accept(this);
- if (modifiedExpr != null)
- e.setOprand1(modifiedExpr);
}
catch (NoFieldException nfe) {
if (nfe.getExpr() != target)
MemberResolver.Method minfo
= atMethodCallCore(targetClass, mname, args);
expr.setMethod(minfo);
- modifiedExpr = null;
}
private static void badMethod() throws CompileError {
}
/**
- * modifiedExpr is not set.
- *
* @return a pair of the class declaring the invoked method
* and the MethodInfo of that method. Never null.
*/
while (args != null) {
ASTree a = args.head();
a.accept(this);
- if (modifiedExpr != null)
- args.setHead(modifiedExpr);
-
types[i] = exprType;
dims[i] = arrayDim;
cnames[i] = className;
}
}
- /* modifiedExpr is not set.
- */
void setReturnType(String desc) throws CompileError {
int i = desc.indexOf(')');
if (i < 0)
}
}
- private CtField atFieldRead(ASTree expr) throws CompileError {
- CtField f = fieldAccess(expr);
+ private void atFieldRead(ASTree expr) throws CompileError {
+ atFieldRead(fieldAccess(expr));
+ }
+
+ private void atFieldRead(CtField f) throws CompileError {
FieldInfo finfo = f.getFieldInfo2();
String type = finfo.getDescriptor();
className = type.substring(i + 1, type.indexOf(';', i + 1));
else
className = null;
-
- modifiedExpr = null; ??
- return f;
}
protected CtField fieldAccess(ASTree expr) throws CompileError {
else if (op == '.')
try {
e.oprand1().accept(this);
- if (modifiedExpr != null)
- e.setOprand1(modifiedExpr);
-
if (exprType == CLASS && arrayDim == 0)
return resolver.lookupFieldByJvmName(className,
(Symbol)e.oprand2());
exprType = CLASS;
arrayDim = 0;
className =jvmJavaLangClass;
- modifiedExpr = null;
}
public void atArrayLength(Expr expr) throws CompileError {
expr.oprand1().accept(this);
- if (modifiedExpr != null)
- expr.setOprand1(modifiedExpr);
-
exprType = INT;
arrayDim = 0;
- modifiedExpr = null;
}
- public void atArrayRead(Expr expr) throws CompileError {
- ASTree array = expr.oprand1();
+ public void atArrayRead(ASTree array, ASTree index)
+ throws CompileError
+ {
array.accept(this);
- if (modifiedExpr != null)
- expr.setOprand1(modifiedExpr);
-
int type = exprType;
int dim = arrayDim;
String cname = className;
-
- ASTree index = expr.oprand2();
index.accept(this);
- if (modifiedExpr != null)
- expr.setOprand2(modifiedExpr);
-
exprType = type;
arrayDim = dim - 1;
className = cname;
- modifiedExpr = null;
}
private void atPlusPlus(int token, ASTree oprand, Expr expr)
Declarator d = ((Variable)oprand).getDeclarator();
exprType = d.getType();
arrayDim = d.getArrayDim();
- modifiedExpr = null;
}
else {
if (oprand instanceof Expr) {
Expr e = (Expr)oprand;
if (e.getOperator() == ARRAY) {
- atArrayRead(e);
+ atArrayRead(e.oprand1(), e.oprand2());
// arrayDim should be 0.
int t = exprType;
if (t == INT || t == BYTE || t == CHAR || t == SHORT)
protected void atFieldPlusPlus(ASTree oprand) throws CompileError
{
- atFieldRead(oprand);
+ CtField f = fieldAccess(oprand);
+ atFieldRead(f);
int t = exprType;
if (t == INT || t == BYTE || t == CHAR || t == SHORT)
exprType = INT;
<p><div align="right"><a href="tutorial2.html">Next page</a></div>
-<ul>1. <a href="#read">Reading bytecode</a>
-<br>2. <a href="#def">Defining a new class</a>
-<br>3. <a href="#pool">ClassPool</a>
-<br>4. <a href="#load">Class loader</a>
-<br>5. <a href="tutorial2.html#intro">Introspection and customization</a>
-<br>6. <a href="tutorial3.html#intro">Bytecode level API</a>
+<ul>1. <a href="#read">Reading and writing bytecode</a>
+<br>2. <a href="#pool">ClassPool</a>
+<br>3. <a href="#load">Class loader</a>
+<br>4. <a href="tutorial2.html#intro">Introspection and customization</a>
+<br>5. <a href="tutorial3.html#intro">Bytecode level API</a>
</ul>
<p><br>
<a name="read">
-<h2>1. Reading bytecode</h2>
+<h2>1. Reading and writing bytecode</h2>
<p>Javassist is a class library for dealing with Java bytecode.
Java bytecode is stored in a binary file called a class file.
byte[] b = cc.toBytecode();
</pre></ul>
+<a name="def">
+<h4>Defining a new class</h4>
+
+<p>To define a new class from scratch, <code>makeClass()</code>
+must be called on a <code>ClassPool</code>.
+
+<ul><pre>
+ClassPool pool = ClassPool.getDefault();
+CtClass cc = pool.makeClass("Point");
+</pre></ul>
+
+<p>This program defines a class <code>Point</code>
+including no members.
+
+<h4>Frozen classes</h4>
+
+<p>If a <code>CtClass</code> object is converted into a class file by
+<code>writeFile()</code>, <code>toClass()</code>, or
+<code>toBytecode()</code>, Javassist freezes that <code>CtClass</code>
+object. Further modifications of that <code>CtClass</code> object are
+not permitted.
+
+<p>When Javassist freezes a <code>CtClass</code> object, it also
+prunes the data structure contained in that object. To reduce memory
+consumption, Javassist discards some part of data structure, for
+example, the data of method bodies. Thus, after a
+<code>CtClass</code> object is pruned, the bytecode of a method is not
+accessible although method names and signatures are accessible.
+
+<p>To disallow pruning a <code>CtClass</code>, <code>stopPruning()</code>
+must be called in advance:
+
+<ul><pre>
+CtClasss cc = ...;
+cc.stopPruning(true);
+ :
+cc.writeFile(); // convert to a class file.
+// cc is not pruned.
+</pre></ul>
+
+<p>If a <code>CtClass</code> is not pruned, it can be defrost so that
+modifications of the class definition can be permitted. For example,
+
+<ul><pre>
+CtClasss cc = ...;
+cc.stopPruning(true);
+ :
+cc.writeFile();
+cc.defrost();
+cc.setSuperclass(...); // OK since the class is not frozen.
+</pre></ul>
+
+
<h4>Class search path</h4>
<p>The default <code>ClassPool</code> returned
<p><br>
-<a name="def">
-<h2>2. Defining a new class</h2>
+<a name="pool">
+<h2>2. ClassPool</h2>
-<p>To define a new class from scratch, <code>makeClass()</code>
-must be called on a <code>ClassPool</code>.
+<p>
+A <code>ClassPool</code> object is a container of <code>CtClass</code>
+objects. Once a <code>CtClass</code> object is created, it is
+recorded in a <code>ClassPool</code> for ever. This is because a
+compiler may need to access the <code>CtClass</code> object later when
+it compiles source code that refers to the class represented by that
+<code>CtClass</code>.
+
+<p>
+For example, suppose that a new method <code>getter()</code> is added
+to a <code>CtClass</code> object representing <code>Point</code>
+class. Later, the program attempts to compile source code including a
+method call to <code>getter()</code> in <code>Point</code> and use the
+compiled code as the body of a method, which will be added to another
+class <code>Line</code>. If the <code>CtClass</code> object representing
+<code>Point</code> is lost, the compiler cannot compile the method call
+to <code>getter()</code>. Note that the original class definition does
+not include <code>getter()</code>. Therefore, to correctly compile
+such a method call, the <code>ClassPool</code>
+must contain all the instances of <code>CtClass</code> all the time of
+program execution.
+
+<h4>Avoid out of memory</h4>
+
+<p>
+This specification of <code>ClassPool</code> may cause huge memory
+consumption if the number of <code>CtClass</code> objects becomes
+amazingly large (this rarely happens since Javassist tries to reduce
+memory consumption in various ways). To avoid this problem, you
+can explicitly remove an unnecessary <code>CtClass</code> object from
+the <code>ClassPool</code>. If you call <code>detach()</code> on a
+<code>CtClass</code> object, then that <code>CtClass</code> object is
+removed from the <code>ClassPool</code>. For example,
<ul><pre>
-ClassPool pool = ClassPool.getDefault();
-CtClass cc = pool.makeClass("Point");
+CtClass cc = ... ;
+cc.writeFile();
+cc.detach();
</pre></ul>
-<p>This program defines a class <code>Point</code>
-including no members.
+<p>You must not call any method on that
+<code>CtClass</code> object after <code>detach()</code> is called.
+
+<p>
+Another idea is to occasionally replace a <code>ClassPool</code> with
+a new one and discard the old one. If an old <code>ClassPool</code>
+is garbage collected, the <code>CtClass</code> objects included in
+that <code>ClassPool</code> are also garbage collected.
+To create a new instance of <code>ClassPool</code>, execute the following
+code snippet:
-<p>A new class can be also defined as a copy of an existing class.
+<ul><pre>
+ClassPool cp = new ClassPool();
+cp.appendSystemPath(); // or append another path by appendClassPath()
+</pre></ul>
+
+<p>This creates a <code>ClassPool</code> object that behaves as the
+default <code>ClassPool</code> returned by
+<code>ClassPool.getDefault()</code> does.
+Note that <code>ClassPool.getDefault()</code> is a singleton factory method
+provided for convenience. Therefore, the default <code>ClassPool</code>
+is never garbage-collected.
+
+<h4>Cascaded ClassPools.</h4>
+
+<p>
+<code>ClassPool</code> objects can be cascaded like
+<code>java.lang.ClassLoader</code>. For example,
+
+<ul><pre>
+ClassPool parent = ClassPool.getDefault();
+ClassPool child = new ClassPool(parent);
+child.insertClassPath("./classes");
+</pre></ul>
+
+<p>
+If <code>child.get()</code> is called, the child <code>ClassPool</code>
+first delegates to the parent <code>ClassPool</code>. If the parent
+<code>ClassPool</code> fails to find a class file, then the child
+<code>ClassPool</code> attempts to find a class file
+under the <code>./classes</code> directory.
+
+<p>
+If <code>child.childFirstLookup</code> is true, the child
+<code>ClassPool</code> attempts to find a class file before delegating
+to the parent <code>ClassPool</code>. For example,
+
+<ul><pre>
+ClassPool parent = ClassPool.getDefault();
+ClassPool child = new ClassPool(parent);
+child.appendSystemPath(); // the same class path as the default one.
+child.childFirstLookup = true; // changes the behavior of the child.
+</pre></ul>
+
+<h4>Changing a class name for defining a new class</h4>
+
+<p>A new class can be defined as a copy of an existing class.
The program below does that:
<ul><pre>
never allows two distinct <code>CtClass</code> objects to represent
the same class unless two independent <code>ClassPool</code> are created.
This is a significant feature for consistent program
-transformation. To create multiple
-instances of <code>ClassPool</code>, write the following code:
+transformation.
+
+<p>To create another copy of the default instance of
+<code>ClassPool</code>, which is returned by
+<code>ClassPool.getDefault()</code>, execute the following code
+snippet (this code was already shown above):
<ul><pre>
ClassPool cp = new ClassPool();
cp.appendSystemPath(); // or append another path by appendClassPath()
</pre></ul>
-<p>This creates a <code>ClassPool</code> object that behaves as the
-default <code>ClassPool</code> returned by
-<code>ClassPool.getDefault()</code> does.
-<code>ClassPool.getDefault()</code> is a singleton factory method
-provided for convenience.
-
<p>If you have two <code>ClassPool</code> objects, then you can
obtain, from each <code>ClassPool</code>, a distinct
<code>CtClass</code> object representing the same class file. You can
differently modify these <code>CtClass</code> objects to generate
different versions of the class.
+<h4>Renaming a frozen class for defining a new class</h4>
+
<p>Once a <code>CtClass</code> object is converted into a class file
by <code>writeFile()</code> or <code>toBytecode()</code>, Javassist
rejects further modifications of that <code>CtClass</code> object.
is called on the the <code>CtClass</code> object representing <code>Point</code>
class.
-
-<p><br>
-
-<a name="pool">
-<h2>3. ClassPool</h2>
-
-<p>
-A <code>ClassPool</code> object is a container of <code>CtClass</code>
-objects. Once a <code>CtClass</code> object is created, it is
-recorded in a <code>ClassPool</code> for ever. This is because a
-compiler may need to access the <code>CtClass</code> object later when
-it compiles source code that refers to the class represented by that
-<code>CtClass</code>. If the class definition represented by that
-<code>CtClass</code> object is different from that of the original class
-file, the compiler cannot correctly compile the source code without
-the <code>CtClass</code> object.
-
-<p>
-This specification of <code>ClassPool</code> may cause huge memory
-consumption if the number of <code>CtClass</code> objects becomes large.
-To avoid this problem, you can explicitly remove an unnecessary
-<code>CtClass</code> object from the <code>ClassPool</code>. If you
-call <code>detach()</code> on a <code>CtClass</code> object, then that
-<code>CtClass</code> object is removed from the <code>ClassPool</code>.
-For example,
-
-<ul><pre>
-CtClass cc = ... ;
-cc.writeFile();
-cc.detach();
-</pre></ul>
-
-<p>You must not call any method on that
-<code>CtClass</code> object after <code>detach()</code> is called.
-
-<p><code>ClassPool</code> objects can be cascaded like
-<code>java.lang.ClassLoader</code>. For example,
-
-<ul><pre>
-ClassPool parent = ClassPool.getDefault();
-ClassPool child = new ClassPool(parent);
-</pre></ul>
-
-<p>If <code>child.get()</code> is called, the child <code>ClassPool</code>
-first delegates to the parent <code>ClassPool</code>. If the parent
-<code>ClassPool</code> fails to find a class file, then the child
-<code>ClassPool</code> attempts to find a class file.
-If <code>child.childFirstLookup</code> is true, the child
-<code>ClassPool</code> attempts to find a class file before delegating
-to the parent <code>ClassPool</code>. For example,
-
-<ul><pre>
-ClassPool parent = ClassPool.getDefault();
-ClassPool child = new ClassPool(parent);
-child.childFirstLookup = true; // changes the behavior of the child.
-</pre></ul>
-
<p><br>
<a name="load">
-<h2>4. Class loader</h2>
+<h2>3. Class loader</h2>
<p>If what classes must be modified is known in advance,
the easiest way for modifying the classes is as follows:
<p><br>
-<h3>4.1 The <code>toClass</code> method in <code>CtClass</code></h3>
+<h3>3.1 The <code>toClass</code> method in <code>CtClass</code></h3>
<p>The <code>CtClass</code> provides a convenience method
<code>toClass()</code>, which requests the context class loader for
<p><br>
-<h3>4.2 Class loading in Java</h3>
+<h3>3.2 Class loading in Java</h3>
<p>In Java, multiple class loaders can coexist and
each class loader creates its own name space.
<p><br>
-<h3>4.3 Using <code>javassist.Loader</code></h3>
+<h3>3.3 Using <code>javassist.Loader</code></h3>
<p>Javassist provides a class loader
<code>javassist.Loader</code>. This class loader uses a
<p><br>
-<h3>4.4 Writing a class loader</h3>
+<h3>3.4 Writing a class loader</h3>
<p>A simple class loader using Javassist is as follows:
<p><br>
-<h3>4.5 Modifying a system class</h3>
+<h3>3.5 Modifying a system class</h3>
<p>The system classes like <code>java.lang.String</code> cannot be
loaded by a class loader other than the system class loader.
<div align="right"><a href="tutorial3.html">Next page</a></div>
<p>
-<a href="#intro">5. Introspection and customization</a>
+<a href="#intro">4. Introspection and customization</a>
<ul>
<li><a href="#before">Inserting source text at the beginning/end of a method body</a>
<br><li><a href="#alter">Altering a method body</a>
<p><br>
<a name="intro">
-<h2>5. Introspection and customization</h2>
+<h2>4. Introspection and customization</h2>
<p><code>CtClass</code> provides methods for introspection. The
introspective ability of Javassist is compatible with that of
<p><br>
<a name="before">
-<h3>5.1 Inserting source text at the beginning/end of a method body</h3>
+<h3>4.1 Inserting source text at the beginning/end of a method body</h3>
<p><code>CtMethod</code> and <code>CtConstructor</code> provide
methods <code>insertBefore()</code>, <code>insertAfter()</code>, and
<p><br>
<a name="alter">
-<h3>5.2 Altering a method body</h3>
+<h3>4.2 Altering a method body</h3>
<p><code>CtMethod</code> and <code>CtConstructor</code> provide
<code>setBody()</code> for substituting a whole
<p><br>
<a name="add">
-<h3>5.3 Adding a new method or field</h3>
+<h3>4.3 Adding a new method or field</h3>
<h4>Adding a method</h4>
<p><br>
<a name="runtime">
-<h3>5.4 Runtime support classes</h3>
+<h3>4.4 Runtime support classes</h3>
<p>In most cases, a class modified by Javassist does not require
Javassist to run. However, some kinds of bytecode generated by the
<p><br>
<a name="limit">
-<h3>5.5 Limitations</h3>
+<h3>4.5 Limitations</h3>
<p>In the current implementation, the Java compiler included in Javassist
has several limitations with respect to the language that the compiler can
exception; for example, the compiler accepts <code>Object</code> as
well as <code>java.lang.Object</code>.
-<p><li>The <code>.class</code> notation is not supported. Use the
-method <code>Class.forName()</code>.
-In regular
-Java, an expression <code>Point.class</code> means a <code>Class</code>
-object representing the <code>Point</code> class. This notation is
-not available.
-
<p><li>Array initializers, a comma-separated list of expressions
enclosed by braces <code>{</code> and <code>}</code>, are not
supported.
<div align="left"><a href="tutorial2.html">Previous page</a></div>
<p>
-<a href="#intro">6. Bytecode level API</a>
+<a href="#intro">5. Bytecode level API</a>
<ul>
<li><a href="#classfile">Obtaining a <code>ClassFile</code> object</a>
<br><li><a href="#member">Adding and removing a member</a>
<p><br>
<a name="intro">
-<h2>6. Bytecode level API</h2>
+<h2>5. Bytecode level API</h2>
<p>
Javassist also provides lower-level API for directly editing
of class files.
<a name="classfile">
-<h3>6.1 Obtaining a <code>ClassFile</code> object</h3>
+<h3>5.1 Obtaining a <code>ClassFile</code> object</h3>
<p>A <code>javassist.bytecode.ClassFile</code> object represents
a class file. To obtian this object, <code>getClassFile()</code>
<p><br>
<a name="member">
-<h3>6.2 Adding and removing a member</h3>
+<h3>5.2 Adding and removing a member</h3>
<p>
<code>ClassFile</code> provides <code>addField()</code> and
<p><br>
<a name="traverse">
-<h3>6.3 Traversing a method body</h3>
+<h3>5.3 Traversing a method body</h3>
<p>
To examine every bytecode instruction in a method body,
<p><br>
<a name="bytecode">
-<h3>6.4 Producing a bytecode sequence</h3>
+<h3>5.4 Producing a bytecode sequence</h3>
<p>
A <code>Bytecode</code> object represents a sequence of bytecode