git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@14 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -242,6 +242,8 @@ see javassist.Dump. | |||
<li>The license was changed from MPL to MPL/LGPL dual. | |||
<li>ClassPool.removeClassPath() and ClassPath.close() have been added. | |||
<li>ClassPool.makeClass(InputStream) has been added. | |||
<li>CtClass.makeClassInitializer() has been added. | |||
<li>javassist.expr.Expr has been changed to a public class. | |||
<li>javassist.expr.Handler has been added. | |||
<li>CtMethod.isEmpty() and CtConstructor.isEmpty() have been added. | |||
<li>LoaderClassPath has been implemented. | |||
@@ -410,13 +412,17 @@ see javassist.Dump. | |||
<dl> | |||
<dt>Bug reports: | |||
<dd><tt><a href="mailto:chiba@acm.org">chiba@acm.org</a></tt> | |||
<br>or | |||
<dd>Post your reports at <a href="http://www.jboss.org/jive.jsp">Forums</a> | |||
or directly send an email to: | |||
<br> | |||
<tt><a href="mailto:chiba@acm.org">chiba@acm.org</a></tt> | |||
or | |||
<tt><a href="mailto:chiba@is.titech.ac.jp">chiba@is.titech.ac.jp</a></tt> | |||
<br> | |||
<p><dt>The home page of Javassist: | |||
<dd>Visit <a href="http://www.jboss.org/index.html?module=html&op=userdisplay&id=developers/projects/javassist"><tt>www.jboss.org</tt></a> | |||
<dd>Visit <a href="http://www.javassist.org"><tt>www.javassist.org</tt></a> | |||
and <a href="http://www.jboss.org/index.html?module=html&op=userdisplay&id=developers/projects/javassist"><tt>www.jboss.org</tt></a> | |||
</dl> | |||
@@ -512,6 +512,7 @@ public abstract class CtClass { | |||
* This method returns <code>null</code> if | |||
* no class initializer is not declared. | |||
* | |||
* @see #makeClassInitializer() | |||
* @see javassist.CtConstructor | |||
*/ | |||
public CtConstructor getClassInitializer() { | |||
@@ -584,6 +585,19 @@ public abstract class CtClass { | |||
throw new NotFoundException(name); | |||
} | |||
/** | |||
* Makes a class initializer (static constructor). | |||
* If the class already includes a class initializer, | |||
* this method returns it. | |||
* | |||
* @see #getClassInitializer() | |||
*/ | |||
public CtConstructor makeClassInitializer() | |||
throws CannotCompileException | |||
{ | |||
throw new CannotCompileException("not a class"); | |||
} | |||
/** | |||
* Adds a constructor. | |||
*/ |
@@ -449,15 +449,9 @@ class CtClassType extends CtClass { | |||
public CtConstructor getClassInitializer() { | |||
if (classInitializerCache == null) { | |||
List list = getClassFile2().getMethods(); | |||
int n = list.size(); | |||
for (int i = 0; i < n; ++i) { | |||
MethodInfo minfo = (MethodInfo)list.get(i); | |||
if (minfo.isStaticInitializer()) { | |||
classInitializerCache = new CtConstructor(minfo, this); | |||
break; | |||
} | |||
} | |||
MethodInfo minfo = getClassFile2().getStaticInitializer(); | |||
if (minfo != null) | |||
classInitializerCache = new CtConstructor(minfo, this); | |||
} | |||
return classInitializerCache; | |||
@@ -635,6 +629,26 @@ class CtClassType extends CtClass { | |||
} | |||
} | |||
public CtConstructor makeClassInitializer() | |||
throws CannotCompileException | |||
{ | |||
CtConstructor clinit = getClassInitializer(); | |||
if (clinit != null) | |||
return clinit; | |||
checkModify(); | |||
ClassFile cf = getClassFile2(); | |||
Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); | |||
try { | |||
modifyClassConstructor(cf, b, 0, 0); | |||
} | |||
catch (CompileError e) { | |||
throw new CannotCompileException(e); | |||
} | |||
return getClassInitializer(); | |||
} | |||
public void addConstructor(CtConstructor c) | |||
throws CannotCompileException | |||
{ | |||
@@ -728,11 +742,11 @@ class CtClassType extends CtClass { | |||
Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); | |||
Javac jv = new Javac(code, this); | |||
int stacksize = 0; | |||
boolean none = true; | |||
boolean doInit = false; | |||
for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { | |||
CtField f = fi.field; | |||
if (Modifier.isStatic(f.getModifiers())) { | |||
none = false; | |||
doInit = true; | |||
int s = fi.init.compileIfStatic(f.getType(), f.getName(), | |||
code, jv); | |||
if (stacksize < s) | |||
@@ -740,15 +754,20 @@ class CtClassType extends CtClass { | |||
} | |||
} | |||
if (none) | |||
return; // no initializer for static fileds. | |||
if (doInit) // need an initializer for static fileds. | |||
modifyClassConstructor(cf, code, stacksize, 0); | |||
} | |||
private void modifyClassConstructor(ClassFile cf, Bytecode code, | |||
int stacksize, int localsize) | |||
throws CannotCompileException | |||
{ | |||
MethodInfo m = cf.getStaticInitializer(); | |||
if (m == null) { | |||
code.add(Bytecode.RETURN); | |||
code.setMaxStack(stacksize); | |||
m = new MethodInfo(cf.getConstPool(), | |||
"<clinit>", "()V"); | |||
code.setMaxLocals(localsize); | |||
m = new MethodInfo(cf.getConstPool(), "<clinit>", "()V"); | |||
m.setAccessFlags(AccessFlag.STATIC); | |||
m.setCodeAttribute(code.toCodeAttribute()); | |||
cf.addMethod(m); | |||
@@ -765,6 +784,10 @@ class CtClassType extends CtClass { | |||
int maxstack = codeAttr.getMaxStack(); | |||
if (maxstack < stacksize) | |||
codeAttr.setMaxStack(stacksize); | |||
int maxlocals = codeAttr.getMaxLocals(); | |||
if (maxlocals < localsize) | |||
codeAttr.setMaxLocals(localsize); | |||
} | |||
catch (BadBytecode e) { | |||
throw new CannotCompileException(e); |
@@ -390,12 +390,16 @@ public final class CtConstructor extends CtBehavior { | |||
/** | |||
* Inserts bytecode just after another constructor in the super class | |||
* or this class is called. | |||
* It does not work if this object represents a class initializer. | |||
* | |||
* @param src the source code representing the inserted bytecode. | |||
* It must be a single statement or block. | |||
*/ | |||
public void insertBeforeBody(String src) throws CannotCompileException { | |||
declaringClass.checkModify(); | |||
if (isClassInitializer()) | |||
throw new CannotCompileException("class initializer"); | |||
CodeAttribute ca = methodInfo.getCodeAttribute(); | |||
CodeIterator iterator = ca.iterator(); | |||
Bytecode b = new Bytecode(methodInfo.getConstPool(), |
@@ -34,6 +34,11 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
protected Bytecode bytecode; | |||
private int tempVar; | |||
/** | |||
* true if the last visited node is a return statement. | |||
*/ | |||
protected boolean hasReturned; | |||
/** | |||
* Must be true if compilation is for a static method. | |||
*/ | |||
@@ -51,6 +56,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
public CodeGen(Bytecode b) { | |||
bytecode = b; | |||
tempVar = -1; | |||
hasReturned = false; | |||
inStaticMethod = false; | |||
breakList = null; | |||
continueList = null; | |||
@@ -229,11 +235,11 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
if (isCons && needsSuperCall(s)) | |||
insertDefaultSuperCall(); | |||
hasReturned = false; | |||
s.accept(this); | |||
if (isVoid | |||
&& (bytecode.read(bytecode.currentPc() - 1) & 0xff) | |||
!= Opcode.RETURN) { | |||
if (isVoid && !hasReturned) { | |||
bytecode.addOpcode(Opcode.RETURN); | |||
hasReturned = true; | |||
} | |||
} | |||
@@ -302,9 +308,12 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
atThrowStmnt(st); | |||
else if (op == TRY) | |||
atTryStmnt(st); | |||
else // LABEL, SWITCH label stament might be null?. | |||
else { | |||
// LABEL, SWITCH label stament might be null?. | |||
hasReturned = false; | |||
throw new CompileError( | |||
"sorry, not supported statement: TokenId " + op); | |||
} | |||
} | |||
private void atIfStmnt(Stmnt st) throws CompileError { | |||
@@ -316,10 +325,14 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int pc2 = 0; | |||
bytecode.addIndex(0); // correct later | |||
hasReturned = false; | |||
if (thenp != null) | |||
thenp.accept(this); | |||
if (elsep != null) { | |||
boolean thenHasReturned = hasReturned; | |||
hasReturned = false; | |||
if (elsep != null && !thenHasReturned) { | |||
bytecode.addOpcode(Opcode.GOTO); | |||
pc2 = bytecode.currentPc(); | |||
bytecode.addIndex(0); | |||
@@ -329,7 +342,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
if (elsep != null) { | |||
elsep.accept(this); | |||
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); | |||
if (!thenHasReturned) | |||
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); | |||
hasReturned = thenHasReturned && hasReturned; | |||
} | |||
} | |||
@@ -364,6 +380,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
patchGoto(continueList, pc3); | |||
continueList = prevContList; | |||
breakList = prevBreakList; | |||
hasReturned = false; | |||
} | |||
private void patchGoto(ArrayList list, int targetPc) { | |||
@@ -416,6 +433,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
patchGoto(continueList, pc3); | |||
continueList = prevContList; | |||
breakList = prevBreakList; | |||
hasReturned = false; | |||
} | |||
private void atBreakStmnt(Stmnt st, boolean notCont) | |||
@@ -462,6 +480,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
} | |||
bytecode.addOpcode(op); | |||
hasReturned = true; | |||
} | |||
private void atThrowStmnt(Stmnt st) throws CompileError { | |||
@@ -471,9 +490,12 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
throw new CompileError("bad throw statement"); | |||
bytecode.addOpcode(ATHROW); | |||
hasReturned = true; | |||
} | |||
protected abstract void atTryStmnt(Stmnt st) throws CompileError; | |||
protected void atTryStmnt(Stmnt st) throws CompileError { | |||
hasReturned = false; | |||
} | |||
private static boolean isPlusPlusExpr(ASTree expr) { | |||
if (expr instanceof Expr) { |
@@ -107,6 +107,7 @@ public class MemberCodeGen extends CodeGen { | |||
"sorry, finally has not been supported yet"); | |||
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | |||
hasReturned = false; | |||
} | |||
public void atNewExpr(NewExpr expr) throws CompileError { | |||
@@ -228,13 +229,20 @@ public class MemberCodeGen extends CodeGen { | |||
if (method instanceof Member) { | |||
mname = ((Member)method).get(); | |||
targetClass = thisClass; | |||
bytecode.addAload(0); // this | |||
if (inStaticMethod) | |||
isStatic = true; // should be static | |||
else | |||
bytecode.addAload(0); // this | |||
} | |||
else if (method instanceof Keyword) { // constructor | |||
isSpecial = true; | |||
mname = MethodInfo.nameInit; // <init> | |||
targetClass = thisClass; | |||
bytecode.addAload(0); // this | |||
if (inStaticMethod) | |||
throw new CompileError("a constructor cannot be static"); | |||
else | |||
bytecode.addAload(0); // this | |||
if (((Keyword)method).get() == SUPER) | |||
targetClass = getSuperclass(targetClass); | |||
} | |||
@@ -358,7 +366,10 @@ public class MemberCodeGen extends CodeGen { | |||
else if (declClass.isInterface()) | |||
bytecode.addInvokeinterface(declClass, mname, desc, count); | |||
else | |||
bytecode.addInvokevirtual(declClass, mname, desc); | |||
if (isStatic) | |||
throw new CompileError(mname + " is not static"); | |||
else | |||
bytecode.addInvokevirtual(declClass, mname, desc); | |||
setReturnType(desc, isStatic, popTarget); | |||
} |
@@ -22,9 +22,9 @@ import java.util.LinkedList; | |||
import java.util.Iterator; | |||
/** | |||
* Caller-side expression. | |||
* Expression. | |||
*/ | |||
abstract class Expr implements Opcode { | |||
public abstract class Expr implements Opcode { | |||
int currentPos; | |||
CodeIterator iterator; | |||
CtClass thisClass; |
@@ -74,8 +74,8 @@ into the body of an existing method. The users can specify those code | |||
fragments with <em>source text</em> written in Java. | |||
Javassist includes a simple Java compiler for processing source | |||
text. It receives source text | |||
written in Java and compiles it into Java bytecode, which will be inserted | |||
into a method body. | |||
written in Java and compiles it into Java bytecode, which will be | |||
<em>inlined</em> into a method body. | |||
<p>The methods <code>insertBefore()</code>, <code>insertAfter()</code>, and | |||
<code>addCatch()</code> receives a <code>String</code> object representing | |||
@@ -989,6 +989,8 @@ exception. | |||
<h3>5.3 Adding a new method or field</h3> | |||
<h4>Adding a method</h4> | |||
<p>Javassist allows the users to create a new method and constructor | |||
from scratch. <code>CtNewMethod</code> | |||
and <code>CtNewConstructor</code> provide several factory methods, | |||
@@ -1032,6 +1034,34 @@ public int ymove(int dy) { this.move(0, dy); } | |||
<p>Note that <code>$proceed</code> has been replaced with | |||
<code>this.move</code>. | |||
<h4>Mutual recursive methods</h4> | |||
<p>Javassist cannot compile a method if it calls another method that | |||
has not been added to a class. (Javassist can compile a method that | |||
calls itself recursively.) To add mutual recursive methods to a class, | |||
you need a trick shown below. Suppose that you want to add methods | |||
<code>m()</code> and <code>n()</code> to a class represented | |||
by <code>cc</code>: | |||
<ul><pre> | |||
CtClass cc = ... ; | |||
CtMethod m = CtNewMethod.make("public abstract int m(int i);", cc); | |||
CtMethod n = CtNewMethod.make("public abstract int n(int i);", cc); | |||
cc.addMethod(m); | |||
cc.addMethod(n); | |||
m.setBody("{ return ($1 <= 0) ? 1 : (n($1 - 1) * $1); }"); | |||
n.setBody("{ return m($1); }"); | |||
cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT); | |||
</pre></ul> | |||
<p>You must first make two abstract methods and add them to the class. | |||
Then you can give the method bodies to these methods even if the method | |||
bodies include method calls to each other. Finally you must change the | |||
class to a not-abstract class since <code>addMethod()</code> automatically | |||
changes a class into an abstract one if an abstract method is added. | |||
<h4>Adding a field</h4> | |||
<p>Javassist also allows the users to create a new field. | |||
<ul><pre> |