*** empty log message ***

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@115 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent acaa45ed8d
commit 394e733b1c
  1. 12
      jode/jode/bytecode/Opcodes.java
  2. 2
      jode/jode/decompiler/ClassAnalyzer.java
  3. 155
      jode/jode/decompiler/ImportHandler.java
  4. 8
      jode/jode/expr/ConstOperator.java
  5. 42
      jode/jode/flow/CaseBlock.java
  6. 11
      jode/jode/flow/CreateForInitializer.java
  7. 1
      jode/jode/flow/FlowBlock.java
  8. 63
      jode/jode/flow/LoopBlock.java
  9. 195
      jode/jode/flow/TransformExceptionHandlers.java
  10. 4
      jode/jode/type/ClassInterfacesType.java
  11. 6
      jode/jode/type/Type.java

@ -475,7 +475,7 @@ public abstract class Opcodes {
return createNormal return createNormal
(ca, addr, 1, new ConstOperator (ca, addr, 1, new ConstOperator
(FLOAT_TYPE, (FLOAT_TYPE,
Integer.toString(opcode - opc_fconst_0) + ".0F")); Integer.toString(opcode - opc_fconst_0) + ".0"));
case opc_dconst_0: case opc_dconst_1: case opc_dconst_0: case opc_dconst_1:
return createNormal return createNormal
(ca, addr, 1, new ConstOperator (ca, addr, 1, new ConstOperator
@ -485,11 +485,15 @@ public abstract class Opcodes {
return createNormal return createNormal
(ca, addr, 2, new ConstOperator (ca, addr, 2, new ConstOperator
(ALL_INT_TYPE, Integer.toString(stream.readByte()))); (ALL_INT_TYPE, Integer.toString(stream.readByte())));
case opc_sipush: case opc_sipush: {
short value = stream.readShort();
return createNormal return createNormal
(ca, addr, 3, new ConstOperator (ca, addr, 3, new ConstOperator
(Type.tRange(Type.tInt, Type.tChar), ((value < Byte.MIN_VALUE || value > Byte.MAX_VALUE)
Integer.toString(stream.readShort()))); /* yes javac codes -128 with sipush :-( */
? Type.tRange(Type.tInt, Type.tChar) : ALL_INT_TYPE,
Integer.toString(value)));
}
case opc_ldc: { case opc_ldc: {
int index = stream.readUnsignedByte(); int index = stream.readUnsignedByte();
return createNormal return createNormal

@ -97,7 +97,7 @@ public class ClassAnalyzer implements Analyzer {
} }
Class[] interfaces = clazz.getInterfaces(); Class[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) { if (interfaces.length > 0) {
writer.print("implements "); writer.print(clazz.isInterface() ? "extends " : "implements ");
for (int i=0; i < interfaces.length; i++) { for (int i=0; i < interfaces.length; i++) {
if (i > 0) if (i > 0)
writer.print(", "); writer.print(", ");

@ -21,7 +21,9 @@ package jode;
import java.util.*; import java.util.*;
public class JodeEnvironment { public class JodeEnvironment {
Hashtable imports = new Hashtable(); Hashtable imports;
/* Classes that doesn't need to be qualified. */
Hashtable goodClasses = new Hashtable();
ClassAnalyzer main; ClassAnalyzer main;
String className; String className;
String pkg; String pkg;
@ -31,6 +33,9 @@ public class JodeEnvironment {
JodeEnvironment() { JodeEnvironment() {
Type.setEnvironment(this); Type.setEnvironment(this);
classPath = new SearchPath(System.getProperty("java.class.path")); classPath = new SearchPath(System.getProperty("java.class.path"));
imports = new Hashtable();
/* java.lang is always imported */
imports.put("java.lang.*", new Integer(Integer.MAX_VALUE));
} }
public java.io.InputStream getClassStream(Class clazz) public java.io.InputStream getClassStream(Class clazz)
@ -40,13 +45,61 @@ public class JodeEnvironment {
+".class"); +".class");
} }
public void dumpHeader(TabbedPrintWriter writer) /**
throws java.io.IOException * Checks if the className conflicts with a class imported from
{ * another package and must be fully qualified therefore.
writer.println("/* "+ className + " - Decompiled by JoDe (Jochen's Decompiler)\n * Send comments or bug reports to Jochen Hoenicke <jochenh@bigfoot.com>\n */"); * The imports must should have been cleaned up before.
if (pkg.length() != 0) * <p>
writer.println("package "+pkg+";"); * Known Bug: If a class, local, field or method with the same
* name as the package of className exists, using the fully
* qualified name is no solution. This sometimes can't be fixed
* at all (except by renaming the package). It happens only in
* ambigous contexts, namely static field/method access.
* @param name The full qualified class name.
* @return true if this className must be printed fully qualified.
*/
private boolean conflictsImport(String name) {
int pkgdelim = name.lastIndexOf('.');
if (pkgdelim != -1) {
String pkgName = name.substring(0, pkgdelim);
/* All classes in this package doesn't conflict */
if (pkgName.equals(pkg))
return false;
name = name.substring(pkgdelim+1);
Enumeration enum = imports.keys();
while (enum.hasMoreElements()) {
String importName = (String) enum.nextElement();
if (importName.endsWith(".*")) {
/* strip the "*" */
importName = importName.substring
(0, importName.length()-2);
if (!importName.equals(pkgName)) {
String checkName = importName + "." + name;
try {
Class.forName(checkName);
/* UGLY: If class doesn't conflict, above
* Instruction throws an exception and we
* doesn't reach here.
* XXX - Is there a better way to do it ???
*/
return true;
} catch (ClassNotFoundException ex) {
/* BTW: Exception generation is slow. I'm
* really sad that this is the default.
*/
}
}
}
}
}
return false;
}
private void cleanUpImports() {
Integer dummyVote = new Integer(Integer.MAX_VALUE);
Hashtable newImports = new Hashtable();
Vector classImports = new Vector();
Enumeration enum = imports.keys(); Enumeration enum = imports.keys();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
String importName = (String) enum.nextElement(); String importName = (String) enum.nextElement();
@ -60,11 +113,46 @@ public class JodeEnvironment {
if (pkgvote.intValue() >= Decompiler.importPackageLimit) if (pkgvote.intValue() >= Decompiler.importPackageLimit)
continue; continue;
/* This is a single Class import. Mark it for importation,
* but don't put it in newImports, yet.
*/
classImports.addElement(importName);
} else { } else {
if (vote.intValue() < Decompiler.importPackageLimit) if (vote.intValue() < Decompiler.importPackageLimit)
continue; continue;
} }
writer.println("import "+importName+";"); newImports.put(importName, dummyVote);
}
imports = newImports;
/* Now check if the class import conflict with any of the
* package imports.
*/
enum = classImports.elements();
while (enum.hasMoreElements()) {
/* If there are more than one single class imports with
* the same name, exactly the first (in hash order) will
* be imported. */
String className = (String) enum.nextElement();
if (!conflictsImport(className))
imports.put(className, dummyVote);
}
}
private void dumpHeader(TabbedPrintWriter writer)
throws java.io.IOException
{
writer.println("/* "+ className + " - Decompiled by JoDe (Jochen's Decompiler)\n * Send comments or bug reports to Jochen Hoenicke <jochenh@bigfoot.com>\n */");
if (pkg.length() != 0)
writer.println("package "+pkg+";");
cleanUpImports();
Enumeration enum = imports.keys();
while (enum.hasMoreElements()) {
String pkgName = (String)enum.nextElement();
if (!pkgName.equals("java.lang.*"))
writer.println("import "+pkgName+";");
} }
writer.println(""); writer.println("");
} }
@ -109,8 +197,7 @@ public class JodeEnvironment {
/* Marks the clazz as used, so that it will be imported if used often /* Marks the clazz as used, so that it will be imported if used often
* enough. * enough.
*/ */
public void useClass(Class clazz) { public void useClass(String name) {
String name = clazz.getName();
int pkgdelim = name.lastIndexOf('.'); int pkgdelim = name.lastIndexOf('.');
if (pkgdelim != -1) { if (pkgdelim != -1) {
String pkgName = name.substring(0, pkgdelim); String pkgName = name.substring(0, pkgdelim);
@ -118,9 +205,13 @@ public class JodeEnvironment {
|| pkgName.equals("java.lang")) || pkgName.equals("java.lang"))
return; return;
Integer i = (Integer) imports.get(name); Integer i = (Integer) imports.get(name);
if (i== null) { if (i == null) {
/* This class wasn't imported before. Mark the package
* as used. */
i = (Integer) imports.get(pkgName+".*"); i = (Integer) imports.get(pkgName+".*");
if (i != null && i.intValue() >= Decompiler.importPackageLimit)
return;
i = (i == null)? new Integer(1): new Integer(i.intValue()+1); i = (i == null)? new Integer(1): new Integer(i.intValue()+1);
imports.put(pkgName+".*", i); imports.put(pkgName+".*", i);
if (i.intValue() >= Decompiler.importPackageLimit) if (i.intValue() >= Decompiler.importPackageLimit)
@ -128,39 +219,61 @@ public class JodeEnvironment {
i = new Integer(1); i = new Integer(1);
} else } else {
if (i.intValue() >= Decompiler.importClassLimit)
return;
i = new Integer(i.intValue()+1); i = new Integer(i.intValue()+1);
}
imports.put(name, i); imports.put(name, i);
} }
} }
/* Marks the clazz as used, so that it will be imported if used often
* enough.
*/
public void useClass(Class clazz) {
useClass(clazz.getName());
}
/** /**
* Check if clazz is imported and maybe remove package delimiter from * Check if clazz is imported and maybe remove package delimiter from
* full qualified class name. * full qualified class name.
* <p> * <p>
* Known Bug: If the same class name is in more than one imported package * Known Bug 1: If this is called before the imports are cleaned up,
* the name should be qualified, but isn't. * (that is only for debugging messages), the result is unpredictable.
* <p>
* Known Bug 2: It is not checked if the class name conflicts with
* a local variable, field or method name. This is very unlikely
* since the java standard has different naming convention for those
* names. (But maybe a intelligent obfuscator may use this fact.)
* This can only happen with static fields or static methods.
* @return a legal string representation of clazz. * @return a legal string representation of clazz.
*/ */
public String classString(Class clazz) { public String classString(String name) {
String name = clazz.getName();
int pkgdelim = name.lastIndexOf('.'); int pkgdelim = name.lastIndexOf('.');
if (pkgdelim != -1) { if (pkgdelim != -1) {
/* First look in our cache. */
if (goodClasses.get(name) != null)
return name.substring(pkgdelim+1);
String pkgName = name.substring(0, pkgdelim); String pkgName = name.substring(0, pkgdelim);
Integer i; Integer i;
if (pkgName.equals(pkg) if (pkgName.equals(pkg)
|| pkgName.equals("java.lang") || (( imports.get(pkgName+".*") != null
|| ( (i = (Integer)imports.get(pkgName+".*")) != null || imports.get(name) != null)
&& i.intValue() >= Decompiler.importPackageLimit ) && !conflictsImport(name))) {
|| ( (i = (Integer)imports.get(name)) != null goodClasses.put(name, name);
&& i.intValue() >= Decompiler.importClassLimit )) {
return name.substring(pkgdelim+1); return name.substring(pkgdelim+1);
} }
} }
return name; return name;
} }
public String classString(Class clazz) {
return classString(clazz.getName());
}
protected int loadFileFlags() protected int loadFileFlags()
{ {
return 1; return 1;

@ -92,12 +92,16 @@ public class ConstOperator extends NoArgOperator {
} else if (type.equals(Type.tLong)) { } else if (type.equals(Type.tLong)) {
long l = Long.parseLong(value); long l = Long.parseLong(value);
if (l < -1) if (l < -1)
return "~0x"+Long.toHexString(-l-1); return "~0x"+Long.toHexString(-l-1)+"L";
else else
return "0x"+Long.toHexString(l); return "0x"+Long.toHexString(l)+"L";
} }
} }
} }
if (type.isOfType(Type.tLong))
return value+"L";
if (type.isOfType(Type.tFloat))
return value+"F";
return value; return value;
} }
} }

@ -47,6 +47,16 @@ public class CaseBlock extends StructuredBlock {
*/ */
boolean isLastBlock; boolean isLastBlock;
/**
* All variables used somewhere inside this block.
*/
VariableSet allUsed;
/**
* Do we want braces around the case block (if a sub block
* declares a variable).
*/
boolean wantBraces;
public CaseBlock(int value) { public CaseBlock(int value) {
this.value = value; this.value = value;
subBlock = null; subBlock = null;
@ -79,6 +89,31 @@ public class CaseBlock extends StructuredBlock {
return true; return true;
} }
public VariableSet propagateUsage() {
/* We remember if this sub block uses some variables, to introduce
* braces around this block in that case.
*/
return (allUsed = super.propagateUsage());
}
/**
* Make the declarations, i.e. initialize the declare variable
* to correct values. This will declare every variable that
* is marked as used, but not done.
* @param done The set of the already declare variables.
*/
public void makeDeclaration(VariableSet done) {
java.util.Enumeration enum = allUsed.elements();
while (enum.hasMoreElements()) {
jode.LocalInfo li = (jode.LocalInfo) enum.nextElement();
if (!done.contains(li)) {
wantBraces = true;
break;
}
}
super.makeDeclaration(done);
}
/** /**
* Returns all sub block of this structured block. * Returns all sub block of this structured block.
*/ */
@ -96,18 +131,21 @@ public class CaseBlock extends StructuredBlock {
&& subBlock instanceof EmptyBlock && subBlock instanceof EmptyBlock
&& subBlock.jump == null) && subBlock.jump == null)
return; return;
writer.println("default:"); writer.println("default:" + (wantBraces ? " {" : ""));
} else { } else {
ConstOperator constOp = new ConstOperator ConstOperator constOp = new ConstOperator
(((SwitchBlock)outer).getInstruction().getType(), (((SwitchBlock)outer).getInstruction().getType(),
Integer.toString(value)); Integer.toString(value));
writer.println("case " + constOp.toString()+":"); writer.println("case " + constOp.toString() + ":"
+ (wantBraces ? " {" : ""));
} }
if (subBlock != null) { if (subBlock != null) {
writer.tab(); writer.tab();
subBlock.dumpSource(writer); subBlock.dumpSource(writer);
writer.untab(); writer.untab();
} }
if (wantBraces)
writer.println("}");
} }
/** /**

@ -38,19 +38,16 @@ public class CreateForInitializer {
if (!(sequBlock.subBlocks[0] instanceof InstructionBlock)) if (!(sequBlock.subBlocks[0] instanceof InstructionBlock))
return false; return false;
Expression initializer = InstructionBlock init = (InstructionBlock) sequBlock.subBlocks[0];
((InstructionBlock) sequBlock.subBlocks[0]).getInstruction();
if (!initializer.getOperator().isVoid() if (!init.getInstruction().isVoid()
|| (forBlock.cond != forBlock.TRUE || !forBlock.conditionMatches(init))
&& !forBlock.cond.containsMatchingLoad(initializer)))
return false; return false;
if (jode.Decompiler.isVerbose) if (jode.Decompiler.isVerbose)
System.err.print('f'); System.err.print('f');
forBlock.init = (InstructionBlock) sequBlock.subBlocks[0]; forBlock.setInit((InstructionBlock) sequBlock.subBlocks[0]);
last.replace(sequBlock);
return true; return true;
} }
} }

@ -799,7 +799,6 @@ public class FlowBlock {
forBlock.replace(bodyBlock); forBlock.replace(bodyBlock);
forBlock.setBody(bodyBlock); forBlock.setBody(bodyBlock);
forBlock.incr = (InstructionBlock) lastModified; forBlock.incr = (InstructionBlock) lastModified;
lastModified.removeBlock();
createdForBlock = true; createdForBlock = true;
} }

@ -95,45 +95,42 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
body.setFlowBlock(flowBlock); body.setFlowBlock(flowBlock);
} }
public Expression getCondition() { public void setInit(InstructionBlock init) {
return cond; this.init = init;
if (type == FOR)
init.removeBlock();
} }
public void putBackInit() { public boolean conditionMatches(InstructionBlock instr) {
StructuredBlock last = return (type == POSSFOR ||
(outer instanceof SequentialBlock cond.containsMatchingLoad(instr.getInstruction()));
&& outer.getSubBlocks()[0] == this) ? outer : this; }
SequentialBlock sequBlock = new SequentialBlock();
sequBlock.replace(last); public Expression getCondition() {
sequBlock.setFirst(init); return cond;
sequBlock.setSecond(last);
init = null;
} }
public void setCondition(Expression cond) { public void setCondition(Expression cond) {
this.cond = cond; this.cond = cond;
if (type == POSSFOR) { if (type == POSSFOR) {
/* canCombine returns 1 if cond contains a sub expression /* We can now say, if this is a for block or not.
* that matches the store in incr */ */
if (cond.containsMatchingLoad(incr.getInstruction())) { if (cond.containsMatchingLoad(incr.getInstruction())) {
type = FOR; type = FOR;
if (init != null incr.removeBlock();
&& !cond.containsMatchingLoad(init.getInstruction())) { if (init != null) {
/* This is a for, but the init instruction doesn't if (cond.containsMatchingLoad(init.getInstruction()))
* match. Put the init back to its old place. init.removeBlock();
*/ else
putBackInit(); init = null;
} }
} else { } else {
/* This is not a for block, as it seems first. Make
* it a while block again, and forget about init and
* incr. */
type = WHILE; type = WHILE;
StructuredBlock last = bodyBlock; init = incr = null;
while (last instanceof SequentialBlock)
last = last.getSubBlocks()[1];
last.appendBlock(incr);
incr = null;
if (init != null)
putBackInit();
} }
} }
mayChangeJump = false; mayChangeJump = false;
@ -148,9 +145,9 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
} }
public VariableSet propagateUsage() { public VariableSet propagateUsage() {
if (init != null) if (type == FOR && init != null)
used.unionExact(init.used); used.unionExact(init.used);
if (incr != null) if (type == FOR && incr != null)
used.unionExact(incr.used); used.unionExact(incr.used);
VariableSet allUse = (VariableSet) used.clone(); VariableSet allUse = (VariableSet) used.clone();
allUse.unionExact(bodyBlock.propagateUsage()); allUse.unionExact(bodyBlock.propagateUsage());
@ -211,6 +208,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
} }
boolean needBrace = bodyBlock.needsBraces(); boolean needBrace = bodyBlock.needsBraces();
switch (type) { switch (type) {
case POSSFOR:
/* a possible for is now treated like a WHILE */
case WHILE: case WHILE:
if (cond == TRUE) if (cond == TRUE)
/* special syntax for endless loops: */ /* special syntax for endless loops: */
@ -222,7 +221,6 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
writer.print("do"); writer.print("do");
break; break;
case FOR: case FOR:
case POSSFOR:
writer.print("for ("); writer.print("for (");
if (init != null) { if (init != null) {
if (isDeclaration) if (isDeclaration)
@ -231,7 +229,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
.getLocalInfo().getType().toString() .getLocalInfo().getType().toString()
+ " "); + " ");
writer.print(init.getInstruction().simplify().toString()); writer.print(init.getInstruction().simplify().toString());
} } else
writer.print("/**/");
writer.print("; "+cond.simplify().toString()+"; " writer.print("; "+cond.simplify().toString()+"; "
+incr.getInstruction().simplify().toString()+")"); +incr.getInstruction().simplify().toString()+")");
break; break;
@ -278,7 +277,9 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
} }
/** /**
* Replace all breaks to this block with a continue to this block. * Replace all breaks to block with a continue to this.
* @param block the breakable block where the breaks originally
* breaked to (Have a break now, if you didn't understand that :-).
*/ */
public void replaceBreakContinue(BreakableBlock block) { public void replaceBreakContinue(BreakableBlock block) {
java.util.Stack todo = new java.util.Stack(); java.util.Stack todo = new java.util.Stack();

@ -209,23 +209,28 @@ public class TransformExceptionHandlers {
* This transforms a sub routine, that is checks if the beginning * This transforms a sub routine, that is checks if the beginning
* local assignment matches the final ret and then returns. * local assignment matches the final ret and then returns.
*/ */
boolean transformSubRoutine(FlowBlock subRoutine) { boolean transformSubRoutine(StructuredBlock subRoutine) {
try { if (!(subRoutine instanceof SequentialBlock)
SequentialBlock sequBlock = (SequentialBlock) subRoutine.block; || !(subRoutine.getSubBlocks()[0] instanceof InstructionBlock))
LocalStoreOperator store = (LocalStoreOperator) return false;
((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator(); SequentialBlock sequBlock = (SequentialBlock) subRoutine;
InstructionBlock instr = (InstructionBlock)sequBlock.subBlocks[0];
if (! (instr.getInstruction() instanceof LocalStoreOperator))
return false;
LocalStoreOperator store = (LocalStoreOperator) instr.getInstruction();
while (sequBlock.subBlocks[1] instanceof SequentialBlock) while (sequBlock.subBlocks[1] instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; sequBlock = (SequentialBlock) sequBlock.subBlocks[1];
RetBlock retBlock = (RetBlock)sequBlock.subBlocks[1];
if (! retBlock.local.equals(store.getLocalInfo())) if (! (sequBlock.subBlocks[1] instanceof RetBlock)
/* Ret doesn't match */ || !(((RetBlock)sequBlock.subBlocks[1])
.local.equals(store.getLocalInfo())))
return false; return false;
subRoutine.block.getSubBlocks()[0].removeBlock();
retBlock.removeBlock(); instr.removeBlock();
sequBlock.subBlocks[1].removeBlock();
return true; return true;
} catch (ClassCastException ex) {
return false;
}
} }
/** /**
@ -351,7 +356,10 @@ public class TransformExceptionHandlers {
/* Now we have a jump with a wrong destination. /* Now we have a jump with a wrong destination.
* Complain! * Complain!
*/ */
System.err.println("non well formed try-finally block"); DescriptionBlock msg
= new DescriptionBlock("ERROR: NO JSR TO FINALLY");
prev.appendBlock(msg);
msg.moveJump(jumps);
} }
} }
removeJSR(tryFlow, subRoutine); removeJSR(tryFlow, subRoutine);
@ -386,7 +394,7 @@ public class TransformExceptionHandlers {
subRoutine = jumps.destination; subRoutine = jumps.destination;
subRoutine.analyze(startMonExit, endMonExit); subRoutine.analyze(startMonExit, endMonExit);
transformSubRoutine(subRoutine); transformSubRoutine(subRoutine.block);
if (subRoutine.block instanceof InstructionBlock) { if (subRoutine.block instanceof InstructionBlock) {
Expression instr = Expression instr =
@ -466,7 +474,10 @@ public class TransformExceptionHandlers {
/* Now we have a jump that is not preceded by a monitorexit. /* Now we have a jump that is not preceded by a monitorexit.
* Complain! * Complain!
*/ */
System.err.println("non well formed synchronized block"); DescriptionBlock msg
= new DescriptionBlock("ERROR: NO MONITOREXIT");
prev.appendBlock(msg);
msg.moveJump(jumps);
} }
} }
@ -548,32 +559,8 @@ public class TransformExceptionHandlers {
private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow, private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow,
int end) { int end) {
if (!(catchFlow.block instanceof SequentialBlock
&& catchFlow.block.getSubBlocks()[0]
instanceof InstructionBlock))
return false;
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
Expression instr =
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
if (catchBlock.subBlocks[1] instanceof SequentialBlock
&& catchBlock.subBlocks[1].getSubBlocks()[0]
instanceof JsrBlock
&& instr instanceof LocalStoreOperator
&& catchBlock.subBlocks[1].getSubBlocks()[1]
instanceof ThrowBlock
&& ((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr
instanceof LocalLoadOperator
&& ((LocalStoreOperator) instr)
.matches((LocalLoadOperator)
((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr)) {
/* Wow that was complicated :-) /* Layout of a try-finally block:
* But now we know that the catch block looks
* exactly like an try finally block:
* *
* tryFlow: * tryFlow:
* |- first instruction * |- first instruction
@ -593,28 +580,124 @@ public class TransformExceptionHandlers {
* return_n * return_n
*/ */
FlowBlock subRoutine = if (!(catchFlow.block instanceof SequentialBlock)
((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0]) || !(catchFlow.block.getSubBlocks()[0]
.innerBlock.jump.destination; instanceof InstructionBlock)
|| !(catchFlow.block.getSubBlocks()[1]
instanceof SequentialBlock))
return false;
StructuredBlock finallyBlock = null;
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
Expression instr =
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
catchBlock = (SequentialBlock)catchBlock.subBlocks[1];
/* Now remove the two jumps of the catch block if (catchBlock.subBlocks[0] instanceof LoopBlock) {
* so that we can forget about them. /* In case the try block has no exit (that means, it throws
* This are the jsr and the throw. * an exception), the finallyBlock was already merged with
* the catchBlock. We have to check for this case separately:
*
* do {
* JSR
* break;
* throw local_x
* } while(false);
* finallyBlock;
*/ */
catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0] LoopBlock doWhileFalse = (LoopBlock)catchBlock.subBlocks[0];
.jump.destination.predecessors.removeElement(catchFlow); if (doWhileFalse.type == LoopBlock.DOWHILE
catchBlock.subBlocks[1].getSubBlocks()[1] && doWhileFalse.cond == LoopBlock.FALSE
&& doWhileFalse.bodyBlock instanceof SequentialBlock) {
finallyBlock = catchBlock.subBlocks[1];
catchBlock = (SequentialBlock) doWhileFalse.bodyBlock;
}
}
if (catchBlock instanceof SequentialBlock
&& catchBlock.getSubBlocks()[0] instanceof JsrBlock
&& instr instanceof LocalStoreOperator
&& catchBlock.getSubBlocks()[1] instanceof ThrowBlock
&& (((ThrowBlock)catchBlock.getSubBlocks()[1]).instr
instanceof LocalLoadOperator)
&& (((LocalStoreOperator) instr).matches
((LocalLoadOperator)
((ThrowBlock)catchBlock.getSubBlocks()[1]).instr))) {
/* Wow that was complicated :-)
* But now we know that the catch block looks
* exactly like it should:
*
* catchBlock:
* JSR
* finally
* throw local_n <- matches the local in instr.
*/
if (finallyBlock != null) {
/* Check if the jsr breaks (see two comments above). We don't
* need to check if it breaks to the right block, because
* we know that there is only one Block around the jsr.
*/
if (!(((JsrBlock)catchBlock.getSubBlocks()[0]).innerBlock
instanceof BreakBlock))
return false;
/* Check if the try block has no exit (except throws)
*/
Jump throwJumps = (Jump)
tryFlow.successors.get(FlowBlock.END_OF_METHOD);
if (tryFlow.successors.size() > 1
|| (tryFlow.successors.size() > 0 && throwJumps == null))
return false;
for (/**/; throwJumps != null; throwJumps = throwJumps.next) {
if (!(throwJumps.prev instanceof ThrowBlock))
/* There is a return exit in the try block */
return false;
}
/* Remove the jump of the throw instruction.
*/
catchBlock.getSubBlocks()[1]
.jump.destination.predecessors.removeElement(catchFlow); .jump.destination.predecessors.removeElement(catchFlow);
/* Replace the catchBlock with the finallyBlock.
*/
finallyBlock.replace(catchFlow.block);
transformSubRoutine(finallyBlock);
updateInOutCatch(tryFlow, catchFlow);
tryFlow.length += catchFlow.length;
finallyBlock = catchFlow.block;
tryFlow.mergeSuccessors(catchFlow);
} else {
FlowBlock subRoutine =
((JsrBlock)catchBlock.getSubBlocks()[0])
.innerBlock.jump.destination;
subRoutine.analyze(catchFlow.addr+catchFlow.length, end); subRoutine.analyze(catchFlow.addr+catchFlow.length, end);
if (!transformSubRoutine(subRoutine)) if (!transformSubRoutine(subRoutine.block))
return false; return false;
updateInOutCatch(tryFlow, subRoutine);
tryFlow.length += catchFlow.length; tryFlow.length += catchFlow.length;
checkAndRemoveJSR(tryFlow, subRoutine); checkAndRemoveJSR(tryFlow, subRoutine);
updateInOutCatch(tryFlow, subRoutine);
tryFlow.length += subRoutine.length;
tryFlow.mergeSuccessors(subRoutine);
finallyBlock = subRoutine.block;
/* Now remove the jump to the JSR from the catch block
* and the jump of the throw instruction.
*/
catchBlock.getSubBlocks()[0].getSubBlocks()[0]
.jump.destination.predecessors.removeElement(catchFlow);
catchBlock.getSubBlocks()[1]
.jump.destination.predecessors.removeElement(catchFlow);
}
TryBlock tryBlock = (TryBlock)tryFlow.block; TryBlock tryBlock = (TryBlock)tryFlow.block;
if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
/* remove the nested tryBlock */ /* remove the nested tryBlock */
@ -625,10 +708,8 @@ public class TransformExceptionHandlers {
tryFlow.lastModified = innerTry; tryFlow.lastModified = innerTry;
} }
FinallyBlock newBlock = new FinallyBlock(); FinallyBlock newBlock = new FinallyBlock();
newBlock.setCatchBlock(subRoutine.block); newBlock.setCatchBlock(finallyBlock);
tryBlock.addCatchBlock(newBlock); tryBlock.addCatchBlock(newBlock);
tryFlow.mergeSuccessors(subRoutine);
tryFlow.length += subRoutine.length;
return true; return true;
} }
return false; return false;
@ -660,7 +741,7 @@ public class TransformExceptionHandlers {
Object key = keys.nextElement(); Object key = keys.nextElement();
if (key == succ) if (key == succ)
continue; continue;
if (key != tryFlow.END_OF_METHOD) { if (key != FlowBlock.END_OF_METHOD) {
/* There is another exit in the try block, bad */ /* There is another exit in the try block, bad */
return false; return false;
} }

@ -209,7 +209,7 @@ public class ClassInterfacesType extends Type {
int code = type.typecode; int code = type.typecode;
if (code == TC_UNKNOWN) if (code == TC_UNKNOWN)
return this; return this;
if (code == TC_ARRAY && this == tObject) if ((code == TC_ARRAY || code == TC_UCLASS) && this == tObject)
return type; return type;
if (code != TC_CLASS) if (code != TC_CLASS)
return tError; return tError;
@ -297,7 +297,7 @@ public class ClassInterfacesType extends Type {
int code = type.typecode; int code = type.typecode;
if (code == TC_UNKNOWN) if (code == TC_UNKNOWN)
return this; return this;
if (code == TC_ARRAY) if (code == TC_ARRAY || code == TC_UCLASS)
return tObject; return tObject;
if (code != TC_CLASS) if (code != TC_CLASS)
return tError; return tError;

@ -85,6 +85,7 @@ public class Type {
public static final int TC_RANGE = 103; public static final int TC_RANGE = 103;
public static final int TC_BOOLBYTE = 105; public static final int TC_BOOLBYTE = 105;
public static final int TC_BOOLINT = 106; public static final int TC_BOOLINT = 106;
public static final int TC_UCLASS = 107;
protected static JodeEnvironment env; protected static JodeEnvironment env;
@ -165,7 +166,12 @@ public class Type {
clazzname = clazzname.replace(java.io.File.separatorChar, '.'); clazzname = clazzname.replace(java.io.File.separatorChar, '.');
Object result = classHash.get(clazzname); Object result = classHash.get(clazzname);
if (result == null) { if (result == null) {
try {
Class clazz = Class.forName(clazzname);
result = new ClassInterfacesType(clazzname); result = new ClassInterfacesType(clazzname);
} catch (ClassNotFoundException ex) {
result = new UnfoundClassType(clazzname);
}
classHash.put(clazzname, result); classHash.put(clazzname, result);
} }
return (Type) result; return (Type) result;

Loading…
Cancel
Save