diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java index b1a7286..eff49f4 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java @@ -856,6 +856,13 @@ class BasicBlockReader implements Opcodes { handlers[i].type = (index == 0) ? null : cp.getClassName(index); + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.println("Handler "+handlers[i].start + +"-"+handlers[i].end + +" @"+handlers[i].catcher + + ": "+handlers[i].type); + if (infos[handlers[i].catcher].instr.getOpcode() == opc_athrow) { /* There is an obfuscator, which inserts bogus * exception entries jumping directly to a throw @@ -864,6 +871,35 @@ class BasicBlockReader implements Opcodes { handlersLength--; i--; } + + if (handlers[i].start <= handlers[i].catcher + && handlers[i].end > handlers[i].catcher) + { + /* Javac 1.4 is a bit paranoid with finally and + * synchronize blocks and even breaks the JLS. + * We fix it here. Hopefully this won't produce + * any other problems. + */ + if (handlers[i].start == handlers[i].catcher) { + handlersLength--; + i--; + } else { + handlers[i].end = handlers[i].catcher; + } + } + + if (infos[handlers[i].end].instr.getOpcode() >= opc_ireturn + && infos[handlers[i].end].instr.getOpcode() <= opc_return) { + /* JDK 1.4 sometimes doesn't put return instruction into try + * block, which breaks the decompiler later. The return + * instruction can't throw exceptions so it doesn't really + * matter. + * + * FIXME: This may break other things if the return + * instruction is reachable from outside the try block. + */ + handlers[i].end++; + } } if (handlersLength < handlers.length) { HandlerEntry[] newHandlers = new HandlerEntry[handlersLength]; diff --git a/jode/src/net/sf/jode/bytecode/ClassInfo.java b/jode/src/net/sf/jode/bytecode/ClassInfo.java index de29825..4152931 100644 --- a/jode/src/net/sf/jode/bytecode/ClassInfo.java +++ b/jode/src/net/sf/jode/bytecode/ClassInfo.java @@ -240,6 +240,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable { private ClassPath classpath; private int modifiers = -1; + private boolean deprecatedFlag; private String name; private String className; private boolean methodScoped; @@ -560,6 +561,11 @@ public final class ClassInfo extends BinaryInfo implements Comparable { } else if (howMuch >= ClassInfo.OUTERCLASS && name.equals("InnerClasses")) { readInnerClassesAttribute(length, cp, input); + } else if (name.equals("Deprecated")) { + deprecatedFlag = true; + if (length != 0) + throw new ClassFormatException + ("Deprecated attribute has wrong length"); } else super.readAttribute(name, length, cp, input, howMuch); } @@ -848,7 +854,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable { gcp.putUTF8(ci.className); } } - prepareAttributes(gcp); + if (deprecatedFlag) + gcp.putUTF8("Deprecated"); + prepareAttributes(gcp); } /** @@ -911,6 +919,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable { output.writeShort(ci.modifiers); } } + if (deprecatedFlag) { + output.writeShort(gcp.putUTF8("Deprecated")); + output.writeInt(0); + } } @@ -1191,6 +1203,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable { return Modifier.isInterface(getModifiers()); } + /** + * Checks whether this class was declared as deprecated. In bytecode + * this is represented by a special attribute. + * @return true if this class info represents a deprecated class. + */ + public boolean isDeprecated() { + return deprecatedFlag; + } + /** * Searches for a field with given name and type signature. * @param name the name of the field. @@ -1324,6 +1345,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable { modified = true; } + public void setDeprecated(boolean flag) { + deprecatedFlag = flag; + } + public void setMethods(MethodInfo[] mi) { methods = mi; status = ALL; diff --git a/jode/src/net/sf/jode/expr/CompareToIntOperator.java b/jode/src/net/sf/jode/expr/CompareToIntOperator.java index 58da18b..b42c92a 100644 --- a/jode/src/net/sf/jode/expr/CompareToIntOperator.java +++ b/jode/src/net/sf/jode/expr/CompareToIntOperator.java @@ -54,6 +54,7 @@ public class CompareToIntOperator extends Operator { throws java.io.IOException { subExpressions[0].dumpExpression(writer, 550); + writer.breakOp(); writer.print(" <=>"); if (allowsNaN) writer.print(greaterOnNaN ? "g" : "l"); diff --git a/jode/src/net/sf/jode/expr/StoreInstruction.java b/jode/src/net/sf/jode/expr/StoreInstruction.java index da59ee2..22207b7 100644 --- a/jode/src/net/sf/jode/expr/StoreInstruction.java +++ b/jode/src/net/sf/jode/expr/StoreInstruction.java @@ -25,7 +25,7 @@ import net.sf.jode.decompiler.TabbedPrintWriter; public class StoreInstruction extends Operator implements CombineableOperator { - boolean isOpAssign = false; + boolean opAssign = false; public StoreInstruction(LValueExpression lvalue) { super(Type.tVoid, ASSIGN_OP); @@ -41,7 +41,11 @@ public class StoreInstruction extends Operator setOperatorIndex(operatorIndex); if (subExpressions[1] instanceof NopOperator) subExpressions[1].type = Type.tUnknown; - isOpAssign = true; + opAssign = true; + } + + public boolean isOpAssign() { + return opAssign; } /** @@ -72,7 +76,7 @@ public class StoreInstruction extends Operator Type newType; - if (!isOpAssign) { + if (!opAssign) { /* An opassign (+=, -=, etc.) doesn't merge rvalue type. */ Type lvalueType = subExpressions[0].getType(); Type rvalueType = subExpressions[1].getType(); diff --git a/jode/src/net/sf/jode/flow/CatchBlock.java b/jode/src/net/sf/jode/flow/CatchBlock.java index b64b919..144f274 100644 --- a/jode/src/net/sf/jode/flow/CatchBlock.java +++ b/jode/src/net/sf/jode/flow/CatchBlock.java @@ -24,6 +24,7 @@ import net.sf.jode.decompiler.Declarable; import net.sf.jode.expr.Expression; import net.sf.jode.expr.LocalLoadOperator; import net.sf.jode.expr.LocalStoreOperator; +import net.sf.jode.expr.NopOperator; import net.sf.jode.expr.StoreInstruction; import net.sf.jode.util.SimpleSet; @@ -220,17 +221,19 @@ public class CatchBlock extends StructuredBlock { } else if (firstInstr instanceof InstructionBlock) { Expression instr = ((InstructionBlock) firstInstr).getInstruction(); - if (instr instanceof StoreInstruction - && (((StoreInstruction)instr).getLValue() - instanceof LocalStoreOperator)) { - /* The exception is stored in a local variable */ - exceptionLocal = ((LocalStoreOperator) - ((StoreInstruction)instr).getLValue()) - .getLocalInfo(); - exceptionLocal.setType(exceptionType); - firstInstr.removeBlock(); - return true; - } + if (instr instanceof StoreInstruction) { + StoreInstruction store = (StoreInstruction) instr; + if (store.getOperatorIndex() == store.OPASSIGN_OP + && store.getSubExpressions()[1] instanceof NopOperator + && store.getLValue() instanceof LocalStoreOperator) { + /* The exception is stored in a local variable */ + exceptionLocal = ((LocalStoreOperator) store.getLValue()) + .getLocalInfo(); + exceptionLocal.setType(exceptionType); + firstInstr.removeBlock(); + return true; + } + } } return false; } diff --git a/jode/src/net/sf/jode/flow/CompleteSynchronized.java b/jode/src/net/sf/jode/flow/CompleteSynchronized.java index 63967f3..cbeca78 100644 --- a/jode/src/net/sf/jode/flow/CompleteSynchronized.java +++ b/jode/src/net/sf/jode/flow/CompleteSynchronized.java @@ -24,8 +24,7 @@ import net.sf.jode.expr.*; public class CompleteSynchronized { /** - * This combines the monitorenter into a synchronized statement - * @param flow The FlowBlock that is transformed + * This combines the monitorenter into a synchronized statement. */ public static boolean enter(SynchronizedBlock synBlock, StructuredBlock last) { @@ -63,8 +62,7 @@ public class CompleteSynchronized { /** * This combines the initial expression describing the object - * into a synchronized statement - * @param flow The FlowBlock that is transformed + * into a synchronized statement. */ public static boolean combineObject(SynchronizedBlock synBlock, StructuredBlock last) { diff --git a/jode/src/net/sf/jode/flow/CreateAssignExpression.java b/jode/src/net/sf/jode/flow/CreateAssignExpression.java index 387ae37..8dec5a4 100644 --- a/jode/src/net/sf/jode/flow/CreateAssignExpression.java +++ b/jode/src/net/sf/jode/flow/CreateAssignExpression.java @@ -56,7 +56,7 @@ public class CreateAssignExpression { SequentialBlock opBlock = (SequentialBlock) last.outer; StoreInstruction store = (StoreInstruction) ic.getInstruction(); - if (!store.isFreeOperator()) + if (!store.isFreeOperator() || store.isOpAssign()) return false; Expression lvalue = store.getSubExpressions()[0]; int lvalueCount = lvalue.getFreeOperandCount(); diff --git a/jode/src/net/sf/jode/flow/CreateExpression.java b/jode/src/net/sf/jode/flow/CreateExpression.java index 64e9006..2f41cd0 100644 --- a/jode/src/net/sf/jode/flow/CreateExpression.java +++ b/jode/src/net/sf/jode/flow/CreateExpression.java @@ -35,7 +35,6 @@ public class CreateExpression { /** * This does the transformation. - * @param FlowBlock the flow block to transform. * @return true if flow block was simplified. */ public static boolean transform(InstructionContainer ic, diff --git a/jode/src/net/sf/jode/flow/CreateIfThenElseOperator.java b/jode/src/net/sf/jode/flow/CreateIfThenElseOperator.java index db090fb..a2ca73b 100644 --- a/jode/src/net/sf/jode/flow/CreateIfThenElseOperator.java +++ b/jode/src/net/sf/jode/flow/CreateIfThenElseOperator.java @@ -141,7 +141,6 @@ public class CreateIfThenElseOperator { * * Also note that the produced code is suboptimal: The push-0 could * sometimes be better replaced with a correct jump. - * @param flow The FlowBlock that is transformed */ public static boolean createFunny(ConditionalBlock cb, StructuredBlock last) { @@ -188,7 +187,6 @@ public class CreateIfThenElseOperator { * ->push cond ? e1 : e2 * * The -> points to the lastModified block. - * @param flow The FlowBlock that is transformed */ public static boolean create(InstructionContainer ic, StructuredBlock last) { diff --git a/jode/src/net/sf/jode/flow/TransformConstructors.java b/jode/src/net/sf/jode/flow/TransformConstructors.java index d01a093..d3c0dc6 100644 --- a/jode/src/net/sf/jode/flow/TransformConstructors.java +++ b/jode/src/net/sf/jode/flow/TransformConstructors.java @@ -106,7 +106,8 @@ public class TransformConstructors { this.clazzAnalyzer = clazzAnalyzer; this.isStatic = isStatic; this.cons = cons; - this.outerValues = clazzAnalyzer.getOuterValues(); + if (!isStatic) + this.outerValues = clazzAnalyzer.getOuterValues(); lookForConstructorCall(); } diff --git a/jode/src/net/sf/jode/jvm/CodeVerifier.java b/jode/src/net/sf/jode/jvm/CodeVerifier.java index d6c3235..3d5d267 100644 --- a/jode/src/net/sf/jode/jvm/CodeVerifier.java +++ b/jode/src/net/sf/jode/jvm/CodeVerifier.java @@ -1120,8 +1120,13 @@ public class CodeVerifier implements Opcodes { if (!info.pop().isOfType(tType(type))) throw new VerifyException(instr.getDescription()); Type classType = tType(ref.getClazz()); - if (!info.pop().isOfType(classType)) - throw new VerifyException(instr.getDescription()); + Type classOnStack = info.pop(); + if (!classOnStack.isOfType(classType)) { + /* Sometimes synthetic code writes to uninitialized classes. */ + classType = tType("N" + ref.getClazz().substring(1)); + if (!classOnStack.isOfType(classType)) + throw new VerifyException(instr.getDescription()); + } break; } case opc_invokevirtual: diff --git a/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java b/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java index ce0c510..d1f46b0 100644 --- a/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java +++ b/jode/src/net/sf/jode/jvm/RuntimeEnvironment.java @@ -109,13 +109,13 @@ public interface RuntimeEnvironment { /** * Enter a monitor. - * @param object the object whose monitor should be taken. + * @param obj the object whose monitor should be taken. */ public void enterMonitor(Object obj) throws InterpreterException; /** * Exit a monitor. - * @param object the object whose monitor should be freed. + * @param obj the object whose monitor should be freed. */ public void exitMonitor(Object obj) throws InterpreterException; diff --git a/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java b/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java index 9f9ea0f..74ecacc 100644 --- a/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java +++ b/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java @@ -111,7 +111,8 @@ public class SyntheticAnalyzer implements Opcodes { Block startBlock = bb.getStartBlock(); Handler[] excHandlers = bb.getExceptionHandlers(); if (startBlock == null - || startBlock.getInstructions().length != 3 + || startBlock.getInstructions().length < 2 + || startBlock.getInstructions().length > 3 || excHandlers.length != 1 || excHandlers[0].getStart() != startBlock || excHandlers[0].getEnd() != startBlock @@ -119,7 +120,7 @@ public class SyntheticAnalyzer implements Opcodes { .equals(excHandlers[0].getType())) return false; - for (int i=0; i< 3; i++) { + for (int i=0; i< 2; i++) { Instruction instr = startBlock.getInstructions()[i]; if (instr.getOpcode() != getClassOpcodes[i]) return false; @@ -132,6 +133,17 @@ public class SyntheticAnalyzer implements Opcodes { if (i == 0 && instr.getLocalSlot() != 0) return false; } + if (startBlock.getInstructions().length == 2) { + /* JDK 1.4: The return is outside of startBlock */ + Block nextBlock = startBlock.getSuccs()[0]; + Instruction[] instrs = nextBlock.getInstructions(); + if (instrs[0].getOpcode() != opc_areturn) + return false; + } else { + /* JDK 1.3 */ + if (startBlock.getInstructions()[2].getOpcode() != opc_areturn) + return false; + } Block catchBlock = excHandlers[0].getCatcher(); if (catchBlock.getInstructions().length != 7)