<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.
<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>
* This method returns <code>null</code> if
* no class initializer is not declared.
*
+ * @see #makeClassInitializer()
* @see javassist.CtConstructor
*/
public CtConstructor getClassInitializer() {
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.
*/
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;
}
}
+ 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
{
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)
}
}
- 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);
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);
/**
* 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(),
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.
*/
public CodeGen(Bytecode b) {
bytecode = b;
tempVar = -1;
+ hasReturned = false;
inStaticMethod = false;
breakList = null;
continueList = null;
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;
}
}
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 {
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);
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;
}
}
patchGoto(continueList, pc3);
continueList = prevContList;
breakList = prevBreakList;
+ hasReturned = false;
}
private void patchGoto(ArrayList list, int targetPc) {
patchGoto(continueList, pc3);
continueList = prevContList;
breakList = prevBreakList;
+ hasReturned = false;
}
private void atBreakStmnt(Stmnt st, boolean notCont)
}
bytecode.addOpcode(op);
+ hasReturned = true;
}
private void atThrowStmnt(Stmnt st) throws CompileError {
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) {
"sorry, finally has not been supported yet");
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
+ hasReturned = false;
}
public void atNewExpr(NewExpr expr) throws CompileError {
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);
}
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);
}
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;
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
<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,
<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>