diff options
-rw-r--r-- | Readme.html | 5 | ||||
-rw-r--r-- | src/main/javassist/compiler/CodeGen.java | 25 | ||||
-rw-r--r-- | src/main/javassist/compiler/TypeChecker.java | 289 | ||||
-rw-r--r-- | tutorial/tutorial.html | 254 | ||||
-rw-r--r-- | tutorial/tutorial2.html | 21 | ||||
-rw-r--r-- | tutorial/tutorial3.html | 12 |
6 files changed, 246 insertions, 360 deletions
diff --git a/Readme.html b/Readme.html index a1db7766..035bd6bd 100644 --- a/Readme.html +++ b/Readme.html @@ -262,6 +262,9 @@ see javassist.Dump. 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. @@ -565,7 +568,7 @@ Andreas Salathe, Dante Torres estrada, S. Pam, Nuno Santos, 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> diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 2c186eed..35d06817 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -199,28 +199,20 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { } 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(); } @@ -308,7 +300,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { 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)) { @@ -568,7 +560,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { */ ASTree init = d.getInitializer(); if (init != null) { - init = doTypeCheck(init); + doTypeCheck(init); atVariableAssign(null, '=', null, d, init, false); } } @@ -798,10 +790,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { 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"); diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index d57b95a5..196c7642 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/src/main/javassist/compiler/TypeChecker.java @@ -17,17 +17,11 @@ package javassist.compiler; 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"; @@ -40,7 +34,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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; @@ -105,7 +98,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { exprType = CLASS; arrayDim = 0; className = MemberResolver.javaToJvmName(cname); - modifiedExpr = null; } } @@ -117,12 +109,8 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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 @@ -142,11 +130,8 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { ++count; s.accept(this); - if (modifiedExpr != null) - size.setHead(modifiedExpr); } - modifiedExpr = null; exprType = type; arrayDim = dim; if (type == CLASS) @@ -189,80 +174,49 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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)) @@ -271,89 +225,63 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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); } } @@ -363,37 +291,17 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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); @@ -428,54 +336,33 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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) @@ -487,37 +374,18 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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 { @@ -543,7 +411,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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 == '!') @@ -551,40 +419,13 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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; @@ -614,8 +455,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { ASTree target = e.oprand1(); try { target.accept(this); - if (modifiedExpr != null) - e.setOprand1(modifiedExpr); } catch (NoFieldException nfe) { if (nfe.getExpr() != target) @@ -643,7 +482,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { MemberResolver.Method minfo = atMethodCallCore(targetClass, mname, args); expr.setMethod(minfo); - modifiedExpr = null; } private static void badMethod() throws CompileError { @@ -651,8 +489,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } /** - * modifiedExpr is not set. - * * @return a pair of the class declaring the invoked method * and the MethodInfo of that method. Never null. */ @@ -695,9 +531,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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; @@ -706,8 +539,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } } - /* modifiedExpr is not set. - */ void setReturnType(String desc) throws CompileError { int i = desc.indexOf(')'); if (i < 0) @@ -735,8 +566,11 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } } - 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(); @@ -755,9 +589,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { className = type.substring(i + 1, type.indexOf(';', i + 1)); else className = null; - - modifiedExpr = null; ?? - return f; } protected CtField fieldAccess(ASTree expr) throws CompileError { @@ -780,9 +611,6 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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()); @@ -808,38 +636,25 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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) @@ -853,13 +668,12 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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) @@ -875,7 +689,8 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { 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; diff --git a/tutorial/tutorial.html b/tutorial/tutorial.html index 064f8da8..84de35b4 100644 --- a/tutorial/tutorial.html +++ b/tutorial/tutorial.html @@ -18,19 +18,18 @@ Shigeru Chiba <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. @@ -91,6 +90,59 @@ modified bytecode. To obtain the bytecode, call <code>toBytecode()</code>: 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 @@ -187,21 +239,106 @@ included in the search path. <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> @@ -258,26 +395,26 @@ mapping between classes and <code>CtClass</code> objects. Javassist 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. @@ -314,67 +451,10 @@ can be executed after <code>writeFile()</code> or <code>toBytecode()</code> 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: @@ -396,7 +476,7 @@ by Javassist. <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 @@ -475,7 +555,7 @@ more complex functionality, you should write your own class loader. <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. @@ -655,7 +735,7 @@ be helpful: <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 @@ -792,7 +872,7 @@ make sure whether all the classes using that class have been loaded by <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: @@ -863,7 +943,7 @@ Hence, the <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. diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 50d24712..9c158377 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -13,7 +13,7 @@ <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> @@ -25,7 +25,7 @@ <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 @@ -115,7 +115,7 @@ of the <code>javassist.runtime</code> package. <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 @@ -553,7 +553,7 @@ catch (java.io.IOException e) { <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 @@ -1238,7 +1238,7 @@ exception. <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> @@ -1362,7 +1362,7 @@ does not end with a semi colon (<code>;</code>). <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 @@ -1376,7 +1376,7 @@ Javassist classes are never used at runtime of the modified classes. <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 @@ -1389,13 +1389,6 @@ declarations. However, the <code>java.lang</code> package is an 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. diff --git a/tutorial/tutorial3.html b/tutorial/tutorial3.html index c965a64c..af0a4c1b 100644 --- a/tutorial/tutorial3.html +++ b/tutorial/tutorial3.html @@ -12,7 +12,7 @@ <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> @@ -25,7 +25,7 @@ <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 @@ -35,7 +35,7 @@ while this level of API allows you any kind of modification of class files. <a name="classfile"> -<h3>6.1 Obtaining a <code>ClassFile</code> object</h3> +<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> @@ -64,7 +64,7 @@ writes the contents of the class file to a given <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 @@ -98,7 +98,7 @@ and remove one from the list. <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, @@ -143,7 +143,7 @@ Branch offsets etc. are automatically adjusted.<br> <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 |