map.put(srcSuperName, destSuperName);
}
+ // a stack map table is copied from srcInfo.
methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
if (isCons && patch)
methodInfo.setSuperclass(destSuperName);
String delegateObj, String delegateMethod)
throws CannotCompileException
{
- declaringClass.checkModify();
+ CtClass cc = declaringClass;
+ cc.checkModify();
try {
- Javac jv = new Javac(declaringClass);
+ Javac jv = new Javac(cc);
if (delegateMethod != null)
jv.recordProceed(delegateObj, delegateMethod);
methodInfo.setCodeAttribute(b.toCodeAttribute());
methodInfo.setAccessFlags(methodInfo.getAccessFlags()
& ~AccessFlag.ABSTRACT);
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (CompileError e) {
throw new CannotCompileException(e);
+ } catch (BadBytecode e) {
+ throw new CannotCompileException(e);
}
}
ConstPool cp = destInfo.getConstPool();
CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
destInfo.setCodeAttribute(ca);
+ // a stack map table is copied to destInfo.
}
}
catch (CodeAttribute.RuntimeCopyException e) {
CtField field = new CtField(type, fname, cc);
field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
cc.addField(field, CtField.Initializer.byNew(type));
- insertBefore(fname + ".enter();");
+ insertBefore(fname + ".enter();", false);
String src = fname + ".exit();";
insertAfter(src, true);
}
* @see CtConstructor#insertBeforeBody(String)
*/
public void insertBefore(String src) throws CannotCompileException {
- declaringClass.checkModify();
+ insertBefore(src, true);
+ }
+
+ private void insertBefore(String src, boolean rebuild)
+ throws CannotCompileException
+ {
+ CtClass cc = declaringClass;
+ cc.checkModify();
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
throw new CannotCompileException("no method body");
CodeIterator iterator = ca.iterator();
- Javac jv = new Javac(declaringClass);
+ Javac jv = new Javac(cc);
try {
int nvars = jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
int pos = iterator.insertEx(b.get());
iterator.insert(b.getExceptionTable(), pos);
+ if (rebuild)
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
public void insertAfter(String src, boolean asFinally)
throws CannotCompileException
{
- declaringClass.checkModify();
+ CtClass cc = declaringClass;
+ cc.checkModify();
ConstPool pool = methodInfo.getConstPool();
CodeAttribute ca = methodInfo.getCodeAttribute();
if (ca == null)
int retAddr = ca.getMaxLocals();
Bytecode b = new Bytecode(pool, 0, retAddr + 1);
b.setStackDepth(ca.getMaxStack() + 1);
- Javac jv = new Javac(b, declaringClass);
+ Javac jv = new Javac(b, cc);
try {
int nvars = jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
subr = iterator.getCodeLength() - gapLen;
}
}
+
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
String exceptionName)
throws CannotCompileException
{
- declaringClass.checkModify();
+ CtClass cc = declaringClass;
+ cc.checkModify();
ConstPool cp = methodInfo.getConstPool();
CodeAttribute ca = methodInfo.getCodeAttribute();
CodeIterator iterator = ca.iterator();
Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
b.setStackDepth(1);
- Javac jv = new Javac(b, declaringClass);
+ Javac jv = new Javac(b, cc);
try {
jv.recordParams(getParameterTypes(),
Modifier.isStatic(getModifiers()));
ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
cp.addClassInfo(exceptionType));
iterator.append(b.getExceptionTable(), pos);
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
catch (CompileError e) {
throw new CannotCompileException(e);
+ } catch (BadBytecode e) {
+ throw new CannotCompileException(e);
}
}
if (!modify)
return lineNum;
- declaringClass.checkModify();
+ CtClass cc = declaringClass;
+ cc.checkModify();
CodeIterator iterator = ca.iterator();
- Javac jv = new Javac(declaringClass);
+ Javac jv = new Javac(cc);
try {
jv.recordLocalVariables(ca, index);
jv.recordParams(getParameterTypes(),
iterator.insert(index, b.get());
iterator.insert(b.getExceptionTable(), index);
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
return lineNum;
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
}
}
+
+ try {
+ m.rebuildStackMapIf6(classPool, cf);
+ }
+ catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
}
private void modifyConstructors(ClassFile cf)
classPool);
int stacksize = makeFieldInitializer(init, params);
insertAuxInitializer(codeAttr, init, stacksize);
+ minfo.rebuildStackMapIf6(classPool, cf);
}
catch (BadBytecode e) {
throw new CannotCompileException(e);
* must be added to a class with <code>CtClass.addConstructor()</code>.
*
* <p>The created constructor does not include a constructor body,
- * must be specified with <code>setBody()</code>.
+ * which must be specified with <code>setBody()</code>.
*
* @param declaring the class to which the created method is added.
* @param parameters a list of the parameter types
* It must be a single statement or block.
*/
public void insertBeforeBody(String src) throws CannotCompileException {
- declaringClass.checkModify();
+ CtClass cc = declaringClass;
+ cc.checkModify();
if (isClassInitializer())
throw new CannotCompileException("class initializer");
Bytecode b = new Bytecode(methodInfo.getConstPool(),
ca.getMaxStack(), ca.getMaxLocals());
b.setStackDepth(ca.getMaxStack());
- Javac jv = new Javac(b, declaringClass);
+ Javac jv = new Javac(b, cc);
try {
jv.recordParams(getParameterTypes(), false);
jv.compileStmnt(src);
iterator.skipConstructor();
int pos = iterator.insertEx(b.get());
iterator.insert(b.getExceptionTable(), pos);
+ methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
}
catch (NotFoundException e) {
throw new CannotCompileException(e);
if (isConstructor()) {
MethodInfo minfo = method.getMethodInfo2();
CodeAttribute ca = minfo.getCodeAttribute();
- if (ca != null)
+ if (ca != null) {
removeConsCall(ca);
+ try {
+ methodInfo.rebuildStackMapIf6(declaring.getClassPool(),
+ declaring.getClassFile2());
+ }
+ catch (BadBytecode e) {
+ throw new CannotCompileException(e);
+ }
+ }
}
method.setName(name);
methodInfo.setCodeAttribute(cattr);
methodInfo.setAccessFlags(methodInfo.getAccessFlags()
& ~AccessFlag.ABSTRACT);
+ // rebuilding a stack map table is not needed.
}
// inner classes
Javac compiler = new Javac(declaring);
try {
CtMember obj = compiler.compile(src);
- if (obj instanceof CtConstructor)
+ if (obj instanceof CtConstructor) {
+ // a stack map table has been already created.
return (CtConstructor)obj;
+ }
}
catch (CompileError e) {
throw new CannotCompileException(e);
code.add(Bytecode.RETURN);
+ // no need to construct a stack map table.
cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
return cons;
}
return code;
}
+ /* The generated method body does not need a stack map table
+ * because it does not contain a branch instruction.
+ */
protected static int makeBody0(CtClass clazz, ClassFile classfile,
CtMethod wrappedBody,
boolean isStatic, CtClass[] parameters,
map);
int acc = body.getAccessFlags();
body.setAccessFlags(AccessFlag.setPrivate(acc));
+ // a stack map is copied. rebuilding it is not needed.
classfile.addMethod(body);
bodies.put(src, bodyname);
}
String[] cachedInterfaces;
String cachedSuperclass;
+ /**
+ * The major version number of class files
+ * for JDK 1.3.
+ */
+ public static final int JAVA_3 = 47;
+
+ /**
+ * The major version number of class files
+ * for JDK 1.5.
+ */
+ public static final int JAVA_5 = 49;
+
+ /**
+ * The major version number of class files
+ * for JDK 1.6.
+ */
+ public static final int JAVA_6 = 50;
+
/**
* The major version number of class files created
* from scratch. The value is 47 (JDK 1.3).
*/
- public static final int MAJOR_VERSION = 47;
+ public static final int MAJOR_VERSION = JAVA_3;
/**
* Constructs a class file from a byte stream.
/**
* Appends a method to the class.
+ * If there is a bridge method with the same name and signature,
+ * then the bridge method is removed before a new method is added.
*
* @throws DuplicateMemberException when the method is already included.
*/
String name = newMinfo.getName();
String descriptor = newMinfo.getDescriptor();
ListIterator it = methods.listIterator(0);
- while (it.hasNext()) {
- MethodInfo minfo = (MethodInfo)it.next();
- if (minfo.getName().equals(name)
- && notBridgeMethod(minfo) && notBridgeMethod(newMinfo)
- && Descriptor.eqParamTypes(minfo.getDescriptor(),
- descriptor))
+ while (it.hasNext())
+ if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it))
throw new DuplicateMemberException("duplicate method: " + name
- + " in " + this.getName());
+ + " in " + this.getName());
+ }
+
+ private static boolean isDuplicated(MethodInfo newMethod, String newName,
+ String newDesc, MethodInfo minfo,
+ ListIterator it)
+ {
+ if (!minfo.getName().equals(newName))
+ return false;
+
+ String desc = minfo.getDescriptor();
+ if (!Descriptor.eqParamTypes(desc, newDesc))
+ return false;
+
+ if (desc.equals(newDesc)) {
+ if (notBridgeMethod(minfo))
+ return true;
+ else {
+ it.remove();
+ return false;
+ }
}
+ else
+ return notBridgeMethod(minfo) && notBridgeMethod(newMethod);
}
/* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed.
*/
- private boolean notBridgeMethod(MethodInfo minfo) {
+ private static boolean notBridgeMethod(MethodInfo minfo) {
return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0;
}
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import javassist.ClassPool;
+import javassist.bytecode.stackmap.MapMaker;
/**
* <code>method_info</code> structure.
attribute.add(cattr);
}
+ /**
+ * Rebuilds a stack map table if the class file is for Java 6
+ * or later. Java 5 or older Java VMs do not recognize a stack
+ * map table.
+ *
+ * @param pool used for making type hierarchy.
+ * @param cf rebuild if this class file is for Java 6 or later.
+ * @see #rebuildStackMap(ClassPool)
+ * @since 3.6
+ */
+ public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
+ throws BadBytecode
+ {
+ if (cf.getMajorVersion() >= ClassFile.JAVA_6)
+ rebuildStackMap(pool);
+ }
+
+ /**
+ * Rebuilds a stack map table. If no stack map table is included,
+ * a new one is created. If this <code>MethodInfo</code> does not
+ * include a code attribute, nothing happens.
+ *
+ * @param pool used for making type hierarchy.
+ * @see StackMapTable
+ * @since 3.6
+ */
+ public void rebuildStackMap(ClassPool pool) throws BadBytecode {
+ CodeAttribute ca = getCodeAttribute();
+ if (ca != null) {
+ StackMapTable smt = MapMaker.make(pool, this);
+ ca.setAttribute(smt);
+ }
+ }
+
/**
* Returns the line number of the source line corresponding to the specified
* bytecode contained in this method.
*/
protected static abstract class ReturnHook {
ReturnHook next;
- protected abstract void doit(Bytecode b, int opcode);
+
+ /**
+ * Returns true if the generated code ends with return,
+ * throw, or goto.
+ */
+ protected abstract boolean doit(Bytecode b, int opcode);
+
protected ReturnHook(CodeGen gen) {
next = gen.returnHooks;
gen.returnHooks = this;
}
for (ReturnHook har = returnHooks; har != null; har = har.next)
- har.doit(bytecode, op);
+ if (har.doit(bytecode, op)) {
+ hasReturned = true;
+ return;
+ }
bytecode.addOpcode(op);
hasReturned = true;
bc.addOpcode(MONITORENTER);
ReturnHook rh = new ReturnHook(this) {
- protected void doit(Bytecode b, int opcode) {
+ protected boolean doit(Bytecode b, int opcode) {
b.addAload(var);
b.addOpcode(MONITOREXIT);
+ return false;
}
};
bc.addIndex(0);
}
- int pc4 = bc.currentPc();
- rh.doit(bc, 0); // the 2nd arg is ignored.
- bc.addOpcode(ATHROW);
- bc.addExceptionHandler(pc, pc2, pc4, 0);
+ if (pc < pc2) { // if the body is not empty
+ int pc4 = bc.currentPc();
+ rh.doit(bc, 0); // the 2nd arg is ignored.
+ bc.addOpcode(ATHROW);
+ bc.addExceptionHandler(pc, pc2, pc4, 0);
+ }
+
if (!hasReturned)
bc.write16bit(pc3, bc.currentPc() - pc3 + 1);
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
+import javassist.bytecode.BadBytecode;
import javassist.bytecode.Opcode;
import javassist.NotFoundException;
try {
if (mem instanceof FieldDecl)
return compileField((FieldDecl)mem);
- else
- return compileMethod(p, (MethodDecl)mem);
+ else {
+ CtBehavior cb = compileMethod(p, (MethodDecl)mem);
+ CtClass decl = cb.getDeclaringClass();
+ cb.getMethodInfo2()
+ .rebuildStackMapIf6(decl.getClassPool(),
+ decl.getClassFile2());
+ return cb;
+ }
+ }
+ catch (BadBytecode bb) {
+ throw new CompileError(bb.getMessage());
}
catch (CannotCompileException e) {
throw new CompileError(e.getMessage());
return f;
}
- private CtMember compileMethod(Parser p, MethodDecl md)
+ private CtBehavior compileMethod(Parser p, MethodDecl md)
throws CompileError
{
int mod = MemberResolver.getModifiers(md.getModifiers());
/* Code generator methods depending on javassist.* classes.
*/
public class MemberCodeGen extends CodeGen {
- public static final int JAVA5_VER = 49;
-
protected MemberResolver resolver;
protected CtClass thisClass;
protected MethodInfo thisMethod;
private void jsrJmp(Bytecode b) {
b.addOpcode(Opcode.GOTO);
- jsrList.add(new Integer(b.currentPc()));
+ jsrList.add(new int[] {b.currentPc(), var});
b.addIndex(0);
}
- protected void doit(Bytecode b, int opcode) {
+ protected boolean doit(Bytecode b, int opcode) {
switch (opcode) {
case Opcode.RETURN :
jsrJmp(b);
default :
throw new RuntimeException("fatal");
}
+
+ return false;
+ }
+ }
+
+ static class JsrHook2 extends ReturnHook {
+ int var;
+ int target;
+
+ JsrHook2(CodeGen gen, int[] retTarget) {
+ super(gen);
+ target = retTarget[0];
+ var = retTarget[1];
+ }
+
+ protected boolean doit(Bytecode b, int opcode) {
+ switch (opcode) {
+ case Opcode.RETURN :
+ break;
+ case ARETURN :
+ b.addAstore(var);
+ break;
+ case IRETURN :
+ b.addIstore(var);
+ break;
+ case LRETURN :
+ b.addLstore(var);
+ break;
+ case DRETURN :
+ b.addDstore(var);
+ break;
+ case FRETURN :
+ b.addFstore(var);
+ break;
+ default :
+ throw new RuntimeException("fatal");
+ }
+
+ b.addOpcode(Opcode.GOTO);
+ b.addIndex(target - b.currentPc() + 3);
+ return true;
}
}
Bytecode bc = bytecode;
int n = returnList.size();
for (int i = 0; i < n; ++i) {
- int pc = ((Integer)returnList.get(i)).intValue();
+ final int[] ret = (int[])returnList.get(i);
+ int pc = ret[0];
bc.write16bit(pc, bc.currentPc() - pc + 1);
+ ReturnHook hook = new JsrHook2(this, ret);
finallyBlock.accept(this);
+ hook.remove(this);
if (!hasReturned) {
bc.addOpcode(Opcode.GOTO);
bc.addIndex(pc + 3 - bc.currentPc());
}
protected void atClassObject2(String cname) throws CompileError {
- if (getMajorVersion() < JAVA5_VER)
+ if (getMajorVersion() < ClassFile.JAVA_5)
super.atClassObject2(cname);
else
bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
}
}
- // codeAttr might be modified by other partys
+ // codeAttr might be modified by other partiess
// so I check the current value of max-locals.
if (codeAttr.getMaxLocals() < context.maxLocals)
codeAttr.setMaxLocals(context.maxLocals);
codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack);
+ try {
+ if (edited)
+ minfo.rebuildStackMapIf6(clazz.getClassPool(),
+ clazz.getClassFile2());
+ }
+ catch (BadBytecode b) {
+ throw new CannotCompileException(b.getMessage(), b);
+ }
+
return edited;
}