diff options
author | jgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2008-05-24 05:13:20 +0000 |
---|---|---|
committer | jgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2008-05-24 05:13:20 +0000 |
commit | 2bae8b13291b8ef8fc3df24af627424f344581df (patch) | |
tree | 7d68840873257ab78ae69cc9ed9570adc18bc2d8 /src/main/javassist/convert | |
parent | e48604525140c746404a637282cb7b1dd97c18c4 (diff) | |
download | javassist-2bae8b13291b8ef8fc3df24af627424f344581df.tar.gz javassist-2bae8b13291b8ef8fc3df24af627424f344581df.zip |
Fix subtypeOf in CtArray
Introduce full data-flow analysis API
Fix AALOAD by using data-flow analysis to determine the type
Introduce a testsuite to the project
Add a framedump toolp
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@437 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'src/main/javassist/convert')
-rw-r--r-- | src/main/javassist/convert/TransformAccessArrayField.java | 379 | ||||
-rw-r--r-- | src/main/javassist/convert/Transformer.java | 13 |
2 files changed, 242 insertions, 150 deletions
diff --git a/src/main/javassist/convert/TransformAccessArrayField.java b/src/main/javassist/convert/TransformAccessArrayField.java index d3036056..1af424ed 100644 --- a/src/main/javassist/convert/TransformAccessArrayField.java +++ b/src/main/javassist/convert/TransformAccessArrayField.java @@ -14,168 +14,251 @@ */ package javassist.convert; +import javassist.CannotCompileException; import javassist.CtClass; import javassist.NotFoundException; import javassist.CodeConverter.ArrayAccessReplacementMethodNames; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.analysis.Analyzer; +import javassist.bytecode.analysis.Frame; /** - * + * A transformer which replaces array access with static method invocations. + * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> - * @version $Revision: 1.4 $ + * @author Jason T. Greene + * @version $Revision: 1.5 $ */ -public class TransformAccessArrayField extends Transformer { -// CtClass componentType; - - String methodClassname; - ArrayAccessReplacementMethodNames names; - - public TransformAccessArrayField(Transformer next, String methodClassname, - ArrayAccessReplacementMethodNames names) - throws NotFoundException - { - super(next); - this.methodClassname = methodClassname; - this.names = names; - } - - public int transform(CtClass tclazz, int pos, CodeIterator iterator, - ConstPool cp) throws BadBytecode - { - int c = iterator.byteAt(pos); - - if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD - || c == FALOAD || c == IALOAD || c == LALOAD || c == SALOAD) - replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); - else if (c == AASTORE || c == BASTORE || c == CASTORE || c == DASTORE - || c == FASTORE || c == IASTORE || c == LASTORE || c == SASTORE) - replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); - - return pos; - } - - private void replace(ConstPool cp, CodeIterator iterator, - int pos, int opcode, String signature) - throws BadBytecode - { - String methodName = getMethodName(opcode); - if (methodName != null) { - iterator.insertGap(2); - int mi = cp.addClassInfo(methodClassname); - int methodref = cp.addMethodrefInfo(mi, methodName, signature); - iterator.writeByte(INVOKESTATIC, pos); - iterator.write16bit(methodref, pos + 1); - } - } - - private String getMethodName(int opcode) { - String methodName = null; - switch (opcode) { - case AALOAD: - methodName = names.objectRead(); - break; - case BALOAD: - methodName = names.byteOrBooleanRead(); - break; - case CALOAD: - methodName = names.charRead(); - break; - case DALOAD: - methodName = names.doubleRead(); - break; - case FALOAD: - methodName = names.floatRead(); - break; - case IALOAD: - methodName = names.intRead(); - break; - case SALOAD: - methodName = names.shortRead(); - break; - case LALOAD: - methodName = names.longRead(); - break; - case AASTORE: - methodName = names.objectWrite(); - break; - case BASTORE: - methodName = names.byteOrBooleanWrite(); - break; - case CASTORE: - methodName = names.charWrite(); - break; - case DASTORE: - methodName = names.doubleWrite(); - break; - case FASTORE: - methodName = names.floatWrite(); - break; - case IASTORE: - methodName = names.intWrite(); - break; - case SASTORE: - methodName = names.shortWrite(); - break; - case LASTORE: - methodName = names.longWrite(); - break; - } +public final class TransformAccessArrayField extends Transformer { + private final String methodClassname; + private final ArrayAccessReplacementMethodNames names; + private Frame[] frames; + private int offset; - if (methodName.equals("")) - methodName = null; + public TransformAccessArrayField(Transformer next, String methodClassname, + ArrayAccessReplacementMethodNames names) throws NotFoundException { + super(next); + this.methodClassname = methodClassname; + this.names = names; - return methodName; - } + } - private String getLoadReplacementSignature(int opcode) - throws BadBytecode - { - switch (opcode) { - case AALOAD: - return "(Ljava/lang/Object;I)Ljava/lang/Object;"; - case BALOAD: - return "(Ljava/lang/Object;I)B"; - case CALOAD: - return "(Ljava/lang/Object;I)C"; - case DALOAD: - return "(Ljava/lang/Object;I)D"; - case FALOAD: - return "(Ljava/lang/Object;I)F"; - case IALOAD: - return "(Ljava/lang/Object;I)I"; - case SALOAD: - return "(Ljava/lang/Object;I)S"; - case LALOAD: - return "(Ljava/lang/Object;I)J"; - } + public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { + /* + * This transformer must be isolated from other transformers, since some + * of them affect the local variable and stack maximums without updating + * the code attribute to reflect the changes. This screws up the + * data-flow analyzer, since it relies on consistent code state. Even + * if the attribute values were updated correctly, we would have to + * detect it, and redo analysis, which is not cheap. Instead, we are + * better off doing all changes in initialize() before everyone else has + * a chance to muck things up. + */ + CodeIterator iterator = minfo.getCodeAttribute().iterator(); + while (iterator.hasNext()) { + try { + int pos = iterator.next(); + int c = iterator.byteAt(pos); - throw new BadBytecode(opcode); - } - - private String getStoreReplacementSignature(int opcode) - throws BadBytecode - { - switch (opcode) { - case AASTORE: - return "(Ljava/lang/Object;ILjava/lang/Object;)V"; - case BASTORE: - return "(Ljava/lang/Object;IB)V"; - case CASTORE: - return "(Ljava/lang/Object;IC)V"; - case DASTORE: - return "(Ljava/lang/Object;ID)V"; - case FASTORE: - return "(Ljava/lang/Object;IF)V"; - case IASTORE: - return "(Ljava/lang/Object;II)V"; - case SASTORE: - return "(Ljava/lang/Object;IS)V"; - case LASTORE: - return "(Ljava/lang/Object;IJ)V"; - } + if (c == AALOAD) + initFrames(clazz, minfo); + + if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD + || c == FALOAD || c == IALOAD || c == LALOAD + || c == SALOAD) { + pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); + } else if (c == AASTORE || c == BASTORE || c == CASTORE + || c == DASTORE || c == FASTORE || c == IASTORE + || c == LASTORE || c == SASTORE) { + pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); + } - throw new BadBytecode(opcode); + } catch (Exception e) { + throw new CannotCompileException(e); + } + } } + + public void clean() { + frames = null; + offset = -1; + } + + public int transform(CtClass tclazz, int pos, CodeIterator iterator, + ConstPool cp) throws BadBytecode { + // Do nothing, see above comment + return pos; + } + + private Frame getFrame(int pos) throws BadBytecode { + return frames[pos - offset]; // Adjust pos + } + + private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode { + if (frames == null) { + frames = ((new Analyzer())).analyze(clazz, minfo); + offset = 0; // start tracking changes + } + } + + private int updatePos(int pos, int increment) { + if (offset > -1) + offset += increment; + + return pos + increment; + } + + private String getTopType(int pos) throws BadBytecode { + Frame frame = getFrame(pos); + if (frame == null) + return null; + + CtClass clazz = frame.peek().getCtClass(); + return clazz != null ? clazz.getName() : null; + } + + private int replace(ConstPool cp, CodeIterator iterator, int pos, + int opcode, String signature) throws BadBytecode { + String castType = null; + String methodName = getMethodName(opcode); + if (methodName != null) { + // See if the object must be cast + if (opcode == AALOAD) { + castType = getTopType(iterator.lookAhead()); + // Do not replace an AALOAD instruction that we do not have a type for + // This happens when the state is guaranteed to be null (Type.UNINIT) + // So we don't really care about this case. + if (castType == null) + return pos; + if ("java.lang.Object".equals(castType)) + castType = null; + } + + // The gap may include extra padding + int gapLength = iterator.insertGap(pos, castType != null ? 5 : 2); + + int mi = cp.addClassInfo(methodClassname); + int methodref = cp.addMethodrefInfo(mi, methodName, signature); + iterator.writeByte(INVOKESTATIC, pos); + iterator.write16bit(methodref, pos + 1); + + if (castType != null) { + int index = cp.addClassInfo(castType); + iterator.writeByte(CHECKCAST, pos + 3); + iterator.write16bit(index, pos + 4); + } + + pos = updatePos(pos, gapLength); + } + + return pos; + } + + private String getMethodName(int opcode) { + String methodName = null; + switch (opcode) { + case AALOAD: + methodName = names.objectRead(); + break; + case BALOAD: + methodName = names.byteOrBooleanRead(); + break; + case CALOAD: + methodName = names.charRead(); + break; + case DALOAD: + methodName = names.doubleRead(); + break; + case FALOAD: + methodName = names.floatRead(); + break; + case IALOAD: + methodName = names.intRead(); + break; + case SALOAD: + methodName = names.shortRead(); + break; + case LALOAD: + methodName = names.longRead(); + break; + case AASTORE: + methodName = names.objectWrite(); + break; + case BASTORE: + methodName = names.byteOrBooleanWrite(); + break; + case CASTORE: + methodName = names.charWrite(); + break; + case DASTORE: + methodName = names.doubleWrite(); + break; + case FASTORE: + methodName = names.floatWrite(); + break; + case IASTORE: + methodName = names.intWrite(); + break; + case SASTORE: + methodName = names.shortWrite(); + break; + case LASTORE: + methodName = names.longWrite(); + break; + } + + if (methodName.equals("")) + methodName = null; + + return methodName; + } + + private String getLoadReplacementSignature(int opcode) throws BadBytecode { + switch (opcode) { + case AALOAD: + return "(Ljava/lang/Object;I)Ljava/lang/Object;"; + case BALOAD: + return "(Ljava/lang/Object;I)B"; + case CALOAD: + return "(Ljava/lang/Object;I)C"; + case DALOAD: + return "(Ljava/lang/Object;I)D"; + case FALOAD: + return "(Ljava/lang/Object;I)F"; + case IALOAD: + return "(Ljava/lang/Object;I)I"; + case SALOAD: + return "(Ljava/lang/Object;I)S"; + case LALOAD: + return "(Ljava/lang/Object;I)J"; + } + + throw new BadBytecode(opcode); + } + + private String getStoreReplacementSignature(int opcode) throws BadBytecode { + switch (opcode) { + case AASTORE: + return "(Ljava/lang/Object;ILjava/lang/Object;)V"; + case BASTORE: + return "(Ljava/lang/Object;IB)V"; + case CASTORE: + return "(Ljava/lang/Object;IC)V"; + case DASTORE: + return "(Ljava/lang/Object;ID)V"; + case FASTORE: + return "(Ljava/lang/Object;IF)V"; + case IASTORE: + return "(Ljava/lang/Object;II)V"; + case SASTORE: + return "(Ljava/lang/Object;IS)V"; + case LASTORE: + return "(Ljava/lang/Object;IJ)V"; + } + + throw new BadBytecode(opcode); + } } diff --git a/src/main/javassist/convert/Transformer.java b/src/main/javassist/convert/Transformer.java index d4ea0ecf..1095cf5b 100644 --- a/src/main/javassist/convert/Transformer.java +++ b/src/main/javassist/convert/Transformer.java @@ -15,9 +15,14 @@ package javassist.convert; -import javassist.bytecode.*; -import javassist.CtClass; import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.Opcode; /** * Transformer and its subclasses are used for executing @@ -35,6 +40,10 @@ public abstract class Transformer implements Opcode { public Transformer getNext() { return next; } public void initialize(ConstPool cp, CodeAttribute attr) {} + + public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { + initialize(cp, minfo.getCodeAttribute()); + } public void clean() {} |