git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@371 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
String[] cachedInterfaces; | String[] cachedInterfaces; | ||||
String cachedSuperclass; | String cachedSuperclass; | ||||
/** | |||||
* The major version number of class files created | |||||
* from scratch. The value is 45 (JDK 1.1). | |||||
*/ | |||||
public static final int MAJOR_VERSION = 45; | |||||
/** | /** | ||||
* Constructs a class file from a byte stream. | * Constructs a class file from a byte stream. | ||||
*/ | */ | ||||
* a fully-qualified super class name | * a fully-qualified super class name | ||||
*/ | */ | ||||
public ClassFile(boolean isInterface, String classname, String superclass) { | public ClassFile(boolean isInterface, String classname, String superclass) { | ||||
major = 45; | |||||
major = MAJOR_VERSION; | |||||
minor = 3; // JDK 1.1 or later | minor = 3; // JDK 1.1 or later | ||||
constPool = new ConstPool(classname); | constPool = new ConstPool(classname); | ||||
thisClass = constPool.getThisClassInfo(); | thisClass = constPool.getThisClassInfo(); |
cname = MemberResolver.jvmToJavaName(cname); | cname = MemberResolver.jvmToJavaName(cname); | ||||
} | } | ||||
atClassObject2(cname); | |||||
exprType = CLASS; | |||||
arrayDim = 0; | |||||
className = "java/lang/Class"; | |||||
} | |||||
/* MemberCodeGen overrides this method. | |||||
*/ | |||||
protected void atClassObject2(String cname) throws CompileError { | |||||
int start = bytecode.currentPc(); | int start = bytecode.currentPc(); | ||||
bytecode.addLdc(cname); | bytecode.addLdc(cname); | ||||
bytecode.addInvokestatic("java.lang.Class", "forName", | bytecode.addInvokestatic("java.lang.Class", "forName", | ||||
+ "Ljava/lang/NoClassDefFoundError;"); | + "Ljava/lang/NoClassDefFoundError;"); | ||||
bytecode.addOpcode(ATHROW); | bytecode.addOpcode(ATHROW); | ||||
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | ||||
exprType = CLASS; | |||||
arrayDim = 0; | |||||
className = "java/lang/Class"; | |||||
} | } | ||||
public void atArrayRead(ASTree array, ASTree index) | public void atArrayRead(ASTree array, ASTree index) |
/* Code generator methods depending on javassist.* classes. | /* Code generator methods depending on javassist.* classes. | ||||
*/ | */ | ||||
public class MemberCodeGen extends CodeGen { | public class MemberCodeGen extends CodeGen { | ||||
public static final int JAVA5_VER = 49; | |||||
public static final int JAVA6_VER = 50; | |||||
protected MemberResolver resolver; | protected MemberResolver resolver; | ||||
protected CtClass thisClass; | protected CtClass thisClass; | ||||
protected MethodInfo thisMethod; | protected MethodInfo thisMethod; | ||||
protected int version; // the major version of a class file. | |||||
protected boolean resultStatic; | protected boolean resultStatic; | ||||
resolver = new MemberResolver(cp); | resolver = new MemberResolver(cp); | ||||
thisClass = cc; | thisClass = cc; | ||||
thisMethod = null; | thisMethod = null; | ||||
ClassFile cf = cc.getClassFile2(); | |||||
if (cf == null) | |||||
version = 0; | |||||
else | |||||
version = cf.getMajorVersion(); | |||||
} | } | ||||
/** | /** | ||||
static class JsrHook extends ReturnHook { | static class JsrHook extends ReturnHook { | ||||
ArrayList jsrList; | ArrayList jsrList; | ||||
CodeGen cgen; | |||||
int var; | int var; | ||||
JsrHook(CodeGen gen) { | JsrHook(CodeGen gen) { | ||||
super(gen); | super(gen); | ||||
jsrList = new ArrayList(); | jsrList = new ArrayList(); | ||||
var = gen.getMaxLocals(); | |||||
gen.incMaxLocals(1); | |||||
cgen = gen; | |||||
var = -1; | |||||
} | |||||
private int getVar(int size) { | |||||
if (var < 0) { | |||||
var = cgen.getMaxLocals(); | |||||
cgen.incMaxLocals(size); | |||||
} | |||||
return var; | |||||
} | } | ||||
private void jsrJmp(Bytecode b) { | private void jsrJmp(Bytecode b) { | ||||
b.addOpcode(JSR); | |||||
b.addOpcode(Opcode.GOTO); | |||||
jsrList.add(new Integer(b.currentPc())); | jsrList.add(new Integer(b.currentPc())); | ||||
b.addIndex(0); | b.addIndex(0); | ||||
} | } | ||||
jsrJmp(b); | jsrJmp(b); | ||||
break; | break; | ||||
case ARETURN : | case ARETURN : | ||||
b.addAstore(var); | |||||
b.addAstore(getVar(1)); | |||||
jsrJmp(b); | jsrJmp(b); | ||||
b.addAload(var); | b.addAload(var); | ||||
break; | break; | ||||
case IRETURN : | case IRETURN : | ||||
b.addIstore(var); | |||||
b.addIstore(getVar(1)); | |||||
jsrJmp(b); | jsrJmp(b); | ||||
b.addIload(var); | b.addIload(var); | ||||
break; | break; | ||||
case LRETURN : | case LRETURN : | ||||
b.addLstore(var); | |||||
b.addLstore(getVar(2)); | |||||
jsrJmp(b); | jsrJmp(b); | ||||
b.addLload(var); | b.addLload(var); | ||||
break; | break; | ||||
case DRETURN : | case DRETURN : | ||||
b.addDstore(var); | |||||
b.addDstore(getVar(2)); | |||||
jsrJmp(b); | jsrJmp(b); | ||||
b.addDload(var); | b.addDload(var); | ||||
break; | break; | ||||
case FRETURN : | case FRETURN : | ||||
b.addFstore(var); | |||||
b.addFstore(getVar(1)); | |||||
jsrJmp(b); | jsrJmp(b); | ||||
b.addFload(var); | b.addFload(var); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
int pcFinally = -1; | |||||
if (finallyBlock != null) { | if (finallyBlock != null) { | ||||
jsrHook.remove(this); | jsrHook.remove(this); | ||||
// catch (any) clause | // catch (any) clause | ||||
bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); | bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); | ||||
bc.growStack(1); | bc.growStack(1); | ||||
bc.addAstore(var); | bc.addAstore(var); | ||||
bc.addOpcode(JSR); | |||||
int pcJsrIndex = bc.currentPc(); | |||||
bc.addIndex(0); // correct later | |||||
bc.addAload(var); | |||||
bc.addOpcode(ATHROW); | |||||
// finally clause | |||||
pcFinally = bc.currentPc(); | |||||
bc.write16bit(pcJsrIndex, pcFinally - pcJsrIndex + 1); | |||||
int retAddr = getMaxLocals(); | |||||
incMaxLocals(1); | |||||
bc.growStack(1); // return address | |||||
bc.addAstore(retAddr); | |||||
hasReturned = false; | hasReturned = false; | ||||
finallyBlock.accept(this); | finallyBlock.accept(this); | ||||
if (!hasReturned) { | if (!hasReturned) { | ||||
bc.addOpcode(RET); | |||||
bc.add(retAddr); | |||||
bc.addAload(var); | |||||
bc.addOpcode(ATHROW); | |||||
} | } | ||||
addFinally(jsrHook.jsrList, finallyBlock); | |||||
} | } | ||||
int pcEnd = bc.currentPc(); | int pcEnd = bc.currentPc(); | ||||
patchGoto(gotoList, pcEnd); | patchGoto(gotoList, pcEnd); | ||||
hasReturned = !tryNotReturn; | |||||
if (finallyBlock != null) { | if (finallyBlock != null) { | ||||
patchGoto(jsrHook.jsrList, pcFinally); | |||||
if (tryNotReturn) { | |||||
bc.addOpcode(JSR); | |||||
bc.addIndex(pcFinally - pcEnd); | |||||
} | |||||
if (tryNotReturn) | |||||
finallyBlock.accept(this); | |||||
} | } | ||||
} | |||||
hasReturned = !tryNotReturn; | |||||
/** | |||||
* Adds a finally clause for earch return statement. | |||||
*/ | |||||
private void addFinally(ArrayList returnList, Stmnt finallyBlock) | |||||
throws CompileError | |||||
{ | |||||
Bytecode bc = bytecode; | |||||
int n = returnList.size(); | |||||
for (int i = 0; i < n; ++i) { | |||||
int pc = ((Integer)returnList.get(i)).intValue(); | |||||
bc.write16bit(pc, bc.currentPc() - pc + 1); | |||||
finallyBlock.accept(this); | |||||
if (!hasReturned) { | |||||
bc.addOpcode(Opcode.GOTO); | |||||
bc.addIndex(pc + 3 - bc.currentPc()); | |||||
} | |||||
} | |||||
} | } | ||||
public void atNewExpr(NewExpr expr) throws CompileError { | public void atNewExpr(NewExpr expr) throws CompileError { | ||||
return cp.addFieldrefInfo(ci, name, type); | return cp.addFieldrefInfo(ci, name, type); | ||||
} | } | ||||
protected void atClassObject2(String cname) throws CompileError { | |||||
super.atClassObject2(cname); | |||||
} | |||||
protected void atFieldPlusPlus(int token, boolean isPost, | protected void atFieldPlusPlus(int token, boolean isPost, | ||||
ASTree oprand, Expr expr, boolean doDup) | ASTree oprand, Expr expr, boolean doDup) | ||||
throws CompileError | throws CompileError |
<br>3. <a href="#load">Class loader</a> | <br>3. <a href="#load">Class loader</a> | ||||
<br>4. <a href="tutorial2.html#intro">Introspection and customization</a> | <br>4. <a href="tutorial2.html#intro">Introspection and customization</a> | ||||
<br>5. <a href="tutorial3.html#intro">Bytecode level API</a> | <br>5. <a href="tutorial3.html#intro">Bytecode level API</a> | ||||
<br>6. <a href="tutorial3.html#generics">Generics</a> | |||||
</ul> | </ul> | ||||
<p><br> | <p><br> |
</ul> | </ul> | ||||
<p><a href="#generics">6. Generics</a> | |||||
<p><br> | <p><br> | ||||
of <code>javassist.bytecode.AnnotationsAttribute</code> class | of <code>javassist.bytecode.AnnotationsAttribute</code> class | ||||
and the <code>javassist.bytecode.annotation</code> package. | and the <code>javassist.bytecode.annotation</code> package. | ||||
<p>Javassist also let you access annotations by the higher-level | |||||
API. | |||||
If you want to access annotations through <code>CtClass</code>, | |||||
call <code>getAnnotations()</code> in <code>CtClass</code> or | |||||
<code>CtBehavior</code>. | |||||
<p><br> | |||||
<h2><a name="generics">6. Generics</a></h2> | |||||
<p>The lower-level API of Javassist fully supports generics | |||||
introduced by Java 5. On the other hand, the higher-level | |||||
API such as <code>CtClass</code> does not directly support | |||||
generics. However, this is not a serious problem for bytecode | |||||
transformation. | |||||
<p>The generics of Java is implemented by the erasure technique. | |||||
After compilation, all type parameters are dropped off. For | |||||
example, suppose that your source code declare a parameterized | |||||
type <code>Vector<String></code>: | |||||
<ul><pre> | |||||
Vector<String> v = new Vector<String>(); | |||||
: | |||||
String s = v.get(0); | |||||
</pre></ul> | |||||
<p>The compiled bytecode is equivalent to the following code: | |||||
<ul><pre> | |||||
Vector v = new Vector(); | |||||
: | |||||
String s = (String)v.get(0); | |||||
</pre></ul> | |||||
<p>So when you write a bytecode transformer, you can just drop | |||||
off all type parameters. For example, if you have a class: | |||||
<ul><pre> | |||||
public class Wrapper<T> { | |||||
T value; | |||||
public Wrapper(T t) { value = t; } | |||||
} | |||||
</pre></ul> | |||||
<p>and want to add an interface <code>Getter<T></code> to the | |||||
class <code>Wrapper<T></code>: | |||||
<ul><pre> | |||||
public interface Getter<T> { | |||||
T get(); | |||||
} | |||||
</pre></ul> | |||||
<p>Then the interface you really have to add is <code>Getter</code> | |||||
(the type parameters <code><T><code> drops off) | |||||
and the method you also have to add to the <code>Wrapper</code> | |||||
class is this simple one: | |||||
<ul><pre> | |||||
public Object get() { return value; } | |||||
</pre></ul> | |||||
<p>Note that no type parameters are necessary. | |||||
<p><br> | <p><br> | ||||