From b8445cabacd7ab2a9c04bc5936583060f35ef505 Mon Sep 17 00:00:00 2001 From: chiba Date: Sat, 2 Jun 2007 14:12:36 +0000 Subject: [PATCH] fixed bugs related to stack map tables. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@378 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/CtBehavior.java | 46 +++++++++++---- src/main/javassist/CtClassType.java | 8 +++ src/main/javassist/CtConstructor.java | 18 ++++-- src/main/javassist/CtMethod.java | 1 + src/main/javassist/CtNewConstructor.java | 5 +- src/main/javassist/CtNewWrappedMethod.java | 4 ++ src/main/javassist/bytecode/ClassFile.java | 56 ++++++++++++++++--- src/main/javassist/bytecode/MethodInfo.java | 36 ++++++++++++ src/main/javassist/compiler/CodeGen.java | 27 ++++++--- src/main/javassist/compiler/Javac.java | 16 +++++- .../javassist/compiler/MemberCodeGen.java | 54 ++++++++++++++++-- src/main/javassist/expr/ExprEditor.java | 11 +++- 12 files changed, 240 insertions(+), 42 deletions(-) diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 249c0eae..f5e27ff4 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -62,6 +62,7 @@ public abstract class CtBehavior extends CtMember { 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); @@ -349,9 +350,10 @@ public abstract class CtBehavior extends CtMember { 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); @@ -359,9 +361,12 @@ public abstract class CtBehavior extends CtMember { 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); } } @@ -380,6 +385,7 @@ public abstract class CtBehavior extends CtMember { 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) { @@ -466,7 +472,7 @@ public abstract class CtBehavior extends CtMember { 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); } @@ -561,13 +567,20 @@ public abstract class CtBehavior extends CtMember { * @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())); @@ -586,6 +599,8 @@ public abstract class CtBehavior extends CtMember { 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); @@ -627,7 +642,8 @@ public abstract class CtBehavior extends CtMember { 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) @@ -637,7 +653,7 @@ public abstract class CtBehavior extends CtMember { 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())); @@ -677,6 +693,8 @@ public abstract class CtBehavior extends CtMember { subr = iterator.getCodeLength() - gapLen; } } + + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); @@ -855,13 +873,14 @@ public abstract class CtBehavior extends CtMember { 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())); @@ -883,12 +902,15 @@ public abstract class CtBehavior extends CtMember { 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); } } @@ -961,9 +983,10 @@ public abstract class CtBehavior extends CtMember { 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(), @@ -983,6 +1006,7 @@ public abstract class CtBehavior extends CtMember { iterator.insert(index, b.get()); iterator.insert(b.getExceptionTable(), index); + methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); return lineNum; } catch (NotFoundException e) { diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index d9146d0a..ce20578f 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -1350,6 +1350,13 @@ class CtClassType extends CtClass { throw new CannotCompileException(e); } } + + try { + m.rebuildStackMapIf6(classPool, cf); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } } private void modifyConstructors(ClassFile cf) @@ -1375,6 +1382,7 @@ class CtClassType extends CtClass { classPool); int stacksize = makeFieldInitializer(init, params); insertAuxInitializer(codeAttr, init, stacksize); + minfo.rebuildStackMapIf6(classPool, cf); } catch (BadBytecode e) { throw new CannotCompileException(e); diff --git a/src/main/javassist/CtConstructor.java b/src/main/javassist/CtConstructor.java index a2868fc7..4a14bc60 100644 --- a/src/main/javassist/CtConstructor.java +++ b/src/main/javassist/CtConstructor.java @@ -44,7 +44,7 @@ public final class CtConstructor extends CtBehavior { * must be added to a class with CtClass.addConstructor(). * *

The created constructor does not include a constructor body, - * must be specified with setBody(). + * which must be specified with setBody(). * * @param declaring the class to which the created method is added. * @param parameters a list of the parameter types @@ -244,7 +244,8 @@ public final class CtConstructor extends CtBehavior { * 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"); @@ -253,7 +254,7 @@ public final class CtConstructor extends CtBehavior { 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); @@ -262,6 +263,7 @@ public final class CtConstructor extends CtBehavior { 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); @@ -346,8 +348,16 @@ public final class CtConstructor extends CtBehavior { 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); diff --git a/src/main/javassist/CtMethod.java b/src/main/javassist/CtMethod.java index a2286645..a6015dba 100644 --- a/src/main/javassist/CtMethod.java +++ b/src/main/javassist/CtMethod.java @@ -262,6 +262,7 @@ public final class CtMethod extends CtBehavior { methodInfo.setCodeAttribute(cattr); methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); + // rebuilding a stack map table is not needed. } // inner classes diff --git a/src/main/javassist/CtNewConstructor.java b/src/main/javassist/CtNewConstructor.java index ac3d12b2..2ac2f7ca 100644 --- a/src/main/javassist/CtNewConstructor.java +++ b/src/main/javassist/CtNewConstructor.java @@ -67,8 +67,10 @@ public class CtNewConstructor { 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); @@ -145,6 +147,7 @@ public class CtNewConstructor { code.add(Bytecode.RETURN); + // no need to construct a stack map table. cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute()); return cons; } diff --git a/src/main/javassist/CtNewWrappedMethod.java b/src/main/javassist/CtNewWrappedMethod.java index 88e680eb..5e48749d 100644 --- a/src/main/javassist/CtNewWrappedMethod.java +++ b/src/main/javassist/CtNewWrappedMethod.java @@ -63,6 +63,9 @@ class CtNewWrappedMethod { 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, @@ -146,6 +149,7 @@ class CtNewWrappedMethod { 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); } diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index 3d743d7b..73cdc6ec 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -45,11 +45,29 @@ public final class ClassFile { 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. @@ -540,6 +558,8 @@ public final class ClassFile { /** * 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. */ @@ -558,20 +578,38 @@ public final class ClassFile { 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; } diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index 8d0cce10..6e945570 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Map; +import javassist.ClassPool; +import javassist.bytecode.stackmap.MapMaker; /** * method_info structure. @@ -370,6 +372,40 @@ public final class MethodInfo { 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 MethodInfo 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. diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index dfcddc4e..0712d906 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -53,7 +53,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { */ 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; @@ -607,7 +613,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { } 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; @@ -645,9 +654,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { 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; } }; @@ -665,10 +675,13 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { 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); diff --git a/src/main/javassist/compiler/Javac.java b/src/main/javassist/compiler/Javac.java index d32fddcf..b3d275b3 100644 --- a/src/main/javassist/compiler/Javac.java +++ b/src/main/javassist/compiler/Javac.java @@ -27,6 +27,7 @@ import javassist.Modifier; import javassist.bytecode.Bytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.LocalVariableAttribute; +import javassist.bytecode.BadBytecode; import javassist.bytecode.Opcode; import javassist.NotFoundException; @@ -89,8 +90,17 @@ public class Javac { 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()); @@ -128,7 +138,7 @@ public class Javac { return f; } - private CtMember compileMethod(Parser p, MethodDecl md) + private CtBehavior compileMethod(Parser p, MethodDecl md) throws CompileError { int mod = MemberResolver.getModifiers(md.getModifiers()); diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 3d7ed373..71849957 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -24,8 +24,6 @@ import java.util.ArrayList; /* 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; @@ -106,11 +104,11 @@ public class MemberCodeGen extends CodeGen { 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); @@ -143,6 +141,47 @@ public class MemberCodeGen extends CodeGen { 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; } } @@ -236,9 +275,12 @@ public class MemberCodeGen extends CodeGen { 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()); @@ -923,7 +965,7 @@ public class MemberCodeGen extends CodeGen { } 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)); diff --git a/src/main/javassist/expr/ExprEditor.java b/src/main/javassist/expr/ExprEditor.java index 9bba13b7..db9cb437 100644 --- a/src/main/javassist/expr/ExprEditor.java +++ b/src/main/javassist/expr/ExprEditor.java @@ -101,12 +101,21 @@ public class ExprEditor { } } - // 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; } -- 2.39.5