Browse Source

discarded the last changes


git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@129 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
tags/rel_3_17_1_ga
chiba 19 years ago
parent
commit
4fda748aa5

+ 4
- 1
Readme.html View File

@@ -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>

+ 10
- 15
src/main/javassist/compiler/CodeGen.java View File

@@ -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");

+ 52
- 237
src/main/javassist/compiler/TypeChecker.java View File

@@ -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;

+ 167
- 87
tutorial/tutorial.html View File

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

+ 7
- 14
tutorial/tutorial2.html View File

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

+ 6
- 6
tutorial/tutorial3.html View File

@@ -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

Loading…
Cancel
Save