]> source.dussan.org Git - javassist.git/commitdiff
fixed several compiler bugs and updated the tutorial.
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 14 May 2003 16:20:13 +0000 (16:20 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 14 May 2003 16:20:13 +0000 (16:20 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@14 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/CtConstructor.java
src/main/javassist/compiler/CodeGen.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/expr/Expr.java
tutorial/tutorial2.html

index 7afd488be6bcf9ad937b69b0002c43588bd1b9d7..be433252e1f19c04751c821ee3d77fc3be04fde3 100644 (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>
 
index 04576b49f255804640944d48361ed9dccea1dd89..497b6c8ff0f8b97e194266225e2224be4c912fd3 100644 (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.
      */
index 9e6f90d3f12bc408b73f2374985bd781c248c4d8..73a873e97f4ff147772e40b6d92f66e5b1e76591 100644 (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);
index f83bcd2046623ec9f8bc5202b8e577888f0c07a1..e44e6db03e8b7b404db1bac135676400fe362600 100644 (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(),
index 754b5a4856973fc19ce7edb9f13d424c42d2e8d1..60170dbbfcf816fce4a02c39823e33327f368a47 100644 (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) {
index 437f1f80e531b96c6c4f5324c7acb32a6154e399..97a123410fe694810b400a4e8aa8f585f4ba7411 100644 (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);
     }
index d34180d5a3b8b74cb0eed6f05a4f49c5c6f16655..b02d40e96036243de971aa6e1366e9999ede0e89 100644 (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;
index 6287f1ee5a6f6087f7e36d6653e4ed92474b6bee..62c26a942417ffc19f02b105daa4ac82e40b9c92 100644 (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>