From: chiba Date: Wed, 14 May 2003 16:20:13 +0000 (+0000) Subject: fixed several compiler bugs and updated the tutorial. X-Git-Tag: rel_3_17_1_ga~590 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dea5156362fd172b97c0adb001402fdd6107f2a7;p=javassist.git fixed several compiler bugs and updated the tutorial. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@14 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/Readme.html b/Readme.html index 7afd488b..be433252 100644 --- a/Readme.html +++ b/Readme.html @@ -242,6 +242,8 @@ see javassist.Dump.
  • The license was changed from MPL to MPL/LGPL dual.
  • ClassPool.removeClassPath() and ClassPath.close() have been added.
  • ClassPool.makeClass(InputStream) has been added. +
  • CtClass.makeClassInitializer() has been added. +
  • javassist.expr.Expr has been changed to a public class.
  • javassist.expr.Handler has been added.
  • CtMethod.isEmpty() and CtConstructor.isEmpty() have been added.
  • LoaderClassPath has been implemented. @@ -410,13 +412,17 @@ see javassist.Dump.
    Bug reports: -
    chiba@acm.org -
    or +
    Post your reports at Forums +or directly send an email to: +
      +chiba@acm.org +or chiba@is.titech.ac.jp

    The home page of Javassist: -
    Visit www.jboss.org +
    Visit www.javassist.org +and www.jboss.org
    diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 04576b49..497b6c8f 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -512,6 +512,7 @@ public abstract class CtClass { * This method returns null 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. */ diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 9e6f90d3..73a873e9 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -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(), - "", "()V"); + code.setMaxLocals(localsize); + m = new MethodInfo(cf.getConstPool(), "", "()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); diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java index f83bcd20..e44e6db0 100644 --- a/src/main/javassist/CtConstructor.java +++ b/src/main/javassist/CtConstructor.java @@ -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(), diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 754b5a48..60170dbb 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -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) { diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 437f1f80..97a12341 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -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; // 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); } diff --git a/src/main/javassist/expr/Expr.java b/src/main/javassist/expr/Expr.java index d34180d5..b02d40e9 100644 --- a/src/main/javassist/expr/Expr.java +++ b/src/main/javassist/expr/Expr.java @@ -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; diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 6287f1ee..62c26a94 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -74,8 +74,8 @@ into the body of an existing method. The users can specify those code fragments with source text 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 +inlined into a method body.

    The methods insertBefore(), insertAfter(), and addCatch() receives a String object representing @@ -989,6 +989,8 @@ exception.

    5.3 Adding a new method or field

    +

    Adding a method

    +

    Javassist allows the users to create a new method and constructor from scratch. CtNewMethod and CtNewConstructor provide several factory methods, @@ -1032,6 +1034,34 @@ public int ymove(int dy) { this.move(0, dy); }

    Note that $proceed has been replaced with this.move. +

    Mutual recursive methods

    + +

    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 +m() and n() to a class represented +by cc: + +

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

    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 addMethod() automatically +changes a class into an abstract one if an abstract method is added. + +

    Adding a field

    +

    Javassist also allows the users to create a new field.