From 664bfcc3db3aa02fb9abfc799de8ff274dd6f1da Mon Sep 17 00:00:00 2001 From: chiba Date: Thu, 1 May 2008 10:47:59 +0000 Subject: [PATCH] fixed JASSIST-47 and 60. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@435 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- Readme.html | 2 +- src/main/javassist/CtBehavior.java | 68 ++++++++ src/main/javassist/CtClass.java | 18 +- .../javassist/bytecode/CodeAttribute.java | 159 ++++++++++++++++-- src/main/javassist/bytecode/Descriptor.java | 37 ++++ .../bytecode/LocalVariableAttribute.java | 16 ++ .../javassist/bytecode/StackMapTable.java | 88 ++++++++++ .../javassist/compiler/MemberResolver.java | 31 +++- 8 files changed, 394 insertions(+), 25 deletions(-) diff --git a/Readme.html b/Readme.html index 84d606ae..b19a0101 100644 --- a/Readme.html +++ b/Readme.html @@ -283,7 +283,7 @@ see javassist.Dump.

-version 3.7.2

-version 3.7.1 on March 10, 2008 diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 6fb9361a..3539fd5f 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -519,6 +519,74 @@ public abstract class CtBehavior extends CtMember { ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); } + /** + * Inserts a new parameter, which becomes the first parameter. + */ + public void insertParameter(CtClass type) + throws CannotCompileException + { + declaringClass.checkModify(); + String desc = methodInfo.getDescriptor(); + String desc2 = Descriptor.insertParameter(type, desc); + try { + addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + + methodInfo.setDescriptor(desc2); + } + + /** + * Appends a new parameter, which becomes the last parameter. + */ + public void addParameter(CtClass type) + throws CannotCompileException + { + declaringClass.checkModify(); + String desc = methodInfo.getDescriptor(); + String desc2 = Descriptor.appendParameter(type, desc); + int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; + try { + addParameter2(offset + Descriptor.paramSize(desc), type, desc); + } + catch (BadBytecode e) { + throw new CannotCompileException(e); + } + + methodInfo.setDescriptor(desc2); + } + + private void addParameter2(int where, CtClass type, String desc) + throws BadBytecode + { + CodeAttribute ca = methodInfo.getCodeAttribute(); + if (ca != null) { + int size = 1; + char typeDesc = 'L'; + int classInfo = 0; + if (type.isPrimitive()) { + CtPrimitiveType cpt = (CtPrimitiveType)type; + size = cpt.getDataSize(); + typeDesc = cpt.getDescriptor(); + } + else + classInfo = methodInfo.getConstPool().addClassInfo(type); + + ca.insertLocalVar(where, size); + LocalVariableAttribute va + = (LocalVariableAttribute) + ca.getAttribute(LocalVariableAttribute.tag); + if (va != null) + va.shiftIndex(where, size); + + StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); + if (smt != null) + smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); + } + } + /** * Modifies the method/constructor body. * diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 46351819..313a7618 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -1216,6 +1216,8 @@ public abstract class CtClass { * object in the current directory. * Once this method is called, further modifications are not * possible any more. + * + * @see #debugWriteFile() */ public void writeFile() throws NotFoundException, IOException, CannotCompileException @@ -1230,6 +1232,7 @@ public abstract class CtClass { * possible any more. * * @param directoryName it must end without a directory separator. + * @see #debugWriteFile(String) */ public void writeFile(String directoryName) throws CannotCompileException, IOException @@ -1263,9 +1266,22 @@ public abstract class CtClass { * This method would be useful for debugging. */ public void debugWriteFile() { + debugWriteFile("."); + } + + /** + * Writes a class file as writeFile() does although this + * method does not prune or freeze the class after writing the class + * file. Note that, once writeFile() or toBytecode() + * is called, it cannot be called again since the class is pruned and frozen. + * This method would be useful for debugging. + * + * @param directoryName it must end without a directory separator. + */ + public void debugWriteFile(String directoryName) { try { boolean p = stopPruning(true); - writeFile(); + writeFile(directoryName); defrost(); stopPruning(p); } diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index 35ab7ddd..f3ae8a1b 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -395,25 +395,154 @@ public class CodeAttribute extends AttributeInfo implements Opcode { newcode[i] = (byte)(index >> 8); newcode[i + 1] = (byte)index; } -} -final class LdcEntry { - LdcEntry next; - int where; - int index; + static class LdcEntry { + LdcEntry next; + int where; + int index; + + static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, + CodeAttribute ca) + throws BadBytecode + { + while (ldc != null) { + int where = ldc.where; + code = CodeIterator.insertGap(code, where, 1, false, etable, ca); + code[where] = (byte)Opcode.LDC_W; + ByteArray.write16bit(ldc.index, code, where + 1); + ldc = ldc.next; + } - static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, - CodeAttribute ca) - throws BadBytecode + return code; + } + } + + /** + * Changes the index numbers of the local variables + * to append a new parameter. + * This method does not update LocalVariableAttribute + * or StackMapTable. These attributes must be + * explicitly updated. + * + * @param where the index of the new parameter. + * @param size the type size of the new parameter (1 or 2). + * + * @see LocalVariableAttribute#shiftIndex(int, int) + * @see StackMapTable#insertLocal(int, int, int) + */ + public void insertLocalVar(int where, int size) throws BadBytecode { + CodeIterator ci = iterator(); + while (ci.hasNext()) + shiftIndex(ci, where, size); + + setMaxLocals(getMaxLocals() + size); + } + + /** + * @param lessThan If the index of the local variable is + * less than this value, it does not change. + * Otherwise, the index is increased. + * @param delta the indexes of the local variables are + * increased by this value. + */ + private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { + int index = ci.next(); + int opcode = ci.byteAt(index); + if (opcode < ILOAD) + return; + else if (opcode < IASTORE) { + if (opcode < ILOAD_0) { + // iload, lload, fload, dload, aload + shiftIndex8(ci, index, opcode, lessThan, delta); + } + else if (opcode < IALOAD) { + // iload_0, ..., aload_3 + shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); + } + else if (opcode < ISTORE) + return; + else if (opcode < ISTORE_0) { + // istore, lstore, ... + shiftIndex8(ci, index, opcode, lessThan, delta); + } + else { + // istore_0, ..., astore_3 + shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); + } + } + else if (opcode == IINC) { + int var = ci.byteAt(index + 1); + if (var < lessThan) + return; + + var += delta; + if (var < 0x100) + ci.writeByte(var, index + 1); + else { + int plus = ci.byteAt(index + 2); + ci.insertExGap(3); + ci.writeByte(WIDE, index); + ci.writeByte(IINC, index + 1); + ci.write16bit(var, index + 2); + ci.write16bit(plus, index + 4); + } + } + else if (opcode == RET) + shiftIndex8(ci, index, opcode, lessThan, delta); + else if (opcode == WIDE) { + int var = ci.u16bitAt(index + 2); + if (var < lessThan) + return; + + var += delta; + ci.write16bit(var, index + 2); + } + } + + private static void shiftIndex8(CodeIterator ci, int index, int opcode, + int lessThan, int delta) + throws BadBytecode { - while (ldc != null) { - int where = ldc.where; - code = CodeIterator.insertGap(code, where, 1, false, etable, ca); - code[where] = (byte)Opcode.LDC_W; - ByteArray.write16bit(ldc.index, code, where + 1); - ldc = ldc.next; + int var = ci.byteAt(index + 1); + if (var < lessThan) + return; + + var += delta; + if (var < 0x100) + ci.writeByte(var, index + 1); + else { + ci.insertExGap(2); + ci.writeByte(WIDE, index); + ci.writeByte(opcode, index + 1); + ci.write16bit(var, index + 2); } + } - return code; + private static void shiftIndex0(CodeIterator ci, int index, int opcode, + int lessThan, int delta, + int opcode_i_0, int opcode_i) + throws BadBytecode + { + int var = (opcode - opcode_i_0) % 4; + if (var < lessThan) + return; + + var += delta; + if (var < 4) + ci.writeByte(opcode + delta, index); + else { + opcode = (opcode - opcode_i_0) / 4 + opcode_i; + if (var < 0x100) { + ci.insertExGap(1); + ci.writeByte(opcode, index); + ci.writeByte(var, index + 1); + } + else { + ci.insertExGap(3); + ci.writeByte(WIDE, index); + ci.writeByte(opcode, index + 1); + ci.write16bit(var, index + 2); + } + } } } diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java index d40f2380..d9db92ba 100644 --- a/src/main/javassist/bytecode/Descriptor.java +++ b/src/main/javassist/bytecode/Descriptor.java @@ -352,6 +352,43 @@ public class Descriptor { + desc.substring(1); } + /** + * Appends a parameter type to the parameter list represented + * by the given descriptor. The appended parameter becomes + * the last parameter. + * + * @param type the type of the appended parameter. + * @param desc descriptor + */ + public static String appendParameter(CtClass type, String descriptor) { + int i = descriptor.indexOf(')'); + if (i < 0) + return descriptor; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(descriptor.substring(0, i)); + toDescriptor(newdesc, type); + newdesc.append(descriptor.substring(i)); + return newdesc.toString(); + } + } + + /** + * Inserts a parameter type at the beginning of the parameter + * list represented + * by the given descriptor. + * + * @param type the type of the inserted parameter. + * @param descriptor the descriptor of the method. + */ + public static String insertParameter(CtClass type, + String descriptor) { + if (descriptor.charAt(0) != '(') + return descriptor; + else + return "(" + of(type) + descriptor.substring(1); + } + /** * Changes the return type included in the given descriptor. * diff --git a/src/main/javassist/bytecode/LocalVariableAttribute.java b/src/main/javassist/bytecode/LocalVariableAttribute.java index 54929137..80f96533 100644 --- a/src/main/javassist/bytecode/LocalVariableAttribute.java +++ b/src/main/javassist/bytecode/LocalVariableAttribute.java @@ -91,6 +91,22 @@ public class LocalVariableAttribute extends AttributeInfo { info = newInfo; } + /** + * For each local_variable_table[i].index, + * this method increases index by delta. + * + * @param lessThan the index does not change if it + * is less than this value. + */ + public void shiftIndex(int lessThan, int delta) { + int size = info.length; + for (int i = 2; i < size; i += 10){ + int org = ByteArray.readU16bit(info, i + 8); + if (org >= lessThan) + ByteArray.write16bit(org + delta, info, i + 8); + } + } + /** * Returns local_variable_table_length. * This represents the number of entries in the table. diff --git a/src/main/javassist/bytecode/StackMapTable.java b/src/main/javassist/bytecode/StackMapTable.java index f1704dc4..2ca8bd72 100644 --- a/src/main/javassist/bytecode/StackMapTable.java +++ b/src/main/javassist/bytecode/StackMapTable.java @@ -424,6 +424,94 @@ public class StackMapTable extends AttributeInfo { } } + /** + * Updates this stack map table when a new local variable is added. + * + * @param index the index of the added local variable. + * @param tag the type tag of that local variable. + * @param classInfo the index of the CONSTANT_Class_info structure + * in a constant pool table. This should be zero unless the tag + * is ITEM_Object. + * + * @see #typeTagOf(char) + * @see ConstPool + */ + public void insertLocal(int index, int tag, int classInfo) + throws BadBytecode + { + byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit(); + this.set(data); + } + + /** + * Returns the tag of the type specified by the + * descriptor. This method returns INTEGER + * unless the descriptor is either D (double), F (float), + * J (long), L (class type), or [ (array). + * + * @param descriptor the type descriptor. + * @see Descriptor + */ + public static int typeTagOf(char descriptor) { + switch (descriptor) { + case 'D' : + return DOUBLE; + case 'F' : + return FLOAT; + case 'J' : + return LONG; + case 'L' : + case '[' : + return OBJECT; + // case 'V' : + default : + return INTEGER; + } + } + + static class InsertLocal extends SimpleCopy { + private int varIndex; + private int varTag, varData; + + public InsertLocal(byte[] data, int varIndex, int varTag, int varData) { + super(data); + this.varIndex = varIndex; + this.varTag = varTag; + this.varData = varData; + } + + public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, + int[] stackTags, int[] stackData) { + int len = localTags.length; + if (len < varIndex) { + super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); + return; + } + + int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1; + int[] localTags2 = new int[len + typeSize]; + int[] localData2 = new int[len + typeSize]; + int index = varIndex; + int j = 0; + for (int i = 0; i < len; i++) { + if (j == index) + j += typeSize; + + localTags2[j] = localTags[i]; + localData2[j++] = localData[i]; + } + + localTags2[index] = varTag; + localData2[index] = varData; + if (typeSize > 1) { + localTags2[index + 1] = TOP; + localData2[index + 1] = 0; + } + + super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData); + } + } + /** * A writer of stack map tables. */ diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java index d263e1b0..858a235c 100644 --- a/src/main/javassist/compiler/MemberResolver.java +++ b/src/main/javassist/compiler/MemberResolver.java @@ -129,19 +129,23 @@ public class MemberResolver implements TokenId { else onlyExact = maybe != null; + int mod = clazz.getModifiers(); + boolean isIntf = Modifier.isInterface(mod); try { - CtClass pclazz = clazz.getSuperclass(); - if (pclazz != null) { - Method r = lookupMethod(pclazz, methodName, argTypes, - argDims, argClassNames, onlyExact); - if (r != null) - return r; + // skip searching java.lang.Object if clazz is an interface type. + if (!isIntf) { + CtClass pclazz = clazz.getSuperclass(); + if (pclazz != null) { + Method r = lookupMethod(pclazz, methodName, argTypes, + argDims, argClassNames, onlyExact); + if (r != null) + return r; + } } } catch (NotFoundException e) {} - int mod = clazz.getModifiers(); - if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) + if (isIntf || Modifier.isAbstract(mod)) try { CtClass[] ifs = clazz.getInterfaces(); int size = ifs.length; @@ -152,6 +156,17 @@ public class MemberResolver implements TokenId { if (r != null) return r; } + + if (isIntf) { + // finally search java.lang.Object. + CtClass pclazz = clazz.getSuperclass(); + if (pclazz != null) { + Method r = lookupMethod(pclazz, methodName, argTypes, + argDims, argClassNames, onlyExact); + if (r != null) + return r; + } + } } catch (NotFoundException e) {} -- 2.39.5