Browse Source

fixed JASSIST-207

tags/rel_3_19_0_ga
chibash 10 years ago
parent
commit
e5492bf606

+ 1
- 1
Readme.html View File



<p>-version 3.19 <p>-version 3.19
<ul> <ul>
<li>JIRA JASSIST-206.
<li>JIRA JASSIST-158, 206.
</ul> </ul>
</p> </p>



+ 9
- 4
src/main/javassist/bytecode/stackmap/BasicBlock.java View File

sbuf.append("}"); sbuf.append("}");
} }


/**
* A Mark indicates the position of a branch instruction
* or a branch target.
*/
static class Mark implements Comparable { static class Mark implements Comparable {
int position; int position;
BasicBlock block; BasicBlock block;
} }
else { else {
// the previous mark already has exits. // the previous mark already has exits.
int prevPos = prev.position;
if (prevPos + prev.length < m.position) {
prev = makeBlock(prevPos + prev.length);
prev.length = m.position - prevPos;
if (prev.position + prev.length < m.position) {
// dead code is found.
prev = makeBlock(prev.position + prev.length);
blocks.add(prev);
prev.length = m.position - prev.position;
// the incoming flow from dead code is not counted // the incoming flow from dead code is not counted
// bb.incoming++; // bb.incoming++;
prev.exit = makeArray(bb); prev.exit = makeArray(bb);

+ 41
- 28
src/main/javassist/bytecode/stackmap/MapMaker.java View File

// Phase 1.5 // Phase 1.5


/* /*
* Javac may generate an exception handler that catches only an exception
* Javac may generate an exception handler that catches only the exception
* thrown within the handler itself. It is dead code. * thrown within the handler itself. It is dead code.
* See javassist.JvstTest4.testJIRA195().
*/ */


private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode { private void findDeadCatchers(byte[] code, TypedBlock[] blocks) throws BadBytecode {
int len = blocks.length; int len = blocks.length;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
TypedBlock block = blocks[i]; TypedBlock block = blocks[i];
if (block.localsTypes == null) { // if block is dead code
if (!block.alreadySet()) {
fixDeadcode(code, block);
BasicBlock.Catch handler = block.toCatch; BasicBlock.Catch handler = block.toCatch;
while (handler != null)
if (handler.body == block) {
BasicBlock.Catch handler2
= new BasicBlock.Catch(block, handler.typeIndex, null);
traceException(code, handler2);
break;
if (handler != null) {
TypedBlock tb = (TypedBlock)handler.body;
if (!tb.alreadySet()) {
// tb is a handler that catches only the exceptions
// thrown from dead code.
recordStackMap(tb, handler.typeIndex);
fixDeadcode(code, tb);
tb.incoming = 1;
} }
else
handler = handler.next;
}
} }
} }
} }


private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode {
int pos = block.position;
int len = block.length - 3;
if (len < 0) {
// if the dead-code length is shorter than 3 bytes.
if (len == -1)
code[pos] = Bytecode.NOP;

code[pos + block.length - 1] = (byte)Bytecode.ATHROW;
block.incoming = 1;
recordStackMap(block, 0);
return;
}

// if block.incomping > 0, all the incoming edges are from
// other dead code blocks. So set block.incoming to 0.
block.incoming = 0;

for (int k = 0; k < len; k++)
code[pos + k] = Bytecode.NOP;

code[pos + len] = (byte)Bytecode.GOTO;
ByteArray.write16bit(-len, code, pos + len + 1);
}

// Phase 2 // Phase 2


/* /*
* This method first finds strongly connected components (SCCs) * This method first finds strongly connected components (SCCs)
* on a graph made by TypeData by Tarjan's algorithm.
* in a TypeData graph by Tarjan's algorithm.
* SCCs are TypeData nodes sharing the same type. * SCCs are TypeData nodes sharing the same type.
* Since SCCs are found in the topologically sorted order, * Since SCCs are found in the topologically sorted order,
* their types are also fixed when they are found. * their types are also fixed when they are found.
int index = 0; int index = 0;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
TypedBlock block = blocks[i]; TypedBlock block = blocks[i];
if (block.localsTypes == null) // if block is dead code
fixDeadcode(code, block);
else {
if (block.alreadySet()) { // if block is not dead code
int n = block.localsTypes.length; int n = block.localsTypes.length;
for (int j = 0; j < n; j++) for (int j = 0; j < n; j++)
index = block.localsTypes[j].dfs(preOrder, index, classPool); index = block.localsTypes[j].dfs(preOrder, index, classPool);
} }
} }


private void fixDeadcode(byte[] code, TypedBlock block) throws BadBytecode {
int pos = block.position;
int len = block.length - 3;
if (len < 0)
throw new BadBytecode("dead code detected at " + pos
+ ". No stackmap table generated.");

for (int k = 0; k < len; k++)
code[pos + k] = Bytecode.NOP;

code[pos + len] = (byte)Bytecode.GOTO;
ByteArray.write16bit(-len, code, pos + len + 1);
}

// Phase 3 // Phase 3


public StackMapTable toStackMap(TypedBlock[] blocks) { public StackMapTable toStackMap(TypedBlock[] blocks) {

+ 1
- 1
src/main/javassist/bytecode/stackmap/Tracer.java View File

return doOpcode148_201(pos, code, op); return doOpcode148_201(pos, code, op);
} }
catch (ArrayIndexOutOfBoundsException e) { catch (ArrayIndexOutOfBoundsException e) {
throw new BadBytecode("inconsistent stack height " + e.getMessage());
throw new BadBytecode("inconsistent stack height " + e.getMessage(), e);
} }
} }



+ 16
- 0
src/test/Test.java View File

import javassist.*;

public class Test {
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();
// ClassPool cp = new ClassPool();
cp.insertClassPath("./target/test-classes");
CtClass cc = cp.get("test4.JIRA207");
// cc.getClassFile().setMajorVersion(javassist.bytecode.ClassFile.JAVA_4);
CtMethod cm = cc.getDeclaredMethod("foo");
cm.insertBefore("throw new Exception();");
CtMethod cm2 = cc.getDeclaredMethod("run2");
cm2.insertBefore("throw new Exception();");
cc.writeFile();
}
}

+ 16
- 0
src/test/javassist/JvstTest4.java View File

Object obj = make(cc.getName()); Object obj = make(cc.getName());
assertEquals(15, invoke(obj, "run")); assertEquals(15, invoke(obj, "run"));
} }

public void testJIRA207() throws Exception {
CtClass cc = sloader.get("test4.JIRA207");
CtMethod cm = cc.getDeclaredMethod("foo");
cm.insertBefore("throw new Exception();");

CtMethod cm2 = cc.getDeclaredMethod("run2");
cm2.insertBefore("throw new Exception();");
cc.writeFile();
Object obj = make(cc.getName());
try {
invoke(obj, "run2");
fail("run2");
}
catch (Exception e) {}
}
} }

+ 9
- 11
src/test/javassist/bytecode/StackMapTest.java View File



public void testJIRA175b() throws Exception { public void testJIRA175b() throws Exception {
CtClass cc = loader.get("javassist.bytecode.StackMapTest$C6"); CtClass cc = loader.get("javassist.bytecode.StackMapTest$C6");
try {
cc.getDeclaredMethod("setter").instrument(new javassist.expr.ExprEditor() {
public void edit(javassist.expr.FieldAccess f) throws javassist.CannotCompileException {
if (!f.where().getMethodInfo().isMethod())
return;
cc.getDeclaredMethod("setter").instrument(new javassist.expr.ExprEditor() {
public void edit(javassist.expr.FieldAccess f) throws javassist.CannotCompileException {
if (!f.where().getMethodInfo().isMethod())
return;


f.replace("{ $_ = $proceed($$); return $_;}");
}
});
fail("deadcode detection");
}
catch (javassist.CannotCompileException e) {}
// this will make a 1-byte dead code.
f.replace("{ $_ = $proceed($$); return $_;}");
}
});
cc.writeFile();
} }


public static class C6 { public static class C6 {

+ 51
- 0
src/test/test4/JIRA207.java View File

package test4;

public class JIRA207 {
public int run() {
int i = 3;
return foo(i);
}

public int foo(int i) {
int k = i + 3;
if (k > 0)
return k * k;
else
return k;
}

public int run2() {
int i = 0;
int p = i;
int q = p;
int r = q;
for (int k = 1; k < 3; ++k)
p += k;

for (int k = 3; k > 0; --k)
try {
foo(k);
p++;
}
finally {
p++;
}

try {
foo(p);
}
catch (RuntimeException e) {
if (p > 0)
throw e;
}

switch (p) {
case 1:
p = 100;
break;
default :
++p;
}
return p + r;
}
}

Loading…
Cancel
Save