summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/javassist/CodeConverter.java8
-rw-r--r--src/main/javassist/CtNewMethod.java9
-rw-r--r--src/main/javassist/CtNewWrappedConstructor.java1
-rw-r--r--src/main/javassist/CtNewWrappedMethod.java4
-rw-r--r--src/main/javassist/bytecode/ConstPool.java2
-rw-r--r--src/main/javassist/bytecode/StackMapTable.java10
-rw-r--r--src/main/javassist/bytecode/stackmap/BasicBlock.java2
-rw-r--r--src/main/javassist/bytecode/stackmap/Liveness.java366
-rw-r--r--src/main/javassist/bytecode/stackmap/MapMaker.java227
-rw-r--r--src/main/javassist/bytecode/stackmap/Tracer.java103
-rw-r--r--src/main/javassist/bytecode/stackmap/TypeData.java846
-rw-r--r--src/main/javassist/bytecode/stackmap/TypeTag.java3
-rw-r--r--src/main/javassist/bytecode/stackmap/TypedBlock.java32
-rw-r--r--src/test/javassist/JvstTest.java2
-rw-r--r--src/test/javassist/JvstTest4.java3
-rw-r--r--src/test/javassist/bytecode/StackMapTest.java315
-rw-r--r--src/test/javassist/tools/reflect/LoaderTest.java4
17 files changed, 1016 insertions, 921 deletions
diff --git a/src/main/javassist/CodeConverter.java b/src/main/javassist/CodeConverter.java
index 8873e560..399454ec 100644
--- a/src/main/javassist/CodeConverter.java
+++ b/src/main/javassist/CodeConverter.java
@@ -532,6 +532,14 @@ public class CodeConverter {
if (stack > 0)
codeAttr.setMaxStack(codeAttr.getMaxStack() + stack);
+
+ try {
+ minfo.rebuildStackMapIf6(clazz.getClassPool(),
+ clazz.getClassFile2());
+ }
+ catch (BadBytecode b) {
+ throw new CannotCompileException(b.getMessage(), b);
+ }
}
/**
diff --git a/src/main/javassist/CtNewMethod.java b/src/main/javassist/CtNewMethod.java
index bddf081a..f134dcdd 100644
--- a/src/main/javassist/CtNewMethod.java
+++ b/src/main/javassist/CtNewMethod.java
@@ -246,7 +246,9 @@ public class CtNewMethod {
}
minfo.setCodeAttribute(code.toCodeAttribute());
- return new CtMethod(minfo, field.getDeclaringClass());
+ CtClass cc = field.getDeclaringClass();
+ // a stack map is not needed.
+ return new CtMethod(minfo, cc);
}
/**
@@ -290,7 +292,9 @@ public class CtNewMethod {
}
minfo.setCodeAttribute(code.toCodeAttribute());
- return new CtMethod(minfo, field.getDeclaringClass());
+ CtClass cc = field.getDeclaringClass();
+ // a stack map is not needed.
+ return new CtMethod(minfo, cc);
}
/**
@@ -359,6 +363,7 @@ public class CtNewMethod {
code.setMaxLocals(++s);
code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
minfo.setCodeAttribute(code.toCodeAttribute());
+ // a stack map is not needed.
return new CtMethod(minfo, declaring);
}
diff --git a/src/main/javassist/CtNewWrappedConstructor.java b/src/main/javassist/CtNewWrappedConstructor.java
index 091a6c9a..7163f1ce 100644
--- a/src/main/javassist/CtNewWrappedConstructor.java
+++ b/src/main/javassist/CtNewWrappedConstructor.java
@@ -39,6 +39,7 @@ class CtNewWrappedConstructor extends CtNewWrappedMethod {
howToCallSuper, body,
parameterTypes, constParam);
cons.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+ // a stack map table is not needed.
return cons;
}
catch (NotFoundException e) {
diff --git a/src/main/javassist/CtNewWrappedMethod.java b/src/main/javassist/CtNewWrappedMethod.java
index f3030f7d..66504bed 100644
--- a/src/main/javassist/CtNewWrappedMethod.java
+++ b/src/main/javassist/CtNewWrappedMethod.java
@@ -44,7 +44,9 @@ class CtNewWrappedMethod {
Bytecode code = makeBody(declaring, declaring.getClassFile2(), body,
parameterTypes, returnType, constParam);
- mt.getMethodInfo2().setCodeAttribute(code.toCodeAttribute());
+ MethodInfo minfo = mt.getMethodInfo2();
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ // a stack map has been already created.
return mt;
}
diff --git a/src/main/javassist/bytecode/ConstPool.java b/src/main/javassist/bytecode/ConstPool.java
index 3112e440..27124ac1 100644
--- a/src/main/javassist/bytecode/ConstPool.java
+++ b/src/main/javassist/bytecode/ConstPool.java
@@ -174,7 +174,7 @@ public final class ConstPool {
* @return a fully-qualified class or interface name specified
* by <code>name_index</code>. If the type is an array
* type, this method returns an encoded name like
- * <code>[java.lang.Object;</code> (note that the separators
+ * <code>[Ljava.lang.Object;</code> (note that the separators
* are not slashes but dots).
* @see javassist.ClassPool#getCtClass(String)
*/
diff --git a/src/main/javassist/bytecode/StackMapTable.java b/src/main/javassist/bytecode/StackMapTable.java
index 333d038e..15726be2 100644
--- a/src/main/javassist/bytecode/StackMapTable.java
+++ b/src/main/javassist/bytecode/StackMapTable.java
@@ -67,7 +67,7 @@ public class StackMapTable extends AttributeInfo {
{
try {
return new StackMapTable(newCp,
- new Copier(this.constPool, info, newCp).doit());
+ new Copier(this.constPool, info, newCp, classnames).doit());
}
catch (BadBytecode e) {
throw new RuntimeCopyException("bad bytecode. fatal?");
@@ -400,16 +400,18 @@ public class StackMapTable extends AttributeInfo {
static class Copier extends SimpleCopy {
private ConstPool srcPool, destPool;
+ private Map classnames;
- public Copier(ConstPool src, byte[] data, ConstPool dest) {
+ public Copier(ConstPool src, byte[] data, ConstPool dest, Map names) {
super(data);
srcPool = src;
destPool = dest;
+ classnames = names;
}
protected int copyData(int tag, int data) {
if (tag == OBJECT)
- return srcPool.copy(data, destPool, null);
+ return srcPool.copy(data, destPool, classnames);
else
return data;
}
@@ -418,7 +420,7 @@ public class StackMapTable extends AttributeInfo {
int[] newData = new int[data.length];
for (int i = 0; i < data.length; i++)
if (tags[i] == OBJECT)
- newData[i] = srcPool.copy(data[i], destPool, null);
+ newData[i] = srcPool.copy(data[i], destPool, classnames);
else
newData[i] = data[i];
diff --git a/src/main/javassist/bytecode/stackmap/BasicBlock.java b/src/main/javassist/bytecode/stackmap/BasicBlock.java
index 073099cf..9afe1a3a 100644
--- a/src/main/javassist/bytecode/stackmap/BasicBlock.java
+++ b/src/main/javassist/bytecode/stackmap/BasicBlock.java
@@ -274,7 +274,7 @@ public class BasicBlock {
else if (op == Opcode.JSR_W)
makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5);
else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET)
- makeMark(marks, index, null, 1, true);
+ makeMark(marks, index, null, 4, true);
}
if (et != null) {
diff --git a/src/main/javassist/bytecode/stackmap/Liveness.java b/src/main/javassist/bytecode/stackmap/Liveness.java
deleted file mode 100644
index c600c6c2..00000000
--- a/src/main/javassist/bytecode/stackmap/Liveness.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.bytecode.stackmap;
-
-import javassist.bytecode.*;
-
-public class Liveness {
- protected static final byte UNKNOWN = 0;
- protected static final byte READ = 1;
- protected static final byte UPDATED = 2;
- protected byte[] localsUsage;
-
- /**
- * If true, all the arguments become alive within the whole method body.
- *
- * To correctly compute a stack map table, all the arguments must
- * be alive (localsUsage[?] must be READ) at least in the first block.
- */
- public static boolean useArgs = true;
-
- public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals,
- TypeData[] args)
- throws BadBytecode
- {
- computeUsage(ci, blocks, maxLocals);
- if (useArgs)
- useAllArgs(blocks, args);
-
- computeLiveness1(blocks[0]);
- while (hasChanged(blocks))
- computeLiveness2(blocks[0]);
- }
-
- private void useAllArgs(TypedBlock[] blocks, TypeData[] args) {
- for (int k = 0; k < blocks.length; k++) {
- byte[] usage = blocks[k].localsUsage;
- for (int i = 0; i < args.length; i++)
- if (args[i] != TypeTag.TOP)
- usage[i] = READ;
- }
- }
-
- static final int NOT_YET = 0;
- static final int CHANGED_LAST = 1;
- static final int DONE = 2;
- static final int CHANGED_NOW = 3;
-
- private void computeLiveness1(TypedBlock tb) {
- if (tb.updating) {
- // a loop was detected.
- computeLiveness1u(tb);
- return;
- }
-
- if (tb.inputs != null)
- return;
-
- tb.updating = true;
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- boolean[] in = new boolean[n];
- for (int i = 0; i < n; i++)
- in[i] = usage[i] == READ;
-
- BasicBlock.Catch handlers = tb.toCatch;
- while (handlers != null) {
- TypedBlock h = (TypedBlock)handlers.body;
- computeLiveness1(h);
- for (int k = 0; k < n; k++)
- if (h.inputs[k])
- in[k] = true;
-
- handlers = handlers.next;
- }
-
- if (tb.exit != null) {
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- computeLiveness1(e);
- for (int k = 0; k < n; k++)
- if (!in[k])
- in[k] = usage[k] == UNKNOWN && e.inputs[k];
- }
- }
-
- tb.updating = false;
- if (tb.inputs == null) {
- tb.inputs = in;
- tb.status = DONE;
- }
- else {
- for (int i = 0; i < n; i++)
- if (in[i] && !tb.inputs[i]) {
- tb.inputs[i] = true;
- tb.status = CHANGED_NOW;
- }
- }
- }
-
- private void computeLiveness1u(TypedBlock tb) {
- if (tb.inputs == null) {
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- boolean[] in = new boolean[n];
- for (int i = 0; i < n; i++)
- in[i] = usage[i] == READ;
-
- tb.inputs = in;
- tb.status = DONE;
- }
- }
-
- private void computeLiveness2(TypedBlock tb) {
- if (tb.updating || tb.status >= DONE)
- return;
-
- tb.updating = true;
- if (tb.exit == null)
- tb.status = DONE;
- else {
- boolean changed = false;
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- computeLiveness2(e);
- if (e.status != DONE)
- changed = true;
- }
-
- if (changed) {
- changed = false;
- byte[] usage = tb.localsUsage;
- int n = usage.length;
- for (int i = 0; i < tb.exit.length; i++) {
- TypedBlock e = (TypedBlock)tb.exit[i];
- if (e.status != DONE)
- for (int k = 0; k < n; k++)
- if (!tb.inputs[k]) {
- if (usage[k] == UNKNOWN && e.inputs[k]) {
- tb.inputs[k] = true;
- changed = true;
- }
- }
- }
-
- tb.status = changed ? CHANGED_NOW : DONE;
- }
- else
- tb.status = DONE;
- }
-
- if (computeLiveness2except(tb))
- tb.status = CHANGED_NOW;
-
- tb.updating = false;
- }
-
- private boolean computeLiveness2except(TypedBlock tb) {
- BasicBlock.Catch handlers = tb.toCatch;
- boolean changed = false;
- while (handlers != null) {
- TypedBlock h = (TypedBlock)handlers.body;
- computeLiveness2(h);
- if (h.status != DONE) {
- boolean[] in = tb.inputs;
- int n = in.length;
- for (int k = 0; k < n; k++)
- if (!in[k] && h.inputs[k]) {
- in[k] = true;
- changed = true;
- }
- }
-
- handlers = handlers.next;
- }
-
- return changed;
- }
-
- private boolean hasChanged(TypedBlock[] blocks) {
- int n = blocks.length;
- boolean changed = false;
- for (int i = 0; i < n; i++) {
- TypedBlock tb = blocks[i];
- if (tb.status == CHANGED_NOW) {
- tb.status = CHANGED_LAST;
- changed = true;
- }
- else
- tb.status = NOT_YET;
- }
-
- return changed;
- }
-
- private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals)
- throws BadBytecode
- {
- int n = blocks.length;
- for (int i = 0; i < n; i++) {
- TypedBlock tb = blocks[i];
- localsUsage = tb.localsUsage = new byte[maxLocals];
- int pos = tb.position;
- analyze(ci, pos, pos + tb.length);
- localsUsage = null;
- }
- }
-
- protected final void readLocal(int reg) {
- if (localsUsage[reg] == UNKNOWN)
- localsUsage[reg] = READ;
- }
-
- protected final void writeLocal(int reg) {
- if (localsUsage[reg] == UNKNOWN)
- localsUsage[reg] = UPDATED;
- }
-
- protected void analyze(CodeIterator ci, int begin, int end)
- throws BadBytecode
- {
- ci.begin();
- ci.move(begin);
- while (ci.hasNext()) {
- int index = ci.next();
- if (index >= end)
- break;
-
- int op = ci.byteAt(index);
- if (op < 96)
- if (op < 54)
- doOpcode0_53(ci, index, op);
- else
- doOpcode54_95(ci, index, op);
- else
- if (op == Opcode.IINC) {
- // this does not call writeLocal().
- readLocal(ci.byteAt(index + 1));
- }
- else if (op == Opcode.WIDE)
- doWIDE(ci, index);
- }
- }
-
- private void doOpcode0_53(CodeIterator ci, int pos, int op) {
- switch (op) {
- case Opcode.ILOAD :
- case Opcode.LLOAD :
- case Opcode.FLOAD :
- case Opcode.DLOAD :
- case Opcode.ALOAD :
- readLocal(ci.byteAt(pos + 1));
- break;
- case Opcode.ILOAD_0 :
- case Opcode.ILOAD_1 :
- case Opcode.ILOAD_2 :
- case Opcode.ILOAD_3 :
- readLocal(op - Opcode.ILOAD_0);
- break;
- case Opcode.LLOAD_0 :
- case Opcode.LLOAD_1 :
- case Opcode.LLOAD_2 :
- case Opcode.LLOAD_3 :
- readLocal(op - Opcode.LLOAD_0);
- break;
- case Opcode.FLOAD_0 :
- case Opcode.FLOAD_1 :
- case Opcode.FLOAD_2 :
- case Opcode.FLOAD_3 :
- readLocal(op - Opcode.FLOAD_0);
- break;
- case Opcode.DLOAD_0 :
- case Opcode.DLOAD_1 :
- case Opcode.DLOAD_2 :
- case Opcode.DLOAD_3 :
- readLocal(op - Opcode.DLOAD_0);
- break;
- case Opcode.ALOAD_0 :
- case Opcode.ALOAD_1 :
- case Opcode.ALOAD_2 :
- case Opcode.ALOAD_3 :
- readLocal(op - Opcode.ALOAD_0);
- break;
- }
- }
-
- private void doOpcode54_95(CodeIterator ci, int pos, int op) {
- switch (op) {
- case Opcode.ISTORE :
- case Opcode.LSTORE :
- case Opcode.FSTORE :
- case Opcode.DSTORE :
- case Opcode.ASTORE :
- writeLocal(ci.byteAt(pos + 1));
- break;
- case Opcode.ISTORE_0 :
- case Opcode.ISTORE_1 :
- case Opcode.ISTORE_2 :
- case Opcode.ISTORE_3 :
- writeLocal(op - Opcode.ISTORE_0);
- break;
- case Opcode.LSTORE_0 :
- case Opcode.LSTORE_1 :
- case Opcode.LSTORE_2 :
- case Opcode.LSTORE_3 :
- writeLocal(op - Opcode.LSTORE_0);
- break;
- case Opcode.FSTORE_0 :
- case Opcode.FSTORE_1 :
- case Opcode.FSTORE_2 :
- case Opcode.FSTORE_3 :
- writeLocal(op - Opcode.FSTORE_0);
- break;
- case Opcode.DSTORE_0 :
- case Opcode.DSTORE_1 :
- case Opcode.DSTORE_2 :
- case Opcode.DSTORE_3 :
- writeLocal(op - Opcode.DSTORE_0);
- break;
- case Opcode.ASTORE_0 :
- case Opcode.ASTORE_1 :
- case Opcode.ASTORE_2 :
- case Opcode.ASTORE_3 :
- writeLocal(op - Opcode.ASTORE_0);
- break;
- }
- }
-
- private void doWIDE(CodeIterator ci, int pos) throws BadBytecode {
- int op = ci.byteAt(pos + 1);
- int var = ci.u16bitAt(pos + 2);
- switch (op) {
- case Opcode.ILOAD :
- case Opcode.LLOAD :
- case Opcode.FLOAD :
- case Opcode.DLOAD :
- case Opcode.ALOAD :
- readLocal(var);
- break;
- case Opcode.ISTORE :
- case Opcode.LSTORE :
- case Opcode.FSTORE :
- case Opcode.DSTORE :
- case Opcode.ASTORE :
- writeLocal(var);
- break;
- case Opcode.IINC :
- readLocal(var);
- // this does not call writeLocal().
- break;
- }
- }
-}
diff --git a/src/main/javassist/bytecode/stackmap/MapMaker.java b/src/main/javassist/bytecode/stackmap/MapMaker.java
index 37406281..b846661b 100644
--- a/src/main/javassist/bytecode/stackmap/MapMaker.java
+++ b/src/main/javassist/bytecode/stackmap/MapMaker.java
@@ -16,7 +16,9 @@
package javassist.bytecode.stackmap;
+import java.util.ArrayList;
import javassist.ClassPool;
+import javassist.NotFoundException;
import javassist.bytecode.*;
/**
@@ -125,9 +127,7 @@ public class MapMaker extends Tracer {
TypedBlock.getRetType(minfo.getDescriptor()));
}
- protected MapMaker(MapMaker old, boolean copyStack) {
- super(old, copyStack);
- }
+ protected MapMaker(MapMaker old) { super(old); }
/**
* Runs an analyzer (Phase 1 and 2).
@@ -135,34 +135,11 @@ public class MapMaker extends Tracer {
void make(TypedBlock[] blocks, byte[] code)
throws BadBytecode
{
- TypedBlock first = blocks[0];
- fixParamTypes(first);
- TypeData[] srcTypes = first.localsTypes;
- copyFrom(srcTypes.length, srcTypes, this.localsTypes);
- make(code, first);
-
- int n = blocks.length;
- for (int i = 0; i < n; i++)
- evalExpected(blocks[i]);
- }
-
- /*
- * If a parameter type is String but it is used only as Object
- * within the method body, this MapMaker class will report its type
- * is Object. To avoid this, fixParamTypes calls TypeData.setType()
- * on each parameter type.
- */
- private void fixParamTypes(TypedBlock first) throws BadBytecode {
- TypeData[] types = first.localsTypes;
- int n = types.length;
- for (int i = 0; i < n; i++) {
- TypeData t = types[i];
- if (t instanceof TypeData.ClassName) {
- /* Skip the following statement if t.isNullType() is true
- * although a parameter type is never null type.
- */
- TypeData.setType(t, t.getName(), classPool);
- }
+ make(code, blocks[0]);
+ try {
+ fixTypes(blocks);
+ } catch (NotFoundException e) {
+ throw new BadBytecode("failed to resolve types", e);
}
}
@@ -171,17 +148,19 @@ public class MapMaker extends Tracer {
private void make(byte[] code, TypedBlock tb)
throws BadBytecode
{
- BasicBlock.Catch handlers = tb.toCatch;
- while (handlers != null) {
- traceException(code, handlers);
- handlers = handlers.next;
- }
+ copyTypeData(tb.stackTop, tb.stackTypes, stackTypes);
+ stackTop = tb.stackTop;
+ copyTypeData(tb.localsTypes.length, tb.localsTypes, localsTypes);
+
+ traceException(code, tb.toCatch);
int pos = tb.position;
int end = pos + tb.length;
while (pos < end)
pos += doOpcode(pos, code);
+ traceException(code, tb.toCatch);
+
if (tb.exit != null) {
for (int i = 0; i < tb.exit.length; i++) {
TypedBlock e = (TypedBlock)tb.exit[i];
@@ -189,7 +168,7 @@ public class MapMaker extends Tracer {
mergeMap(e, true);
else {
recordStackMap(e);
- MapMaker maker = new MapMaker(this, true);
+ MapMaker maker = new MapMaker(this);
maker.make(code, e);
}
}
@@ -199,71 +178,66 @@ public class MapMaker extends Tracer {
private void traceException(byte[] code, TypedBlock.Catch handler)
throws BadBytecode
{
- TypedBlock tb = (TypedBlock)handler.body;
- if (tb.alreadySet())
- mergeMap(tb, false);
- else {
- recordStackMap(tb, handler.typeIndex);
- MapMaker maker = new MapMaker(this, false);
-
- /* the following code is equivalent to maker.copyFrom(this)
- * except stackTypes are not copied.
- */
- maker.stackTypes[0] = tb.stackTypes[0].getSelf();
- maker.stackTop = 1;
- maker.make(code, tb);
+ while (handler != null) {
+ TypedBlock tb = (TypedBlock)handler.body;
+ if (tb.alreadySet())
+ mergeMap(tb, false);
+ else {
+ recordStackMap(tb, handler.typeIndex);
+ MapMaker maker = new MapMaker(this);
+ maker.make(code, tb);
+ }
+
+ handler = handler.next;
}
}
- private void mergeMap(TypedBlock dest, boolean mergeStack) {
- boolean[] inputs = dest.inputs;
- int n = inputs.length;
+ private void mergeMap(TypedBlock dest, boolean mergeStack) throws BadBytecode {
+ int n = localsTypes.length;
for (int i = 0; i < n; i++)
- if (inputs[i])
- merge(localsTypes[i], dest.localsTypes[i]);
+ dest.localsTypes[i] = merge(localsTypes[i], dest.localsTypes[i]);
if (mergeStack) {
n = stackTop;
for (int i = 0; i < n; i++)
- merge(stackTypes[i], dest.stackTypes[i]);
+ dest.stackTypes[i] = merge(stackTypes[i], dest.stackTypes[i]);
}
}
- private void merge(TypeData td, TypeData target) {
- boolean tdIsObj = false;
- boolean targetIsObj = false;
- // td or target is null if it is TOP.
- if (td != TOP && td.isObjectType())
- tdIsObj = true;
-
- if (target != TOP && target.isObjectType())
- targetIsObj = true;
-
- if (tdIsObj && targetIsObj)
- target.merge(td);
+ private TypeData merge(TypeData src, TypeData target) throws BadBytecode {
+ if (src == target)
+ return target;
+ else if (target instanceof TypeData.ClassName
+ || target instanceof TypeData.BasicType) // a parameter
+ return target;
+ else if (target instanceof TypeData.AbsTypeVar) {
+ ((TypeData.AbsTypeVar)target).merge(src);
+ return target;
+ }
+ else
+ throw new RuntimeException("fatal: this should never happen");
}
private void recordStackMap(TypedBlock target)
throws BadBytecode
{
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
int st = stackTop;
- copyFrom(st, stackTypes, tStackTypes);
+ recordTypeData(st, stackTypes, tStackTypes);
recordStackMap0(target, st, tStackTypes);
}
private void recordStackMap(TypedBlock target, int exceptionType)
throws BadBytecode
{
+ TypeData[] tStackTypes = TypeData.make(stackTypes.length);
String type;
- if (exceptionType == 0)
+ if (exceptionType == 0) // for finally clauses
type = "java.lang.Throwable";
else
type = cpool.getClassInfo(exceptionType);
- TypeData[] tStackTypes = new TypeData[stackTypes.length];
tStackTypes[0] = new TypeData.ClassName(type);
-
recordStackMap0(target, 1, tStackTypes);
}
@@ -271,34 +245,50 @@ public class MapMaker extends Tracer {
throws BadBytecode
{
int n = localsTypes.length;
- TypeData[] tLocalsTypes = new TypeData[n];
- int k = copyFrom(n, localsTypes, tLocalsTypes);
-
- boolean[] inputs = target.inputs;
- for (int i = 0; i < n; i++)
- if (!inputs[i])
- tLocalsTypes[i] = TOP;
-
+ TypeData[] tLocalsTypes = TypeData.make(n);
+ int k = recordTypeData(n, localsTypes, tLocalsTypes);
target.setStackMap(st, tStackTypes, k, tLocalsTypes);
}
- // Phase 2
+ protected static int recordTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+ int k = -1;
+ for (int i = 0; i < n; i++) {
+ TypeData t = srcTypes[i];
+ destTypes[i] = t.join();
+ if (t != TOP)
+ k = i + 1; // t might be long or double.
+ }
- void evalExpected(TypedBlock target) throws BadBytecode {
- ClassPool cp = classPool;
- evalExpected(cp, target.stackTop, target.stackTypes);
- TypeData[] types = target.localsTypes;
- if (types != null) // unless this block is dead code
- evalExpected(cp, types.length, types);
+ return k + 1;
}
- private static void evalExpected(ClassPool cp, int n, TypeData[] types)
- throws BadBytecode
- {
- for (int i = 0; i < n; i++) {
- TypeData td = types[i];
- if (td != null)
- td.evalExpectedType(cp);
+ protected static void copyTypeData(int n, TypeData[] srcTypes, TypeData[] destTypes) {
+ for (int i = 0; i < n; i++)
+ destTypes[i] = srcTypes[i];
+ }
+
+ // Phase 2
+
+ /*
+ * This method first finds strongly connected components (SCCs)
+ * on a graph made by TypeData by Tarjan's algorithm.
+ * SCCs are TypeData nodes sharing the same type.
+ * Since SCCs are found in the topologically sorted order,
+ * their types are also fixed when they are found.
+ */
+ private void fixTypes(TypedBlock[] blocks) throws NotFoundException {
+ ArrayList preOrder = new ArrayList();
+ int len = blocks.length;
+ int index = 0;
+ for (int i = 0; i < len; i++) {
+ TypedBlock block = blocks[i];
+ int n = block.localsTypes.length;
+ for (int j = 0; j < n; j++)
+ index = block.localsTypes[j].dfs(preOrder, index, classPool);
+
+ n = block.stackTop;
+ for (int j = 0; j < n; j++)
+ index = block.stackTypes[j].dfs(preOrder, index, classPool);
}
}
@@ -370,19 +360,14 @@ public class MapMaker extends Tracer {
}
else if (stackTop == 1 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td == TOP)
- writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
- else
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
else if (stackTop == 2 && diffL == 0) {
TypeData td = bb.stackTypes[0];
- if (td != TOP && td.is2WordType()) {
+ if (td.is2WordType()) {
// bb.stackTypes[1] must be TOP.
- writer.sameLocals(offsetDelta, td.getTypeTag(),
- td.getTypeData(cpool));
+ writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool));
return;
}
}
@@ -401,16 +386,10 @@ public class MapMaker extends Tracer {
int j = 0;
for (int i = 0; i < num; i++) {
TypeData td = types[offset + i];
- if (td == TOP) {
- tags[j] = StackMapTable.TOP;
- data[j] = 0;
- }
- else {
- tags[j] = td.getTypeTag();
- data[j] = td.getTypeData(cp);
- if (td.is2WordType())
- i++;
- }
+ tags[j] = td.getTypeTag();
+ data[j] = td.getTypeData(cp);
+ if (td.is2WordType())
+ i++;
j++;
}
@@ -439,14 +418,8 @@ public class MapMaker extends Tracer {
private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
for (int i = 0; i < len; i++) {
- TypeData td = oldTd[i];
- if (td == TOP) { // the next element to LONG/DOUBLE is TOP.
- if (newTd[i] != TOP)
- return false;
- }
- else
- if (!oldTd[i].equals(newTd[i]))
- return false;
+ if (!oldTd[i].eq(newTd[i]))
+ return false;
}
return true;
@@ -457,7 +430,7 @@ public class MapMaker extends Tracer {
while (offset < len) {
TypeData td = types[offset++];
num++;
- if (td != TOP && td.is2WordType())
+ if (td.is2WordType())
offset++;
}
@@ -515,13 +488,9 @@ public class MapMaker extends Tracer {
writer.write16bit(num - numDWord);
for (int i = 0; i < num; i++) {
TypeData td = types[i];
- if (td == TOP)
- writer.writeVerifyTypeInfo(StackMap.TOP, 0);
- else {
- writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
- if (td.is2WordType())
- i++;
- }
+ writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
+ if (td.is2WordType())
+ i++;
}
}
}
diff --git a/src/main/javassist/bytecode/stackmap/Tracer.java b/src/main/javassist/bytecode/stackmap/Tracer.java
index 7a702bcc..d63fa75a 100644
--- a/src/main/javassist/bytecode/stackmap/Tracer.java
+++ b/src/main/javassist/bytecode/stackmap/Tracer.java
@@ -31,7 +31,7 @@ import javassist.ClassPool;
public abstract class Tracer implements TypeTag {
protected ClassPool classPool;
protected ConstPool cpool;
- protected String returnType;
+ protected String returnType; // used as the type of ARETURN
protected int stackTop;
protected TypeData[] stackTypes;
@@ -43,39 +43,17 @@ public abstract class Tracer implements TypeTag {
cpool = cp;
returnType = retType;
stackTop = 0;
- stackTypes = new TypeData[maxStack];
- localsTypes = new TypeData[maxLocals];
+ stackTypes = TypeData.make(maxStack);
+ localsTypes = TypeData.make(maxLocals);
}
- public Tracer(Tracer t, boolean copyStack) {
+ public Tracer(Tracer t) {
classPool = t.classPool;
cpool = t.cpool;
returnType = t.returnType;
-
stackTop = t.stackTop;
- int size = t.stackTypes.length;
- stackTypes = new TypeData[size];
- if (copyStack)
- copyFrom(t.stackTop, t.stackTypes, stackTypes);
-
- int size2 = t.localsTypes.length;
- localsTypes = new TypeData[size2];
- copyFrom(size2, t.localsTypes, localsTypes);
- }
-
- protected static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
- int k = -1;
- for (int i = 0; i < n; i++) {
- TypeData t = srcTypes[i];
- destTypes[i] = t == TOP ? TOP : t.getSelf();
- if (t != TOP)
- if (t.is2WordType())
- k = i + 1;
- else
- k = i;
- }
-
- return k + 1;
+ stackTypes = TypeData.make(t.stackTypes.length);
+ localsTypes = TypeData.make(t.localsTypes.length);
}
/**
@@ -255,11 +233,7 @@ public abstract class Tracer implements TypeTag {
case Opcode.AALOAD : {
int s = --stackTop - 1;
TypeData data = stackTypes[s];
- if (data == null || !data.isObjectType())
- throw new BadBytecode("bad AALOAD");
- else
- stackTypes[s] = new TypeData.ArrayElement(data);
-
+ stackTypes[s] = TypeData.ArrayElement.make(data);
break; }
case Opcode.BALOAD :
case Opcode.CALOAD :
@@ -309,14 +283,12 @@ public abstract class Tracer implements TypeTag {
return 2;
}
- private int doALOAD(int localVar) { // int localVar, TypeData type) {
+ private int doALOAD(int localVar) {
stackTypes[stackTop++] = localsTypes[localVar];
return 2;
}
private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode {
- TypeData[] localsTypes = this.localsTypes;
- TypeData[] stackTypes = this.stackTypes;
switch (op) {
case Opcode.ISTORE :
return doXSTORE(pos, code, INTEGER);
@@ -376,9 +348,9 @@ public abstract class Tracer implements TypeTag {
stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
break;
case Opcode.AASTORE :
- TypeData.setType(stackTypes[stackTop - 1],
- TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()),
- classPool);
+ TypeData.ArrayElement.aastore(stackTypes[stackTop - 3],
+ stackTypes[stackTop - 1],
+ classPool);
stackTop -= 3;
break;
case Opcode.BASTORE :
@@ -450,7 +422,7 @@ public abstract class Tracer implements TypeTag {
private int doASTORE(int index) {
stackTop--;
// implicit upcast might be done.
- localsTypes[index] = stackTypes[stackTop].copy();
+ localsTypes[index] = stackTypes[stackTop];
return 2;
}
@@ -475,16 +447,16 @@ public abstract class Tracer implements TypeTag {
// this does not call writeLocal().
return 3;
case Opcode.I2L :
- stackTypes[stackTop] = LONG;
- stackTypes[stackTop - 1] = TOP;
+ stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop] = TOP;
stackTop++;
break;
case Opcode.I2F :
stackTypes[stackTop - 1] = FLOAT;
break;
case Opcode.I2D :
- stackTypes[stackTop] = DOUBLE;
- stackTypes[stackTop - 1] = TOP;
+ stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop] = TOP;
stackTop++;
break;
case Opcode.L2I :
@@ -494,24 +466,26 @@ public abstract class Tracer implements TypeTag {
stackTypes[--stackTop - 1] = FLOAT;
break;
case Opcode.L2D :
- stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop - 2] = DOUBLE;
break;
case Opcode.F2I :
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.F2L :
- stackTypes[stackTop - 1] = TOP;
- stackTypes[stackTop++] = LONG;
+ stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop] = TOP;
+ stackTop++;
break;
case Opcode.F2D :
- stackTypes[stackTop - 1] = TOP;
- stackTypes[stackTop++] = DOUBLE;
+ stackTypes[stackTop - 1] = DOUBLE;
+ stackTypes[stackTop] = TOP;
+ stackTop++;
break;
case Opcode.D2I :
stackTypes[--stackTop - 1] = INTEGER;
break;
case Opcode.D2L :
- stackTypes[stackTop - 1] = LONG;
+ stackTypes[stackTop - 2] = LONG;
break;
case Opcode.D2F :
stackTypes[--stackTop - 1] = FLOAT;
@@ -602,7 +576,7 @@ public abstract class Tracer implements TypeTag {
visitReturn(pos, code);
break;
case Opcode.ARETURN :
- TypeData.setType(stackTypes[--stackTop], returnType, classPool);
+ stackTypes[--stackTop].setType(returnType, classPool);
visitReturn(pos, code);
break;
case Opcode.RETURN :
@@ -644,17 +618,21 @@ public abstract class Tracer implements TypeTag {
= new TypeData.ClassName(type);
return 3; }
case Opcode.ARRAYLENGTH :
- TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool);
+ stackTypes[stackTop - 1].setType("[Ljava.lang.Object;", classPool);
stackTypes[stackTop - 1] = INTEGER;
break;
case Opcode.ATHROW :
- TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
+ stackTypes[--stackTop].setType("java.lang.Throwable", classPool);
visitThrow(pos, code);
break;
case Opcode.CHECKCAST : {
// TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
int i = ByteArray.readU16bit(code, pos + 1);
- stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
+ String type = cpool.getClassInfo(i);
+ if (type.charAt(0) == '[')
+ type = type.replace('.', '/'); // getClassInfo() may return "[java.lang.Object;".
+
+ stackTypes[stackTop - 1] = new TypeData.ClassName(type);
return 3; }
case Opcode.INSTANCEOF :
// TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
@@ -748,9 +726,9 @@ public abstract class Tracer implements TypeTag {
stackTop -= Descriptor.dataSize(desc);
char c = desc.charAt(0);
if (c == 'L')
- TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
+ stackTypes[stackTop].setType(getFieldClassName(desc, 0), classPool);
else if (c == '[')
- TypeData.setType(stackTypes[stackTop], desc, classPool);
+ stackTypes[stackTop].setType(desc, classPool);
setFieldTarget(notStatic, index);
return 3;
@@ -767,7 +745,7 @@ public abstract class Tracer implements TypeTag {
private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
if (notStatic) {
String className = cpool.getFieldrefClassName(index);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ stackTypes[--stackTop].setType(className, classPool);
}
}
@@ -823,7 +801,7 @@ public abstract class Tracer implements TypeTag {
checkParamTypes(desc, 1);
if (notStatic) {
String className = cpool.getMethodrefClassName(i);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ stackTypes[--stackTop].setType(className, classPool);
}
pushMemberType(desc);
@@ -835,7 +813,7 @@ public abstract class Tracer implements TypeTag {
String desc = cpool.getInterfaceMethodrefType(i);
checkParamTypes(desc, 1);
String className = cpool.getInterfaceMethodrefClassName(i);
- TypeData.setType(stackTypes[--stackTop], className, classPool);
+ stackTypes[--stackTop].setType(className, classPool);
pushMemberType(desc);
return 5;
}
@@ -912,10 +890,9 @@ public abstract class Tracer implements TypeTag {
stackTop--;
if (array)
- TypeData.setType(stackTypes[stackTop],
- desc.substring(i, k), classPool);
+ stackTypes[stackTop].setType(desc.substring(i, k), classPool);
else if (c == 'L')
- TypeData.setType(stackTypes[stackTop],
- desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
+ stackTypes[stackTop].setType(desc.substring(i + 1, k - 1).replace('/', '.'),
+ classPool);
}
}
diff --git a/src/main/javassist/bytecode/stackmap/TypeData.java b/src/main/javassist/bytecode/stackmap/TypeData.java
index cef23211..2b11905f 100644
--- a/src/main/javassist/bytecode/stackmap/TypeData.java
+++ b/src/main/javassist/bytecode/stackmap/TypeData.java
@@ -20,8 +20,11 @@ import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
import javassist.bytecode.StackMapTable;
import javassist.bytecode.BadBytecode;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.ArrayList;
public abstract class TypeData {
@@ -29,9 +32,15 @@ public abstract class TypeData {
* array type is a subtype of Cloneable and Serializable
*/
- protected TypeData() {}
+ public static TypeData[] make(int size) {
+ TypeData[] array = new TypeData[size];
+ for (int i = 0; i < size; i++)
+ array[i] = TypeTag.TOP;
+
+ return array;
+ }
- public abstract void merge(TypeData neighbor);
+ protected TypeData() {}
/**
* Sets the type name of this object type. If the given type name is
@@ -40,36 +49,48 @@ public abstract class TypeData {
*
* @param className dot-separated name unless the type is an array type.
*/
- static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
- if (td == TypeTag.TOP)
- throw new BadBytecode("unset variable");
- else
- td.setType(className, cp);
+ private static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode {
+ td.setType(className, cp);
}
- public abstract boolean equals(Object obj);
-
public abstract int getTypeTag();
public abstract int getTypeData(ConstPool cp);
- /*
- * See UninitData.getSelf().
- */
- public TypeData getSelf() { return this; }
+ public TypeData join() { return new TypeVar(this); }
- /* An operand value is copied when it is stored in a
- * local variable.
+ /**
+ * If the type is a basic type, this method normalizes the type
+ * and returns a BasicType object. Otherwise, it returns null.
*/
- public abstract TypeData copy();
+ public abstract BasicType isBasicType();
+
+ public abstract boolean is2WordType();
- public abstract boolean isObjectType();
- public boolean is2WordType() { return false; }
+ /**
+ * Returns false if getName() returns a valid type name.
+ */
public boolean isNullType() { return false; }
- public abstract String getName() throws BadBytecode;
- protected abstract void setType(String s, ClassPool cp) throws BadBytecode;
- public abstract void evalExpectedType(ClassPool cp) throws BadBytecode;
- public abstract String getExpected() throws BadBytecode;
+ public boolean isUninit() { return false; }
+
+ public abstract boolean eq(TypeData d);
+
+ public abstract String getName();
+ public abstract void setType(String s, ClassPool cp) throws BadBytecode;
+
+ // depth-first search
+ public int dfs(ArrayList order, int index, ClassPool cp)
+ throws NotFoundException
+ {
+ return index;
+ }
+
+ /**
+ * Returns this if it is a TypeVar or a TypeVar that this
+ * type depends on. Otherwise, this method returns null.
+ * It is used by dfs().
+ */
+ protected TypeVar toTypeVar() { return null; }
/**
* Primitive types.
@@ -83,437 +104,607 @@ public abstract class TypeData {
typeTag = tag;
}
- public void merge(TypeData neighbor) {}
-
- public boolean equals(Object obj) {
- return this == obj;
- }
-
public int getTypeTag() { return typeTag; }
public int getTypeData(ConstPool cp) { return 0; }
- public boolean isObjectType() { return false; }
+ public TypeData join() {
+ if (this == TypeTag.TOP)
+ return this;
+ else
+ return super.join();
+ }
+
+ public BasicType isBasicType() { return this; }
public boolean is2WordType() {
return typeTag == StackMapTable.LONG
|| typeTag == StackMapTable.DOUBLE;
}
- public TypeData copy() {
- return this;
- }
-
- public void evalExpectedType(ClassPool cp) throws BadBytecode {}
-
- public String getExpected() throws BadBytecode {
- return name;
- }
+ public boolean eq(TypeData d) { return this == d; }
public String getName() {
return name;
}
- protected void setType(String s, ClassPool cp) throws BadBytecode {
+ public void setType(String s, ClassPool cp) throws BadBytecode {
throw new BadBytecode("conflict: " + name + " and " + s);
}
public String toString() { return name; }
}
- protected static abstract class TypeName extends TypeData {
- protected ArrayList equivalences;
-
- protected String expectedName;
- private CtClass cache;
- private boolean evalDone;
+ // a type variable
+ public static abstract class AbsTypeVar extends TypeData {
+ public AbsTypeVar() {}
+ public abstract void merge(TypeData t);
+ public int getTypeTag() { return StackMapTable.OBJECT; }
- protected TypeName() {
- equivalences = new ArrayList();
- equivalences.add(this);
- expectedName = null;
- cache = null;
- evalDone = false;
+ public int getTypeData(ConstPool cp) {
+ return cp.addClassInfo(getName());
}
- public void merge(TypeData neighbor) {
- if (this == neighbor)
- return;
-
- if (!(neighbor instanceof TypeName))
- return; // neighbor might be UninitData
+ public boolean eq(TypeData d) { return getName().equals(d.getName()); }
+ }
- TypeName neighbor2 = (TypeName)neighbor;
- ArrayList list = equivalences;
- ArrayList list2 = neighbor2.equivalences;
- if (list == list2)
- return;
+ // a type variable representing a class type.
+ public static class TypeVar extends AbsTypeVar {
+ protected ArrayList lowers; // lower bounds of this type. ArrayList<TypeData>
+ protected ArrayList usedBy; // reverse relations of lowers
+ protected ArrayList uppers; // upper bounds of this type.
+ protected String fixedType;
- int n = list2.size();
- for (int i = 0; i < n; i++) {
- TypeName tn = (TypeName)list2.get(i);
- add(list, tn);
- tn.equivalences = list;
- }
+ public TypeVar(TypeData t) {
+ uppers = null;
+ lowers = new ArrayList(2);
+ usedBy = new ArrayList(2);
+ merge(t);
+ fixedType = null;
}
- private static void add(ArrayList list, TypeData td) {
- int n = list.size();
- for (int i = 0; i < n; i++)
- if (list.get(i) == td)
- return;
-
- list.add(td);
+ public String getName() {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).getName();
+ else
+ return fixedType;
}
- /* NullType overrides this method.
- */
- public int getTypeTag() { return StackMapTable.OBJECT; }
-
- public int getTypeData(ConstPool cp) {
- String type;
- try {
- type = getExpected();
- } catch (BadBytecode e) {
- throw new RuntimeException("fatal error: ", e);
- }
-
- return getTypeData2(cp, type);
+ public BasicType isBasicType() {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).isBasicType();
+ else
+ return null;
}
- /* NullType overrides this method.
- */
- protected int getTypeData2(ConstPool cp, String type) {
- return cp.addClassInfo(type);
+ public boolean is2WordType() {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).is2WordType();
+ else
+ return false;
}
- public boolean equals(Object obj) {
- if (obj instanceof TypeName) {
- try {
- TypeName tn = (TypeName)obj;
- return getExpected().equals(tn.getExpected());
- }
- catch (BadBytecode e) {}
- }
-
- return false;
+ public boolean isNullType() {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).isNullType();
+ else
+ return false;
}
- public boolean isObjectType() { return true; }
+ public boolean isUninit() {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).isUninit();
+ else
+ return false;
+ }
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- if (update(cp, expectedName, typeName))
- expectedName = typeName;
+ public void merge(TypeData t) {
+ lowers.add(t);
+ if (t instanceof TypeVar)
+ ((TypeVar)t).usedBy.add(this);
}
- public void evalExpectedType(ClassPool cp) throws BadBytecode {
- if (this.evalDone)
- return;
+ public int getTypeTag() {
+ /* If fixedType is null after calling dfs(), then this
+ type is NULL, Uninit, or a basic type. So call
+ getTypeTag() on the first element of lowers. */
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).getTypeTag();
+ else
+ return super.getTypeTag();
+ }
- ArrayList equiv = this.equivalences;
- int n = equiv.size();
- String name = evalExpectedType2(equiv, n);
- if (name == null) {
- name = this.expectedName;
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (td instanceof TypeName) {
- TypeName tn = (TypeName)td;
- if (update(cp, name, tn.expectedName))
- name = tn.expectedName;
+ public int getTypeData(ConstPool cp) {
+ if (fixedType == null)
+ return ((TypeData)lowers.get(0)).getTypeData(cp);
+ else
+ return super.getTypeData(cp);
+ }
+
+ public void setType(String typeName, ClassPool cp) throws BadBytecode {
+ if (uppers == null)
+ uppers = new ArrayList();
+
+ uppers.add(typeName);
+ }
+
+ protected TypeVar toTypeVar() { return this; }
+
+ private int visited = 0;
+ private int smallest = 0;
+ private boolean inList = false;
+
+ // depth-first serach
+ public int dfs(ArrayList preOrder, int index, ClassPool cp) throws NotFoundException {
+ if (visited > 0)
+ return index; // MapMaker.make() may call an already visited node.
+
+ visited = smallest = ++index;
+ preOrder.add(this);
+ inList = true;
+ int n = lowers.size();
+ for (int i = 0; i < n; i++) {
+ TypeVar child = ((TypeData)lowers.get(i)).toTypeVar();
+ if (child != null)
+ if (child.visited == 0) {
+ index = child.dfs(preOrder, index, cp);
+ if (child.smallest < smallest)
+ smallest = child.smallest;
+ }
+ else if (child.inList)
+ if (child.visited < smallest)
+ smallest = child.visited;
+ }
+
+ if (visited == smallest) {
+ ArrayList scc = new ArrayList(); // strongly connected component
+ TypeVar cv;
+ do {
+ cv = (TypeVar)preOrder.remove(preOrder.size() - 1);
+ cv.inList = false;
+ scc.add(cv);
+ } while (cv != this);
+ fixTypes(scc, cp);
+ }
+
+ return index;
+ }
+
+ private void fixTypes(ArrayList scc, ClassPool cp) throws NotFoundException {
+ HashSet lowersSet = new HashSet();
+ boolean isBasicType = false;
+ TypeData kind = null;
+ int size = scc.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList tds = ((TypeVar)scc.get(i)).lowers;
+ int size2 = tds.size();
+ for (int j = 0; j < size2; j++) {
+ TypeData d = (TypeData)tds.get(j);
+ BasicType bt = d.isBasicType();
+ if (kind == null) {
+ if (bt == null) {
+ isBasicType = false;
+ kind = d;
+ /* If scc has only an UninitData, fixedType is kept null.
+ So lowerSet must be empty. If scc has not only an UninitData
+ but also another TypeData, an error must be thrown but this
+ error detection has not been implemented. */
+ if (d.isUninit())
+ break;
+ }
+ else {
+ isBasicType = true;
+ kind = bt;
+ }
}
+ else {
+ if ((bt == null && isBasicType)
+ || (bt != null && kind != bt)) {
+ isBasicType = true;
+ kind = TypeTag.TOP;
+ break;
+ }
+ }
+
+ if (bt == null && !d.isNullType())
+ lowersSet.add(d.getName());
}
}
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (td instanceof TypeName) {
- TypeName tn = (TypeName)td;
- tn.expectedName = name;
- tn.cache = null;
- tn.evalDone = true;
+ if (isBasicType) {
+ for (int i = 0; i < size; i++) {
+ TypeVar cv = (TypeVar)scc.get(i);
+ cv.lowers.clear();
+ cv.lowers.add(kind);
+ }
+ }
+ else {
+ String typeName = fixTypes2(scc, lowersSet, cp);
+ for (int i = 0; i < size; i++) {
+ TypeVar cv = (TypeVar)scc.get(i);
+ cv.fixedType = typeName;
}
}
}
- private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode {
- String origName = null;
- for (int i = 0; i < n; i++) {
- TypeData td = (TypeData)equiv.get(i);
- if (!td.isNullType())
- if (origName == null)
- origName = td.getName();
- else if (!origName.equals(td.getName()))
- return null;
+ private String fixTypes2(ArrayList scc, HashSet lowersSet, ClassPool cp) throws NotFoundException {
+ Iterator it = lowersSet.iterator();
+ if (lowersSet.size() == 0)
+ return null; // only NullType
+ else if (lowersSet.size() == 1)
+ return (String)it.next();
+ else {
+ CtClass cc = cp.get((String)it.next());
+ while (it.hasNext())
+ cc = commonSuperClassEx(cc, cp.get((String)it.next()));
+
+ if (cc.getSuperclass() == null || isObjectArray(cc))
+ cc = fixByUppers(scc, cp, new HashSet(), cc);
+
+ if (cc.isArray())
+ return Descriptor.toJvmName(cc);
+ else
+ return cc.getName();
}
+ }
- return origName;
+ private static boolean isObjectArray(CtClass cc) throws NotFoundException {
+ return cc.isArray() && cc.getComponentType().getSuperclass() == null;
}
- protected boolean isTypeName() { return true; }
+ private CtClass fixByUppers(ArrayList users, ClassPool cp, HashSet visited, CtClass type)
+ throws NotFoundException
+ {
+ if (users == null)
+ return type;
- private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode {
- if (typeName == null)
- return false;
- else if (oldName == null)
- return true;
- else if (oldName.equals(typeName))
- return false;
- else if (typeName.charAt(0) == '['
- && oldName.equals("[Ljava.lang.Object;")) {
- /* this rule is not correct but Tracer class sets the type
- of the operand of arraylength to java.lang.Object[].
- Thus, int[] etc. must be a subtype of java.lang.Object[].
- */
- return true;
- }
+ int size = users.size();
+ for (int i = 0; i < size; i++) {
+ TypeVar t = (TypeVar)users.get(i);
+ if (!visited.add(t))
+ return type;
- try {
- if (cache == null)
- cache = cp.get(oldName);
-
- CtClass cache2 = cp.get(typeName);
- if (cache2.subtypeOf(cache)) {
- cache = cache2;
- return true;
+ if (t.uppers != null) {
+ int s = t.uppers.size();
+ for (int k = 0; k < s; k++) {
+ CtClass cc = cp.get((String)t.uppers.get(k));
+ if (cc.subtypeOf(type))
+ type = cc;
+ }
}
- else
- return false;
- }
- catch (NotFoundException e) {
- throw new BadBytecode("cannot find " + e.getMessage());
- }
- }
- /* See also {NullType,ArrayElement}.getExpected().
- */
- public String getExpected() throws BadBytecode {
- ArrayList equiv = equivalences;
- if (equiv.size() == 1)
- return getName();
- else {
- String en = expectedName;
- if (en == null)
- return "java.lang.Object";
- else
- return en;
+ type = fixByUppers(t.usedBy, cp, visited, type);
}
- }
- public String toString() {
- try {
- String en = expectedName;
- if (en != null)
- return en;
+ return type;
+ }
+ }
- String name = getName();
- if (equivalences.size() == 1)
- return name;
- else
- return name + "?";
- }
- catch (BadBytecode e) {
- return "<" + e.getMessage() + ">";
- }
+ /**
+ * Finds the most specific common super class of the given classes
+ * by considering array types.
+ */
+ public static CtClass commonSuperClassEx(CtClass one, CtClass two) throws NotFoundException {
+ if (one.isArray() && two.isArray()) {
+ CtClass ele1 = one.getComponentType();
+ CtClass ele2 = two.getComponentType();
+ CtClass element = commonSuperClassEx(ele1, ele2);
+ if (element == ele1)
+ return one;
+ else if (element == ele2)
+ return two;
+
+ return one.getClassPool().get(element.getName() + "[]");
}
+ else
+ return commonSuperClass(one, two);
}
/**
- * Type data for OBJECT.
+ * Finds the most specific common super class of the given classes.
+ * This method is a copy from javassist.bytecode.analysis.Type.
*/
- public static class ClassName extends TypeName {
- private String name; // dot separated. null if this object is a copy of another.
-
- public ClassName(String n) {
- name = n;
+ public static CtClass commonSuperClass(CtClass one, CtClass two) throws NotFoundException {
+ CtClass deep = one;
+ CtClass shallow = two;
+ CtClass backupShallow = shallow;
+ CtClass backupDeep = deep;
+
+ // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly
+ for (;;) {
+ // In case we get lucky, and find a match early
+ if (eq(deep, shallow) && deep.getSuperclass() != null)
+ return deep;
+
+ CtClass deepSuper = deep.getSuperclass();
+ CtClass shallowSuper = shallow.getSuperclass();
+
+ if (shallowSuper == null) {
+ // right, now reset shallow
+ shallow = backupShallow;
+ break;
+ }
+
+ if (deepSuper == null) {
+ // wrong, swap them, since deep is now useless, its our tmp before we swap it
+ deep = backupDeep;
+ backupDeep = backupShallow;
+ backupShallow = deep;
+
+ deep = shallow;
+ shallow = backupShallow;
+ break;
+ }
+
+ deep = deepSuper;
+ shallow = shallowSuper;
}
- public TypeData copy() {
- return new ClassName(name);
+ // Phase 2 - Move deepBackup up by (deep end - deep)
+ for (;;) {
+ deep = deep.getSuperclass();
+ if (deep == null)
+ break;
+
+ backupDeep = backupDeep.getSuperclass();
}
- public String getName() { // never returns null.
- return name;
+ deep = backupDeep;
+
+ // Phase 3 - The hierarchy positions are now aligned
+ // The common super class is easy to find now
+ while (!eq(deep, shallow)) {
+ deep = deep.getSuperclass();
+ shallow = shallow.getSuperclass();
}
+
+ return deep;
}
- /**
- * Type data for NULL or OBJECT.
- * The types represented by the instances of this class are
- * initially NULL but will be OBJECT.
+ static boolean eq(CtClass one, CtClass two) {
+ return one == two || (one != null && two != null && one.getName().equals(two.getName()));
+ }
+
+ public static void aastore(TypeData array, TypeData value, ClassPool cp) throws BadBytecode {
+ if (array instanceof AbsTypeVar)
+ if (!value.isNullType())
+ ((AbsTypeVar)array).merge(ArrayType.make(value));
+
+ if (value instanceof AbsTypeVar)
+ if (array instanceof AbsTypeVar)
+ ArrayElement.make(array); // should call value.setType() later.
+ else if (array instanceof ClassName) {
+ if (!array.isNullType()) {
+ String type = ArrayElement.typeName(array.getName());
+ value.setType(type, cp);
+ }
+ }
+ else
+ throw new BadBytecode("bad AASTORE: " + array);
+ }
+
+ /* A type variable representing an array type.
+ * It is a decorator of another type variable.
*/
- public static class NullType extends ClassName {
- public NullType() {
- super("null"); // type name
+ public static class ArrayType extends AbsTypeVar {
+ private AbsTypeVar element;
+
+ private ArrayType(AbsTypeVar elementType) {
+ element = elementType;
}
- public TypeData copy() {
- return new NullType();
+ static TypeData make(TypeData element) throws BadBytecode {
+ if (element instanceof ArrayElement)
+ return ((ArrayElement)element).arrayType();
+ else if (element instanceof AbsTypeVar)
+ return new ArrayType((AbsTypeVar)element);
+ else if (element instanceof ClassName)
+ if (!element.isNullType())
+ return new ClassName(typeName(element.getName()));
+
+ throw new BadBytecode("bad AASTORE: " + element);
}
- public boolean isNullType() { return true; }
+ public void merge(TypeData t) {
+ try {
+ if (!t.isNullType())
+ element.merge(ArrayElement.make(t));
+ }
+ catch (BadBytecode e) {
+ // never happens
+ throw new RuntimeException("fatal: " + e);
+ }
+ }
- public int getTypeTag() {
- try {
- if ("null".equals(getExpected()))
- return StackMapTable.NULL;
- else
- return super.getTypeTag();
- }
- catch (BadBytecode e) {
- throw new RuntimeException("fatal error: ", e);
- }
+ public String getName() {
+ return typeName(element.getName());
}
- protected int getTypeData2(ConstPool cp, String type) {
- if ("null".equals(type))
- return 0;
+ public AbsTypeVar elementType() { return element; }
+
+ public BasicType isBasicType() { return null; }
+ public boolean is2WordType() { return false; }
+
+ /* elementType must be a class name. Basic type names
+ * are not allowed.
+ */
+ public static String typeName(String elementType) {
+ if (elementType.charAt(0) == '[')
+ return "[" + elementType;
else
- return super.getTypeData2(cp, type);
+ return "[L" + elementType.replace('.', '/') + ";";
}
- public String getExpected() throws BadBytecode {
- String en = expectedName;
- if (en == null) {
- // ArrayList equiv = equivalences;
- // if (equiv.size() == 1)
- // return getName();
- // else
- return "java.lang.Object";
- }
- else
- return en;
+ public void setType(String s, ClassPool cp) throws BadBytecode {
+ element.setType(ArrayElement.typeName(s), cp);
+ }
+
+ protected TypeVar toTypeVar() { return element.toTypeVar(); }
+
+ public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException {
+ return element.dfs(order, index, cp);
}
}
- /**
- * Type data for OBJECT if the type is an object type and is
- * derived as an element type from an array type by AALOAD.
- */
- public static class ArrayElement extends TypeName {
- TypeData array;
+ /* A type variable representing an array-element type.
+ * It is a decorator of another type variable.
+ */
+ public static class ArrayElement extends AbsTypeVar {
+ private AbsTypeVar array;
- public ArrayElement(TypeData a) { // a is never null
+ private ArrayElement(AbsTypeVar a) { // a is never null
array = a;
}
- public TypeData copy() {
- return new ArrayElement(array);
- }
+ public static TypeData make(TypeData array) throws BadBytecode {
+ if (array instanceof ArrayType)
+ return ((ArrayType)array).elementType();
+ else if (array instanceof AbsTypeVar)
+ return new ArrayElement((AbsTypeVar)array);
+ else if (array instanceof ClassName)
+ if (!array.isNullType())
+ return new ClassName(typeName(array.getName()));
- public boolean isNullType() {
- return array.isNullType();
+ throw new BadBytecode("bad AASTORE: " + array);
}
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- super.setType(typeName, cp);
- array.setType(getArrayType(typeName), cp);
+ public void merge(TypeData t) {
+ try {
+ if (!t.isNullType())
+ array.merge(ArrayType.make(t));
+ }
+ catch (BadBytecode e) {
+ // never happens
+ throw new RuntimeException("fatal: " + e);
+ }
}
- public String getName() throws BadBytecode {
- return getName2(array.getName());
+ public String getName() {
+ return typeName(array.getName());
}
- private String getName2(String name) throws BadBytecode {
- if (name.length() > 1 && name.charAt(0) == '[') {
- char c = name.charAt(1);
+ public AbsTypeVar arrayType() { return array; }
+
+ /* arrayType must be a class name. Basic type names are
+ * not allowed.
+ */
+
+ public BasicType isBasicType() { return null; }
+
+ public boolean is2WordType() { return false; }
+
+ private static String typeName(String arrayType) {
+ if (arrayType.length() > 1 && arrayType.charAt(0) == '[') {
+ char c = arrayType.charAt(1);
if (c == 'L')
- return name.substring(2, name.length() - 1).replace('/', '.');
+ return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');
else if (c == '[')
- return name.substring(1);
+ return arrayType.substring(1);
}
- if (array.isNullType())
- return "java.lang.Object";
- else
- throw new BadBytecode("bad array type for AALOAD: "
- + name);
+ return "java.lang.Object"; // the array type may be NullType
}
- public String getExpected() throws BadBytecode {
- ArrayList equiv = equivalences;
- if (equiv.size() == 1)
- return getName2(array.getExpected()); // not getName();
- else {
- String en = expectedName;
- if (en == null)
- return "java.lang.Object";
- else
- return en;
- }
+ public void setType(String s, ClassPool cp) throws BadBytecode {
+ array.setType(ArrayType.typeName(s), cp);
}
- public static String getArrayType(String elementType) {
- if (elementType.charAt(0) == '[')
- return "[" + elementType;
- else
- return "[L" + elementType.replace('.', '/') + ";";
+ protected TypeVar toTypeVar() { return array.toTypeVar(); }
+
+ public int dfs(ArrayList order, int index, ClassPool cp) throws NotFoundException {
+ return array.dfs(order, index, cp);
}
+ }
- public static String getElementType(String arrayType) {
- char c = arrayType.charAt(1);
- if (c == 'L')
- return arrayType.substring(2, arrayType.length() - 1).replace('/', '.');
- else if (c == '[')
- return arrayType.substring(1);
- else
- return arrayType;
+ /**
+ * Type data for OBJECT.
+ */
+ public static class ClassName extends TypeData {
+ private String name; // dot separated.
+
+ public ClassName(String n) {
+ name = n;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public BasicType isBasicType() { return null; }
+
+ public boolean is2WordType() { return false; }
+
+ public int getTypeTag() { return StackMapTable.OBJECT; }
+
+ public int getTypeData(ConstPool cp) {
+ return cp.addClassInfo(getName());
+ }
+
+ public boolean eq(TypeData d) { return name.equals(d.getName()); }
+
+ public void setType(String typeName, ClassPool cp) throws BadBytecode {}
+ }
+
+ /**
+ * Type data for NULL or OBJECT.
+ * The types represented by the instances of this class are
+ * initially NULL but will be OBJECT.
+ */
+ public static class NullType extends ClassName {
+ public NullType() {
+ super("null-type"); // type name
+ }
+
+ public int getTypeTag() {
+ return StackMapTable.NULL;
}
+
+ public boolean isNullType() { return true; }
+ public int getTypeData(ConstPool cp) { return 0; }
}
/**
* Type data for UNINIT.
*/
- public static class UninitData extends TypeData {
- String className;
+ public static class UninitData extends ClassName {
int offset;
boolean initialized;
UninitData(int offset, String className) {
- this.className = className;
+ super(className);
this.offset = offset;
this.initialized = false;
}
- public void merge(TypeData neighbor) {}
-
- public int getTypeTag() { return StackMapTable.UNINIT; }
- public int getTypeData(ConstPool cp) { return offset; }
-
- public boolean equals(Object obj) {
- if (obj instanceof UninitData) {
- UninitData ud = (UninitData)obj;
- return offset == ud.offset && className.equals(ud.className);
- }
- else
- return false;
+ public int getTypeTag() {
+ return StackMapTable.UNINIT;
}
- public TypeData getSelf() {
- if (initialized)
- return copy();
- else
- return this;
+ public int getTypeData(ConstPool cp) {
+ return offset;
}
- public TypeData copy() {
- return new ClassName(className);
+ public TypeData join() {
+ TypeData td = initialized ? new ClassName(getName()) : this;
+ return new TypeVar(td);
}
- public boolean isObjectType() { return true; }
+ public boolean isUninit() { return true; }
- protected void setType(String typeName, ClassPool cp) throws BadBytecode {
- initialized = true;
+ public boolean eq(TypeData d) {
+ if (d instanceof UninitData) {
+ UninitData ud = (UninitData)d;
+ return offset == ud.offset && getName().equals(ud.getName());
+ }
+ else
+ return false;
}
- public void evalExpectedType(ClassPool cp) throws BadBytecode {}
-
- public String getName() {
- return className;
+ public void setType(String typeName, ClassPool cp) throws BadBytecode {
+ super.setType(typeName, cp);
+ initialized = true;
}
- public String getExpected() { return className; }
-
- public String toString() { return "uninit:" + className + "@" + offset; }
+ public String toString() { return "uninit:" + getName() + "@" + offset; }
}
public static class UninitThis extends UninitData {
@@ -521,11 +712,12 @@ public abstract class TypeData {
super(-1, className);
}
- public int getTypeTag() { return StackMapTable.THIS; }
- public int getTypeData(ConstPool cp) { return 0; }
+ public int getTypeTag() {
+ return StackMapTable.THIS;
+ }
- public boolean equals(Object obj) {
- return obj instanceof UninitThis;
+ public int getTypeData(ConstPool cp) {
+ return 0;
}
public String toString() { return "uninit:this"; }
diff --git a/src/main/javassist/bytecode/stackmap/TypeTag.java b/src/main/javassist/bytecode/stackmap/TypeTag.java
index 963ecc18..2a90d147 100644
--- a/src/main/javassist/bytecode/stackmap/TypeTag.java
+++ b/src/main/javassist/bytecode/stackmap/TypeTag.java
@@ -19,7 +19,8 @@ package javassist.bytecode.stackmap;
import javassist.bytecode.StackMapTable;
public interface TypeTag {
- TypeData TOP = null;
+ String TOP_TYPE = "*top*";
+ TypeData TOP = new TypeData.BasicType(TOP_TYPE, StackMapTable.TOP);
TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER);
TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT);
TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE);
diff --git a/src/main/javassist/bytecode/stackmap/TypedBlock.java b/src/main/javassist/bytecode/stackmap/TypedBlock.java
index f589a6ee..5ecada13 100644
--- a/src/main/javassist/bytecode/stackmap/TypedBlock.java
+++ b/src/main/javassist/bytecode/stackmap/TypedBlock.java
@@ -20,16 +20,10 @@ import javassist.bytecode.*;
public class TypedBlock extends BasicBlock {
public int stackTop, numLocals;
- public TypeData[] stackTypes, localsTypes;
-
- // set by a Liveness object.
- // inputs[i] is true if the i-th variable is used within this block.
- public boolean[] inputs;
-
- // working area for Liveness class.
- public boolean updating;
- public int status;
- public byte[] localsUsage;
+ // localsTypes is set to non-null when this block is first visited by a MapMaker.
+ // see alreadySet().
+ public TypeData[] localsTypes;
+ public TypeData[] stackTypes;
/**
* Divides the method body into basic blocks.
@@ -52,16 +46,12 @@ public class TypedBlock extends BasicBlock {
blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
pool.getClassName(), minfo.getDescriptor(),
isStatic, minfo.isConstructor());
- new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(),
- blocks[0].localsTypes);
return blocks;
}
protected TypedBlock(int pos) {
super(pos);
localsTypes = null;
- inputs = null;
- updating = false;
}
protected void toString2(StringBuffer sbuf) {
@@ -70,11 +60,6 @@ public class TypedBlock extends BasicBlock {
printTypes(sbuf, stackTop, stackTypes);
sbuf.append("}, locals={");
printTypes(sbuf, numLocals, localsTypes);
- sbuf.append("}, inputs={");
- if (inputs != null)
- for (int i = 0; i < inputs.length; i++)
- sbuf.append(inputs[i] ? "1, " : "0, ");
-
sbuf.append('}');
}
@@ -111,10 +96,9 @@ public class TypedBlock extends BasicBlock {
public void resetNumLocals() {
if (localsTypes != null) {
int nl = localsTypes.length;
- while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) {
+ while (nl > 0 && localsTypes[nl - 1].isBasicType() == TypeTag.TOP) {
if (nl > 1) {
- TypeData td = localsTypes[nl - 2];
- if (td == TypeTag.LONG || td == TypeTag.DOUBLE)
+ if (localsTypes[nl - 2].is2WordType())
break;
}
@@ -153,8 +137,8 @@ public class TypedBlock extends BasicBlock {
throw new BadBytecode("no method descriptor: " + methodDesc);
stackTop = 0;
- stackTypes = new TypeData[maxStack];
- TypeData[] locals = new TypeData[maxLocals];
+ stackTypes = TypeData.make(maxStack);
+ TypeData[] locals = TypeData.make(maxLocals);
if (isConstructor)
locals[0] = new TypeData.UninitThis(className);
else if (!isStatic)
diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java
index 16d91390..4e22dbc1 100644
--- a/src/test/javassist/JvstTest.java
+++ b/src/test/javassist/JvstTest.java
@@ -1110,7 +1110,7 @@ public class JvstTest extends JvstTestRoot {
suite.addTestSuite(JvstTest4.class);
suite.addTestSuite(LoaderTestByRandall.class);
suite.addTestSuite(javassist.bytecode.BytecodeTest.class);
- // suite.addTestSuite(javassist.bytecode.StackMapTest.class);
+ suite.addTestSuite(javassist.bytecode.StackMapTest.class);
suite.addTestSuite(javassist.compiler.CompTest.class);
suite.addTestSuite(javassist.SetterTest.class);
suite.addTestSuite(javassist.bytecode.InsertGap0.class);
diff --git a/src/test/javassist/JvstTest4.java b/src/test/javassist/JvstTest4.java
index f06d79f3..3ff6927d 100644
--- a/src/test/javassist/JvstTest4.java
+++ b/src/test/javassist/JvstTest4.java
@@ -683,9 +683,10 @@ public class JvstTest4 extends JvstTestRoot {
}
}
System.gc();
+ System.gc();
int size = javassist.compiler.MemberResolver.getInvalidMapSize();
System.out.println("JIRA150b " + size);
- assertTrue(size < N - 10);
+ assertTrue("JIRA150b size: " + size, size < N - 10);
}
public void testJIRA152() throws Exception {
diff --git a/src/test/javassist/bytecode/StackMapTest.java b/src/test/javassist/bytecode/StackMapTest.java
new file mode 100644
index 00000000..bd21a899
--- /dev/null
+++ b/src/test/javassist/bytecode/StackMapTest.java
@@ -0,0 +1,315 @@
+package javassist.bytecode;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.CtNewMethod;
+import javassist.JvstTest;
+import javassist.Loader;
+import javassist.bytecode.stackmap.MapMaker;
+import javassist.bytecode.stackmap.TypeData;
+import junit.framework.TestCase;
+
+public class StackMapTest extends TestCase {
+ public static final String PATH = JvstTest.PATH;
+ private ClassPool loader, dloader;
+ private Loader cloader;
+
+ public StackMapTest(String name) { super(name); }
+
+ protected void setUp() throws Exception {
+ loader = ClassPool.getDefault();
+ dloader = new ClassPool(null);
+ dloader.appendSystemPath();
+ dloader.insertClassPath(".");
+ cloader = new Loader(dloader);
+ }
+
+ protected Object make(String name) throws Exception {
+ return cloader.loadClass(name).newInstance();
+ }
+
+ protected int invoke(Object target, String method) throws Exception {
+ Method m = target.getClass().getMethod(method, new Class[0]);
+ Object res = m.invoke(target, new Object[0]);
+ return ((Integer)res).intValue();
+ }
+
+ protected static void rebuildStackMaps(CtClass cc) throws BadBytecode, IOException {
+ ClassPool cp = cc.getClassPool();
+ Iterator it = cc.getClassFile().getMethods().iterator();
+ while (it.hasNext()) {
+ MethodInfo minfo = (MethodInfo)it.next();
+ rebuildStackMap(cc, cp, minfo);
+ }
+ }
+
+ protected static void rebuildStackMap(CtClass cc, ClassPool cp, MethodInfo minfo) throws BadBytecode, IOException {
+ CodeAttribute ca = minfo.getCodeAttribute();
+ if (ca != null) {
+ StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
+ if (smt != null) {
+ String data = readSmt(smt);
+ StackMapTable smt2 = MapMaker.make(cp, minfo);
+ String data2 = readSmt(smt2);
+ try {
+ assertEquals(cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor(),
+ data, data2);
+ }
+ catch (junit.framework.ComparisonFailure e) {
+ System.out.println("*** " + cc.getName() + ":" + minfo.getName() + ":" + minfo.getDescriptor());
+ smt.println(System.out);
+ System.out.println("---");
+ smt2.println(System.out);
+ }
+ }
+ }
+ }
+
+ protected static void rebuildStackMaps2(CtClass cc) throws BadBytecode {
+ ClassPool cp = cc.getClassPool();
+ Iterator it = cc.getClassFile().getMethods().iterator();
+ while (it.hasNext()) {
+ MethodInfo minfo = (MethodInfo)it.next();
+ minfo.rebuildStackMap(cp);
+ }
+ }
+
+ protected static String readSmt(StackMapTable smt) throws BadBytecode, IOException {
+ if (smt == null)
+ return "";
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintStream pout = new PrintStream(out);
+ smt.println(pout);
+ pout.close();
+ return out.toString();
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 2 && args[0].equals("-c")) {
+ CtClass cc = ClassPool.getDefault().get(args[0].replace('/', '.'));
+ rebuildStackMaps(cc);
+ }
+ else {
+ for (int i = 0; i < args.length; i++) {
+ CtClass cc = ClassPool.getDefault().get(getClassName(args[i]));
+ System.out.println(cc.getName());
+ rebuildStackMaps2(cc);
+ cc.writeFile("rebuild");
+ }
+ }
+ }
+
+ public static String getClassName(String fileName) {
+ Matcher m = Pattern.compile("(.*)\\.class").matcher(fileName);
+ if (m.matches())
+ return m.group(1).replace('/', '.');
+ else
+ return fileName;
+ }
+
+ public void testCommonSuper() throws Exception {
+ CtClass type = loader.get("javassist.CtClassType[]");
+ CtClass base = loader.get("javassist.CtClass[]");
+ CtClass array = loader.get("javassist.CtArray[]");
+ CtClass array2 = loader.get("javassist.CtArray[][]");
+ CtClass str = loader.get("java.lang.String[]");
+ CtClass strObj = loader.get("java.lang.String");
+ CtClass obj = loader.get("java.lang.Object[]");
+ CtClass objObj = loader.get("java.lang.Object");
+
+ assertEquals(base, TypeData.commonSuperClassEx(type, base));
+ assertEquals(base, TypeData.commonSuperClassEx(base, type));
+ assertEquals(base, TypeData.commonSuperClassEx(array, type));
+ assertEquals(obj, TypeData.commonSuperClassEx(base, str));
+ assertEquals(objObj, TypeData.commonSuperClassEx(strObj, str));
+ assertEquals(obj, TypeData.commonSuperClassEx(array, array2));
+ assertEquals(obj, TypeData.commonSuperClassEx(base, array2));
+ }
+
+ public void testRebuild() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T1");
+ rebuildStackMaps2(cc);
+ //Class c = cc.toClass();
+ //Object t1 = c.newInstance();
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(3, invoke(t1, "test"));
+ }
+
+ public static interface Intf {
+ int foo();
+ }
+
+ public static class C1 implements Intf {
+ public int foo() { return 0; }
+ }
+
+ public static class C2 implements Intf {
+ public int foo() { return 3; }
+ }
+
+ public static class T1 {
+ public int test() {
+ return foo(-1);
+ }
+
+ public int foo(int i) {
+ Intf obj;
+ if (i > 0)
+ obj = new C1();
+ else
+ obj = new C2();
+
+ return obj.foo();
+ }
+ }
+
+ public void testRebuild2() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$C3");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(7, invoke(t1, "test"));
+ }
+
+ public static class C3 {
+ int value;
+ public C3(int i) {
+ value = i;
+ }
+ public C3(boolean b) {
+ this(b ? 7 : 10);
+ }
+ public C3() { this(true); }
+ public int test() { return value; }
+ }
+
+ public void testRebuild3() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T3");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(1100, invoke(t1, "test"));
+ }
+
+ public static interface Intf2 {
+ int bar();
+ }
+
+ public static class D1 extends C1 implements Intf2 {
+ public int bar() { return 10; }
+ }
+
+ public static class D2 extends C1 implements Intf2 {
+ public int bar() { return 100; }
+ }
+
+ public static class T3 {
+ public int bar(Intf2 i) { return 1000; }
+
+ public int test() {
+ return foo(-1);
+ }
+
+ public int foo(int i) {
+ Intf2 obj;
+ if (i > 0)
+ obj = new D1();
+ else
+ obj = new D2();
+
+ System.out.println(obj.toString());
+ return obj.bar() + bar(obj);
+ }
+ }
+
+ public void testRebuildArray() throws Exception {
+ CtClass cc = loader.get("javassist.bytecode.StackMapTest$T4");
+ rebuildStackMaps2(cc);
+ cc.writeFile();
+ Object t1 = make(cc.getName());
+ assertEquals(30, invoke(t1, "test"));
+ }
+
+ public static class T4 {
+ public int test() {
+ return foo(3) + foo2(3) + foo3(3) + foo4(3);
+ }
+
+ public int foo(int i) {
+ C1[] a;
+ if (i > 0)
+ a = new D1[1];
+ else
+ a = new D2[1];
+
+ if (i > 0)
+ a[0] = new D1();
+ else
+ a[0] = new D2();
+
+ return a[0].foo();
+ }
+
+ public int foo2(int i) {
+ Intf2[] a;
+ if (i > 0)
+ a = new D1[1];
+ else
+ a = new D2[1];
+
+ if (i > 0)
+ a[0] = new D1();
+ else
+ a[0] = new D2();
+
+ return a[0].bar();
+ }
+
+ public int foo3(int i) {
+ Intf2[] a = null;
+ if (i > 0)
+ a = new D1[1];
+
+ a[0] = new D1();
+ return a[0].bar();
+ }
+
+ public int foo4(int i) {
+ Intf2[] a = new Intf2[1];
+ if (i > 0)
+ a[0] = new D1();
+ return a[0].bar();
+ }
+ }
+
+ public void tstCtClassType() throws Exception {
+ ClassPool cp = ClassPool.getDefault();
+ CtClass cc = cp.get("javassist.CtClassType");
+ MethodInfo minfo = getMethodInfo(cc.getClassFile(), "getFields", "(Ljava/util/ArrayList;Ljavassist/CtClass;)V");
+ rebuildStackMap(cc, cp, minfo);
+ }
+
+ MethodInfo getMethodInfo(ClassFile cf, String name, String desc) {
+ List list = cf.getMethods();
+ Iterator it = list.iterator();
+ while (it.hasNext()) {
+ MethodInfo mi = (MethodInfo)it.next();
+ if (mi.getName().equals(name) && mi.getDescriptor().equals(desc))
+ return mi;
+ }
+
+ return null;
+ }
+}
diff --git a/src/test/javassist/tools/reflect/LoaderTest.java b/src/test/javassist/tools/reflect/LoaderTest.java
index d11a38a8..f96feb7b 100644
--- a/src/test/javassist/tools/reflect/LoaderTest.java
+++ b/src/test/javassist/tools/reflect/LoaderTest.java
@@ -53,6 +53,10 @@ public class LoaderTest extends TestCase {
"javassist.tools.reflect.ClassMetaobject");
ClassPool cp = ClassPool.getDefault();
+
+ CtClass cc2 = cp.get("javassist.tools.reflect.SuperClass");
+ cc2.debugWriteFile("reflected/");
+
CtClass cc = cp.get("javassist.tools.reflect.SubClass");
CtMethod[] ms = cc.getMethods();