Browse Source

fixed several compiler bugs and updated the tutorial.


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

+ 9
- 3
Readme.html View File

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


+ 14
- 0
src/main/javassist/CtClass.java View File

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

+ 38
- 15
src/main/javassist/CtClassType.java View File

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

+ 4
- 0
src/main/javassist/CtConstructor.java View File

@@ -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(),

+ 29
- 7
src/main/javassist/compiler/CodeGen.java View File

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

+ 14
- 3
src/main/javassist/compiler/MemberCodeGen.java View File

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

+ 2
- 2
src/main/javassist/expr/Expr.java View File

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

+ 32
- 2
tutorial/tutorial2.html View File

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

Loading…
Cancel
Save