From 394e733b1c161a1b140cd08e31739e2016ee5457 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 1998 18:59:11 +0000 Subject: [PATCH] *** empty log message *** git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@115 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/bytecode/Opcodes.java | 12 +- jode/jode/decompiler/ClassAnalyzer.java | 2 +- jode/jode/decompiler/ImportHandler.java | 159 ++++++++++-- jode/jode/expr/ConstOperator.java | 8 +- jode/jode/flow/CaseBlock.java | 42 +++- jode/jode/flow/CreateForInitializer.java | 11 +- jode/jode/flow/FlowBlock.java | 1 - jode/jode/flow/LoopBlock.java | 63 ++--- .../jode/flow/TransformExceptionHandlers.java | 227 ++++++++++++------ jode/jode/type/ClassInterfacesType.java | 4 +- jode/jode/type/Type.java | 8 +- 11 files changed, 390 insertions(+), 147 deletions(-) diff --git a/jode/jode/bytecode/Opcodes.java b/jode/jode/bytecode/Opcodes.java index 60b7638..b24e031 100644 --- a/jode/jode/bytecode/Opcodes.java +++ b/jode/jode/bytecode/Opcodes.java @@ -475,7 +475,7 @@ public abstract class Opcodes { return createNormal (ca, addr, 1, new ConstOperator (FLOAT_TYPE, - Integer.toString(opcode - opc_fconst_0) + ".0F")); + Integer.toString(opcode - opc_fconst_0) + ".0")); case opc_dconst_0: case opc_dconst_1: return createNormal (ca, addr, 1, new ConstOperator @@ -485,11 +485,15 @@ public abstract class Opcodes { return createNormal (ca, addr, 2, new ConstOperator (ALL_INT_TYPE, Integer.toString(stream.readByte()))); - case opc_sipush: + case opc_sipush: { + short value = stream.readShort(); return createNormal (ca, addr, 3, new ConstOperator - (Type.tRange(Type.tInt, Type.tChar), - Integer.toString(stream.readShort()))); + ((value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) + /* yes javac codes -128 with sipush :-( */ + ? Type.tRange(Type.tInt, Type.tChar) : ALL_INT_TYPE, + Integer.toString(value))); + } case opc_ldc: { int index = stream.readUnsignedByte(); return createNormal diff --git a/jode/jode/decompiler/ClassAnalyzer.java b/jode/jode/decompiler/ClassAnalyzer.java index e25876c..7381560 100644 --- a/jode/jode/decompiler/ClassAnalyzer.java +++ b/jode/jode/decompiler/ClassAnalyzer.java @@ -97,7 +97,7 @@ public class ClassAnalyzer implements Analyzer { } Class[] interfaces = clazz.getInterfaces(); if (interfaces.length > 0) { - writer.print("implements "); + writer.print(clazz.isInterface() ? "extends " : "implements "); for (int i=0; i < interfaces.length; i++) { if (i > 0) writer.print(", "); diff --git a/jode/jode/decompiler/ImportHandler.java b/jode/jode/decompiler/ImportHandler.java index b3bef6a..d0e04bb 100644 --- a/jode/jode/decompiler/ImportHandler.java +++ b/jode/jode/decompiler/ImportHandler.java @@ -21,7 +21,9 @@ package jode; import java.util.*; public class JodeEnvironment { - Hashtable imports = new Hashtable(); + Hashtable imports; + /* Classes that doesn't need to be qualified. */ + Hashtable goodClasses = new Hashtable(); ClassAnalyzer main; String className; String pkg; @@ -31,6 +33,9 @@ public class JodeEnvironment { JodeEnvironment() { Type.setEnvironment(this); 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) @@ -40,13 +45,61 @@ public class JodeEnvironment { +".class"); } - public 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 \n */"); - if (pkg.length() != 0) - writer.println("package "+pkg+";"); + /** + * Checks if the className conflicts with a class imported from + * another package and must be fully qualified therefore. + * The imports must should have been cleaned up before. + *

+ * 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(); while (enum.hasMoreElements()) { String importName = (String) enum.nextElement(); @@ -59,12 +112,47 @@ public class JodeEnvironment { imports.get(importName.substring(0, delim)+".*"); if (pkgvote.intValue() >= Decompiler.importPackageLimit) continue; - + + /* This is a single Class import. Mark it for importation, + * but don't put it in newImports, yet. + */ + classImports.addElement(importName); } else { if (vote.intValue() < Decompiler.importPackageLimit) 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 \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(""); } @@ -109,8 +197,7 @@ public class JodeEnvironment { /* Marks the clazz as used, so that it will be imported if used often * enough. */ - public void useClass(Class clazz) { - String name = clazz.getName(); + public void useClass(String name) { int pkgdelim = name.lastIndexOf('.'); if (pkgdelim != -1) { String pkgName = name.substring(0, pkgdelim); @@ -118,9 +205,13 @@ public class JodeEnvironment { || pkgName.equals("java.lang")) return; 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+".*"); + if (i != null && i.intValue() >= Decompiler.importPackageLimit) + return; i = (i == null)? new Integer(1): new Integer(i.intValue()+1); imports.put(pkgName+".*", i); if (i.intValue() >= Decompiler.importPackageLimit) @@ -128,39 +219,61 @@ public class JodeEnvironment { i = new Integer(1); - } else + } else { + if (i.intValue() >= Decompiler.importClassLimit) + return; i = new Integer(i.intValue()+1); + } 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 * full qualified class name. *

- * Known Bug: If the same class name is in more than one imported package - * the name should be qualified, but isn't. + * Known Bug 1: If this is called before the imports are cleaned up, + * (that is only for debugging messages), the result is unpredictable. + *

+ * 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. */ - public String classString(Class clazz) { - String name = clazz.getName(); + public String classString(String name) { int pkgdelim = name.lastIndexOf('.'); if (pkgdelim != -1) { + /* First look in our cache. */ + if (goodClasses.get(name) != null) + return name.substring(pkgdelim+1); + String pkgName = name.substring(0, pkgdelim); Integer i; - if (pkgName.equals(pkg) - || pkgName.equals("java.lang") - || ( (i = (Integer)imports.get(pkgName+".*")) != null - && i.intValue() >= Decompiler.importPackageLimit ) - || ( (i = (Integer)imports.get(name)) != null - && i.intValue() >= Decompiler.importClassLimit )) { + if (pkgName.equals(pkg) + || (( imports.get(pkgName+".*") != null + || imports.get(name) != null) + && !conflictsImport(name))) { + goodClasses.put(name, name); return name.substring(pkgdelim+1); } } return name; } + public String classString(Class clazz) { + return classString(clazz.getName()); + } + protected int loadFileFlags() { return 1; diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java index e04cc0b..b6d352c 100644 --- a/jode/jode/expr/ConstOperator.java +++ b/jode/jode/expr/ConstOperator.java @@ -92,12 +92,16 @@ public class ConstOperator extends NoArgOperator { } else if (type.equals(Type.tLong)) { long l = Long.parseLong(value); if (l < -1) - return "~0x"+Long.toHexString(-l-1); + return "~0x"+Long.toHexString(-l-1)+"L"; 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; } } diff --git a/jode/jode/flow/CaseBlock.java b/jode/jode/flow/CaseBlock.java index d506227..1e41162 100644 --- a/jode/jode/flow/CaseBlock.java +++ b/jode/jode/flow/CaseBlock.java @@ -47,6 +47,16 @@ public class CaseBlock extends StructuredBlock { */ 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) { this.value = value; subBlock = null; @@ -79,6 +89,31 @@ public class CaseBlock extends StructuredBlock { 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. */ @@ -96,18 +131,21 @@ public class CaseBlock extends StructuredBlock { && subBlock instanceof EmptyBlock && subBlock.jump == null) return; - writer.println("default:"); + writer.println("default:" + (wantBraces ? " {" : "")); } else { ConstOperator constOp = new ConstOperator (((SwitchBlock)outer).getInstruction().getType(), Integer.toString(value)); - writer.println("case " + constOp.toString()+":"); + writer.println("case " + constOp.toString() + ":" + + (wantBraces ? " {" : "")); } if (subBlock != null) { writer.tab(); subBlock.dumpSource(writer); writer.untab(); } + if (wantBraces) + writer.println("}"); } /** diff --git a/jode/jode/flow/CreateForInitializer.java b/jode/jode/flow/CreateForInitializer.java index 293cfec..af6f45b 100644 --- a/jode/jode/flow/CreateForInitializer.java +++ b/jode/jode/flow/CreateForInitializer.java @@ -38,19 +38,16 @@ public class CreateForInitializer { if (!(sequBlock.subBlocks[0] instanceof InstructionBlock)) return false; - Expression initializer = - ((InstructionBlock) sequBlock.subBlocks[0]).getInstruction(); + InstructionBlock init = (InstructionBlock) sequBlock.subBlocks[0]; - if (!initializer.getOperator().isVoid() - || (forBlock.cond != forBlock.TRUE - && !forBlock.cond.containsMatchingLoad(initializer))) + if (!init.getInstruction().isVoid() + || !forBlock.conditionMatches(init)) return false; if (jode.Decompiler.isVerbose) System.err.print('f'); - forBlock.init = (InstructionBlock) sequBlock.subBlocks[0]; - last.replace(sequBlock); + forBlock.setInit((InstructionBlock) sequBlock.subBlocks[0]); return true; } } diff --git a/jode/jode/flow/FlowBlock.java b/jode/jode/flow/FlowBlock.java index 36bbc3f..2eb000a 100644 --- a/jode/jode/flow/FlowBlock.java +++ b/jode/jode/flow/FlowBlock.java @@ -799,7 +799,6 @@ public class FlowBlock { forBlock.replace(bodyBlock); forBlock.setBody(bodyBlock); forBlock.incr = (InstructionBlock) lastModified; - lastModified.removeBlock(); createdForBlock = true; } diff --git a/jode/jode/flow/LoopBlock.java b/jode/jode/flow/LoopBlock.java index 4555cd4..cef3803 100644 --- a/jode/jode/flow/LoopBlock.java +++ b/jode/jode/flow/LoopBlock.java @@ -95,45 +95,42 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { body.setFlowBlock(flowBlock); } - public Expression getCondition() { - return cond; + public void setInit(InstructionBlock init) { + this.init = init; + if (type == FOR) + init.removeBlock(); } - public void putBackInit() { - StructuredBlock last = - (outer instanceof SequentialBlock - && outer.getSubBlocks()[0] == this) ? outer : this; + public boolean conditionMatches(InstructionBlock instr) { + return (type == POSSFOR || + cond.containsMatchingLoad(instr.getInstruction())); + } - SequentialBlock sequBlock = new SequentialBlock(); - sequBlock.replace(last); - sequBlock.setFirst(init); - sequBlock.setSecond(last); - init = null; + + public Expression getCondition() { + return cond; } public void setCondition(Expression cond) { this.cond = cond; if (type == POSSFOR) { - /* canCombine returns 1 if cond contains a sub expression - * that matches the store in incr */ + /* We can now say, if this is a for block or not. + */ if (cond.containsMatchingLoad(incr.getInstruction())) { type = FOR; - if (init != null - && !cond.containsMatchingLoad(init.getInstruction())) { - /* This is a for, but the init instruction doesn't - * match. Put the init back to its old place. - */ - putBackInit(); + incr.removeBlock(); + if (init != null) { + if (cond.containsMatchingLoad(init.getInstruction())) + init.removeBlock(); + else + init = null; } } 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; - StructuredBlock last = bodyBlock; - while (last instanceof SequentialBlock) - last = last.getSubBlocks()[1]; - last.appendBlock(incr); - incr = null; - if (init != null) - putBackInit(); + init = incr = null; } } mayChangeJump = false; @@ -148,9 +145,9 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { } public VariableSet propagateUsage() { - if (init != null) + if (type == FOR && init != null) used.unionExact(init.used); - if (incr != null) + if (type == FOR && incr != null) used.unionExact(incr.used); VariableSet allUse = (VariableSet) used.clone(); allUse.unionExact(bodyBlock.propagateUsage()); @@ -211,6 +208,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { } boolean needBrace = bodyBlock.needsBraces(); switch (type) { + case POSSFOR: + /* a possible for is now treated like a WHILE */ case WHILE: if (cond == TRUE) /* special syntax for endless loops: */ @@ -222,7 +221,6 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { writer.print("do"); break; case FOR: - case POSSFOR: writer.print("for ("); if (init != null) { if (isDeclaration) @@ -231,7 +229,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock { .getLocalInfo().getType().toString() + " "); writer.print(init.getInstruction().simplify().toString()); - } + } else + writer.print("/**/"); writer.print("; "+cond.simplify().toString()+"; " +incr.getInstruction().simplify().toString()+")"); 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) { java.util.Stack todo = new java.util.Stack(); diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java index edfadb1..aae856f 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -209,23 +209,28 @@ public class TransformExceptionHandlers { * This transforms a sub routine, that is checks if the beginning * local assignment matches the final ret and then returns. */ - boolean transformSubRoutine(FlowBlock subRoutine) { - try { - SequentialBlock sequBlock = (SequentialBlock) subRoutine.block; - LocalStoreOperator store = (LocalStoreOperator) - ((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator(); - while (sequBlock.subBlocks[1] instanceof SequentialBlock) - sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; - RetBlock retBlock = (RetBlock)sequBlock.subBlocks[1]; - if (! retBlock.local.equals(store.getLocalInfo())) - /* Ret doesn't match */ - return false; - subRoutine.block.getSubBlocks()[0].removeBlock(); - retBlock.removeBlock(); - return true; - } catch (ClassCastException ex) { + boolean transformSubRoutine(StructuredBlock subRoutine) { + if (!(subRoutine instanceof SequentialBlock) + || !(subRoutine.getSubBlocks()[0] instanceof InstructionBlock)) return false; - } + 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) + sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; + + if (! (sequBlock.subBlocks[1] instanceof RetBlock) + || !(((RetBlock)sequBlock.subBlocks[1]) + .local.equals(store.getLocalInfo()))) + return false; + + instr.removeBlock(); + sequBlock.subBlocks[1].removeBlock(); + return true; } /** @@ -351,7 +356,10 @@ public class TransformExceptionHandlers { /* Now we have a jump with a wrong destination. * 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); @@ -386,7 +394,7 @@ public class TransformExceptionHandlers { subRoutine = jumps.destination; subRoutine.analyze(startMonExit, endMonExit); - transformSubRoutine(subRoutine); + transformSubRoutine(subRoutine.block); if (subRoutine.block instanceof InstructionBlock) { Expression instr = @@ -466,7 +474,10 @@ public class TransformExceptionHandlers { /* Now we have a jump that is not preceded by a monitorexit. * Complain! */ - System.err.println("non well formed synchronized block"); + DescriptionBlock msg + = new DescriptionBlock("ERROR: NO MONITOREXIT"); + prev.appendBlock(msg); + msg.moveJump(jumps); } } @@ -548,72 +559,144 @@ public class TransformExceptionHandlers { private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow, int end) { - if (!(catchFlow.block instanceof SequentialBlock - && catchFlow.block.getSubBlocks()[0] - instanceof InstructionBlock)) + + /* Layout of a try-finally block: + * + * tryFlow: + * |- first instruction + * | ... + * | every jump to outside is preceded by jsr finally + * | ... + * | jsr finally -----------------, + * `- jump after finally | + * | + * catchFlow: (already checked) | + * local_n = stack v + * jsr finally ---------------->| + * throw local_n; | + * finally: <-----------------------' + * astore_n + * ... + * return_n + */ + + if (!(catchFlow.block instanceof SequentialBlock) + || !(catchFlow.block.getSubBlocks()[0] + 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(); - - if (catchBlock.subBlocks[1] instanceof SequentialBlock - && catchBlock.subBlocks[1].getSubBlocks()[0] - instanceof JsrBlock + catchBlock = (SequentialBlock)catchBlock.subBlocks[1]; + + if (catchBlock.subBlocks[0] instanceof LoopBlock) { + /* In case the try block has no exit (that means, it throws + * 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; + */ + LoopBlock doWhileFalse = (LoopBlock)catchBlock.subBlocks[0]; + if (doWhileFalse.type == LoopBlock.DOWHILE + && 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.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)) { + && 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 an try finally block: + * exactly like it should: * - * tryFlow: - * |- first instruction - * | ... - * | every jump to outside is preceded by jsr finally - * | ... - * | jsr finally -----------------, - * `- jump after finally | - * | - * catchFlow: (already checked) | - * local_n = stack v - * jsr finally ---------------->| - * throw local_n; | - * finally: <-----------------------' - * astore_n - * ... - * return_n + * catchBlock: + * JSR + * finally + * throw local_n <- matches the local in instr. */ - FlowBlock subRoutine = - ((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0]) - .innerBlock.jump.destination; + 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; - /* Now remove the two jumps of the catch block - * so that we can forget about them. - * This are the jsr and the throw. - */ - catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0] - .jump.destination.predecessors.removeElement(catchFlow); - catchBlock.subBlocks[1].getSubBlocks()[1] - .jump.destination.predecessors.removeElement(catchFlow); + 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); + + /* 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); + if (!transformSubRoutine(subRoutine.block)) + return false; - subRoutine.analyze(catchFlow.addr+catchFlow.length, end); - if (!transformSubRoutine(subRoutine)) - return false; + tryFlow.length += catchFlow.length; - updateInOutCatch(tryFlow, subRoutine); + checkAndRemoveJSR(tryFlow, subRoutine); - tryFlow.length += catchFlow.length; - 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; if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { @@ -625,10 +708,8 @@ public class TransformExceptionHandlers { tryFlow.lastModified = innerTry; } FinallyBlock newBlock = new FinallyBlock(); - newBlock.setCatchBlock(subRoutine.block); + newBlock.setCatchBlock(finallyBlock); tryBlock.addCatchBlock(newBlock); - tryFlow.mergeSuccessors(subRoutine); - tryFlow.length += subRoutine.length; return true; } return false; @@ -660,7 +741,7 @@ public class TransformExceptionHandlers { Object key = keys.nextElement(); if (key == succ) continue; - if (key != tryFlow.END_OF_METHOD) { + if (key != FlowBlock.END_OF_METHOD) { /* There is another exit in the try block, bad */ return false; } diff --git a/jode/jode/type/ClassInterfacesType.java b/jode/jode/type/ClassInterfacesType.java index 557d5f5..cb5c192 100644 --- a/jode/jode/type/ClassInterfacesType.java +++ b/jode/jode/type/ClassInterfacesType.java @@ -209,7 +209,7 @@ public class ClassInterfacesType extends Type { int code = type.typecode; if (code == TC_UNKNOWN) return this; - if (code == TC_ARRAY && this == tObject) + if ((code == TC_ARRAY || code == TC_UCLASS) && this == tObject) return type; if (code != TC_CLASS) return tError; @@ -297,7 +297,7 @@ public class ClassInterfacesType extends Type { int code = type.typecode; if (code == TC_UNKNOWN) return this; - if (code == TC_ARRAY) + if (code == TC_ARRAY || code == TC_UCLASS) return tObject; if (code != TC_CLASS) return tError; diff --git a/jode/jode/type/Type.java b/jode/jode/type/Type.java index 9a3e9f4..eb49364 100644 --- a/jode/jode/type/Type.java +++ b/jode/jode/type/Type.java @@ -85,6 +85,7 @@ public class Type { public static final int TC_RANGE = 103; public static final int TC_BOOLBYTE = 105; public static final int TC_BOOLINT = 106; + public static final int TC_UCLASS = 107; protected static JodeEnvironment env; @@ -165,7 +166,12 @@ public class Type { clazzname = clazzname.replace(java.io.File.separatorChar, '.'); Object result = classHash.get(clazzname); if (result == null) { - result = new ClassInterfacesType(clazzname); + try { + Class clazz = Class.forName(clazzname); + result = new ClassInterfacesType(clazzname); + } catch (ClassNotFoundException ex) { + result = new UnfoundClassType(clazzname); + } classHash.put(clazzname, result); } return (Type) result;