git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@655 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -1,8 +1,9 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<classpath> | |||
<classpathentry excluding="javassist/util/HotSwapper.java" kind="src" path="src/main"/> | |||
<classpathentry kind="src" path="src/test"/> | |||
<classpathentry excluding=".svn" kind="src" path="src/main"/> | |||
<classpathentry excluding=".svn" kind="src" path="src/test"/> | |||
<classpathentry kind="lib" path="lib/junit.jar"/> | |||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> | |||
<classpathentry kind="lib" path="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar"/> | |||
<classpathentry kind="output" path="eclipse-output/classes"/> | |||
</classpath> |
@@ -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); | |||
} | |||
} | |||
/** |
@@ -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); | |||
} | |||
@@ -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) { |
@@ -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; | |||
} | |||
@@ -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) | |||
*/ |
@@ -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]; | |||
@@ -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) { |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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++; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); |
@@ -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) |
@@ -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); |
@@ -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 { |
@@ -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; | |||
} | |||
} |
@@ -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(); |