aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/javassist/convert
diff options
context:
space:
mode:
authorjgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>2008-05-24 05:13:20 +0000
committerjgreene <jgreene@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>2008-05-24 05:13:20 +0000
commit2bae8b13291b8ef8fc3df24af627424f344581df (patch)
tree7d68840873257ab78ae69cc9ed9570adc18bc2d8 /src/main/javassist/convert
parente48604525140c746404a637282cb7b1dd97c18c4 (diff)
downloadjavassist-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.java379
-rw-r--r--src/main/javassist/convert/Transformer.java13
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() {}