From 676e21257ff0ff4934a8d0c54cac12e521c0d720 Mon Sep 17 00:00:00 2001 From: hoenicke Date: Sun, 15 Jul 2001 16:33:01 +0000 Subject: [PATCH] Applied more patches from Jode-1.1 branch. git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1328 379699f6-c40d-0410-875b-85095c16579e --- jode/ChangeLog | 67 +++- jode/acinclude.m4 | 2 +- jode/configure.in | 2 +- jode/jode/GlobalOptions.java | 4 +- jode/jode/bytecode/BasicBlockReader.java | 15 + jode/jode/decompiler/Main.java | 2 +- jode/jode/expr/IfThenElseOperator.java | 72 ++-- jode/jode/flow/CatchBlock.java | 12 + jode/jode/flow/EmptyBlock.java | 11 + jode/jode/flow/StructuredBlock.java | 12 + jode/jode/flow/TransformConstructors.java | 30 +- .../jode/flow/TransformExceptionHandlers.java | 367 ++++++++---------- jode/jode/jvm/CodeVerifier.java | 36 +- jode/jode/swingui/Main.java | 19 +- jode/jode/type/ArrayType.java | 16 +- jode/jode/type/ClassType.java | 2 + jode/jode/util/UnifyHash.java | 5 +- 17 files changed, 398 insertions(+), 276 deletions(-) diff --git a/jode/ChangeLog b/jode/ChangeLog index c2ee591..926db89 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -1,12 +1,67 @@ +2001-07-15 Jochen Hoenicke + Applied patches from 2001-02-27 of Jode 1.1 tree: + + * acinclude.m4 (JODE_CHECK_CLASS): Changed "test -e" to "-f" since + -e is not supported on all architectures (Solaris) and -f is more + correct anyway. + Reported by Erik Modén. + + * jode/swingui/Main.java.in (AreaWriter): Convert all kinds of + line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea + understands. + +2001-07-15 Jochen Hoenicke + Applied patch from 2001-02-04 of Jode 1.1 tree: + + * jode/expr/IfThenElseOperator.java (simplify): Allow in the class$ + simplification the then and else part to be swapped. + * jode/type/ClassType.java (keywords): Added the package + and import keywords. + + * jode/flow/TransformExceptionHandlers.java: + (getPredecessor): New function. + (getMonitorExitSlot): New function. + (skipFinExitChain): New function. + (removeJSR): Replaced by ... + (removeBadJSR): ... this. + (checkAndRemoveJSR): Use the new functions. Much simpler and + handles nested synchronized blocks. It now traces the whole JSR + and monitorexit chain before a jump to the first entry via + skipFinExitChain, then checks and remove the first JSR + resp. monitorexit. JSR jumps are simply ignored now. + (checkAndRemoveMonitorExit): likewise. + * jode/flow/StructuredBlock.java (prependBlock): New function. + * jode/flow/CatchBlock.java (makeDeclaration): Generate name + of dummyLocal, since nobody else will generate it. + + * jode/bytecode/BasicBlockReader.java (readCode): Remove bogus + exceptionHandlers, whose catchers just throw the exception again. + This kind of entries are inserted by an obfuscator and would break + JODE. + * jode/util/UnifyHash.java (iterateHashCode): Call cleanUp, + to clean unneeded references. + * jode/flow/TransformConstructors.java (transformOneField): + Changed to private. Take field number as parameter. Check that + expression doesn't contain a FieldOperator for a later field of + the same class or a PutFieldOperator. Changed all callers. + +2001-07-15 Jochen Hoenicke + + Applied patch from 2001-02-01 of Jode 1.1 tree: + * jode/jvm/CodeVerifier.java (Type.mergeType): If array elem + types can't be merged, return tObject as common super type. + * jode/type/ArrayType.java (getGeneralizedType): If array elem + type can't be intersected, return tObject as common super type. + 2001-07-15 Jochen Hoenicke Applied patch from Java 1.1 tree: - * jode/expr/Expression.java.in (updateParentTypes): Call setType, + * jode/expr/Expression.java (updateParentTypes): Call setType, instead of merging the types. Other childs want to know about the type change as well. * jode/decompiler/LocalInfo.java (combineWith): Reorganized a bit, but no changes. - * jode/expr/InvokeOperator.java.in (dumpExpression): Always print + * jode/expr/InvokeOperator.java (dumpExpression): Always print the ThisOperator if a field is from a parent class of an outer class is used. And always qualify the this operator if not innermost. @@ -22,11 +77,11 @@ (printOptionalSpace): Print space for GNU_SPACING. * jode/decompiler/Options.java (setOptions): changed gnu style to include GNU_SPACING. - * jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use + * jode/decompiler/ClassAnalyzer.java (dumpSource): Use open/closeBraceClass. - * jode/decompiler/MethodAnalyzer.java.in (dumpSource): Use + * jode/decompiler/MethodAnalyzer.java (dumpSource): Use open/closeBraceNoIndent. Call printOptionalSpace. - * jode/decompiler/InvokeOperator.java.in (dumpExpression): + * jode/decompiler/InvokeOperator.java (dumpExpression): Call printOptionalSpace, use open/closeBraceClass for inner classes. * jode/decompiler/UnaryOperator.java (dumpExpression): Call @@ -43,7 +98,7 @@ version in ReferenceType is more correct. Before tNull.isOfType(tRange(X,tNull)) returned false, which lead to incorrect behaviour in InvokeOperator.needsCast. - * jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the + * jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the "= null" hack for final fields; it was not correct, since the field could be initialized in a constructor. * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): diff --git a/jode/acinclude.m4 b/jode/acinclude.m4 index 563ec98..296d1ad 100644 --- a/jode/acinclude.m4 +++ b/jode/acinclude.m4 @@ -28,7 +28,7 @@ AC_DEFUN(JODE_CHECK_CLASS, myclasspath=$2; for path in $myclasspath; do if test -d $path; then - if test -e $path/$clazz; then + if test -f $path/$clazz; then exit 0 fi elif CLASS_CHECK $path $clazz ; then diff --git a/jode/configure.in b/jode/configure.in index 0fc33e2..4b131b5 100644 --- a/jode/configure.in +++ b/jode/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT() -AM_INIT_AUTOMAKE(jode, 1.0.93) +AM_INIT_AUTOMAKE(jode, 1.0.94) dnl Checks for programs. dnl AC_PROG_CXX diff --git a/jode/jode/GlobalOptions.java b/jode/jode/GlobalOptions.java index 17dc90d..bbaba84 100644 --- a/jode/jode/GlobalOptions.java +++ b/jode/jode/GlobalOptions.java @@ -1,4 +1,4 @@ -/* GlobalOptions Copyright (C) 1999-2000 Jochen Hoenicke. +/* GlobalOptions Copyright (C) 1999-2001 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ public class GlobalOptions { public final static String version = "@VERSION@"; public final static String email = "jochen@gnu.org"; public final static String copyright = - "Jode (c) 1998-2000 Jochen Hoenicke <"+email+">"; + "Jode (c) 1998-2001 Jochen Hoenicke <"+email+">"; public final static String URL = "http://jode.sourceforge.net/"; public static PrintWriter err = new PrintWriter(System.err, true); diff --git a/jode/jode/bytecode/BasicBlockReader.java b/jode/jode/bytecode/BasicBlockReader.java index c653780..1410574 100644 --- a/jode/jode/bytecode/BasicBlockReader.java +++ b/jode/jode/bytecode/BasicBlockReader.java @@ -828,6 +828,21 @@ class BasicBlockReader implements Opcodes { int index = input.readUnsignedShort(); handlers[i].type = (index == 0) ? null : cp.getClassName(index); + + if (infos[handlers[i].catcher].instr.getOpcode() == opc_athrow) { + /* There is an obfuscator, which inserts bogus + * exception entries jumping directly to a throw + * instruction. Remove those handlers. + */ + handlersLength--; + i--; + } + } + if (handlersLength < handlers.length) { + HandlerEntry[] newHandlers = new HandlerEntry[handlersLength]; + System.arraycopy(handlers, 0, newHandlers, 0, + handlersLength); + handlers = newHandlers; } for (int i=0; i< infos.length; i++) { diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java index d427e5c..4a5c080 100644 --- a/jode/jode/decompiler/Main.java +++ b/jode/jode/decompiler/Main.java @@ -1,4 +1,4 @@ -/* Main Copyright (C) 1998-1999 Jochen Hoenicke. +/* Main Copyright (C) 1998-2001 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java index 3a66b77..8b82e5c 100644 --- a/jode/jode/expr/IfThenElseOperator.java +++ b/jode/jode/expr/IfThenElseOperator.java @@ -59,39 +59,49 @@ public class IfThenElseOperator extends Operator { return subExpressions[0].negate().simplify(); } } - if (subExpressions[0] instanceof CompareUnaryOperator - && (subExpressions[1] instanceof GetFieldOperator) - && (subExpressions[2] instanceof StoreInstruction)) { - // Check for - // class$classname != null ? class$classname : - // (class$classname = class$("classname")) - // and replace with - // classname.class + if (subExpressions[0] instanceof CompareUnaryOperator + && ((((CompareUnaryOperator) subExpressions[0]) + .getOperatorIndex() & ~1) == Operator.COMPARE_OP)) { CompareUnaryOperator cmp = (CompareUnaryOperator) subExpressions[0]; - GetFieldOperator get = (GetFieldOperator) subExpressions[1]; - StoreInstruction put = (StoreInstruction) subExpressions[2]; - FieldAnalyzer field; - if (cmp.getOperatorIndex() == Operator.NOTEQUALS_OP - && put.getLValue() instanceof PutFieldOperator - && ((field = ((PutFieldOperator)put.getLValue()).getField()) - != null) && field.isSynthetic() - && put.lvalueMatches(get) - && cmp.subExpressions[0] instanceof GetFieldOperator - && put.lvalueMatches((GetFieldOperator)cmp.subExpressions[0]) - && put.subExpressions[1] instanceof InvokeOperator) { - InvokeOperator invoke = (InvokeOperator) put.subExpressions[1]; - if (invoke.isGetClass() - && invoke.subExpressions[0] instanceof ConstOperator - && (invoke.subExpressions[0].getType() - .equals(Type.tString))) { - String clazz = (String) - ((ConstOperator)invoke.subExpressions[0]).getValue(); - ClassPath cp = field.getClassAnalyzer().getClassPath(); - if (field.setClassConstant(clazz)) - return new ClassFieldOperator - (clazz.charAt(0) == '[' - ? Type.tType(cp, clazz) : Type.tClass(cp, clazz)); + int cmpType = cmp.getOperatorIndex() & 1; + if ((subExpressions[2 - cmpType] instanceof GetFieldOperator) + && (subExpressions[1 + cmpType] instanceof StoreInstruction)) { + // Check for + // class$classname != null ? class$classname : + // (class$classname = class$("classname")) + // and replace with + // classname.class + GetFieldOperator get + = (GetFieldOperator) subExpressions[2 - cmpType]; + StoreInstruction put + = (StoreInstruction) subExpressions[1 + cmpType]; + int opIndex = cmp.getOperatorIndex(); + FieldAnalyzer field; + if (put.getLValue() instanceof PutFieldOperator + && ((field = ((PutFieldOperator)put.getLValue()) + .getField()) != null) && field.isSynthetic() + && put.lvalueMatches(get) + && (cmp.subExpressions[0] instanceof GetFieldOperator) + && put.lvalueMatches((GetFieldOperator) + cmp.subExpressions[0]) + && put.subExpressions[1] instanceof InvokeOperator) { + InvokeOperator invoke = (InvokeOperator) + put.subExpressions[1]; + if (invoke.isGetClass() + && invoke.subExpressions[0] instanceof ConstOperator + && (invoke.subExpressions[0].getType() + .equals(Type.tString))) { + String clazz = (String) + ((ConstOperator)invoke.subExpressions[0]) + .getValue(); + ClassPath cp = field.getClassAnalyzer().getClassPath(); + if (field.setClassConstant(clazz)) + return new ClassFieldOperator + (clazz.charAt(0) == '[' + ? Type.tType(cp, clazz) + : Type.tClass(cp, clazz)); + } } } } diff --git a/jode/jode/flow/CatchBlock.java b/jode/jode/flow/CatchBlock.java index 34d1898..0a960e2 100644 --- a/jode/jode/flow/CatchBlock.java +++ b/jode/jode/flow/CatchBlock.java @@ -20,6 +20,7 @@ package jode.flow; import jode.type.Type; import jode.decompiler.LocalInfo; +import jode.decompiler.Declarable; import jode.expr.Expression; import jode.expr.LocalLoadOperator; import jode.expr.LocalStoreOperator; @@ -28,6 +29,7 @@ import jode.util.SimpleSet; ///#def COLLECTIONS java.util import java.util.Collections; +import java.util.Iterator; import java.util.Set; ///#enddef @@ -162,6 +164,16 @@ public class CatchBlock extends StructuredBlock { ib.appendBlock(catchBlock); catchBlock = ib; exceptionLocal = dummyLocal; + String localName = dummyLocal.guessName(); + Iterator doneIter = done.iterator(); + while (doneIter.hasNext()) { + Declarable previous = (Declarable) doneIter.next(); + if (localName.equals(previous.getName())) { + /* A name conflict happened. */ + dummyLocal.makeNameUnique(); + break; + } + } } } } diff --git a/jode/jode/flow/EmptyBlock.java b/jode/jode/flow/EmptyBlock.java index 840e56b..1099934 100644 --- a/jode/jode/flow/EmptyBlock.java +++ b/jode/jode/flow/EmptyBlock.java @@ -56,6 +56,17 @@ public class EmptyBlock extends StructuredBlock { return block; } + /** + * Prepends a block to this block. + * @return the new combined block. + */ + public StructuredBlock prependBlock(StructuredBlock block) { + /* For empty blocks: append == prepend modulo jump */ + block = appendBlock(block); + block.moveJump(this.jump); + return block; + } + public void dumpInstruction(TabbedPrintWriter writer) throws java.io.IOException { diff --git a/jode/jode/flow/StructuredBlock.java b/jode/jode/flow/StructuredBlock.java index c2d6835..9168032 100644 --- a/jode/jode/flow/StructuredBlock.java +++ b/jode/jode/flow/StructuredBlock.java @@ -316,6 +316,18 @@ public abstract class StructuredBlock { } } + /** + * Prepends a block to this block. + * @return the new combined block. + */ + public StructuredBlock prependBlock(StructuredBlock block) { + SequentialBlock sequBlock = new SequentialBlock(); + sequBlock.replace(this); + sequBlock.setFirst(block); + sequBlock.setSecond(this); + return sequBlock; + } + /** * Removes this block, or replaces it with an EmptyBlock. */ diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java index 1466617..322d742 100644 --- a/jode/jode/flow/TransformConstructors.java +++ b/jode/jode/flow/TransformConstructors.java @@ -1,4 +1,4 @@ -/* TransformConstructors Copyright (C) 1998-1999 Jochen Hoenicke. +/* TransformConstructors Copyright (C) 1998-2001 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -585,7 +585,8 @@ public class TransformConstructors { * @param expr the initializer to check * @return the transformed initializer or null if expr is not valid. */ - private Expression transformFieldInitializer(Expression expr) { + private Expression transformFieldInitializer(int fieldSlot, + Expression expr) { if (expr instanceof LocalVarOperator) { if (!(expr instanceof LocalLoadOperator)) { if ((GlobalOptions.debuggingFlags @@ -606,11 +607,21 @@ public class TransformConstructors { +" "+outerValues); return null; } + if (expr instanceof FieldOperator) { + if (expr instanceof PutFieldOperator) + return null; + FieldOperator fo = (FieldOperator) expr; + if (fo.getClassInfo() == clazzAnalyzer.getClazz() + && clazzAnalyzer.getFieldIndex(fo.getFieldName(), + fo.getFieldType()) >= fieldSlot) + return null; + } if (expr instanceof Operator) { Operator op = (Operator) expr; Expression[] subExpr = op.getSubExpressions(); for (int i=0; i< subExpr.length; i++) { - Expression transformed = transformFieldInitializer(subExpr[i]); + Expression transformed + = transformFieldInitializer(fieldSlot, subExpr[i]); if (transformed == null) return null; if (transformed != subExpr[i]) @@ -693,7 +704,7 @@ public class TransformConstructors { break big_loop; Expression expr = store.getSubExpressions()[1]; - expr = transformFieldInitializer(expr); + expr = transformFieldInitializer(field, expr); if (expr == null) break big_loop; @@ -780,8 +791,14 @@ public class TransformConstructors { } } + int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(), + pfo.getFieldType()); + + if (field <= lastField) + return -1; + Expression expr = store.getSubExpressions()[1]; - expr = transformFieldInitializer(expr); + expr = transformFieldInitializer(field, expr); if (expr == null) return -1; @@ -790,9 +807,6 @@ public class TransformConstructors { GlobalOptions.err.println(" field " + pfo.getFieldName() + " = " + expr); - int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(), - pfo.getFieldType()); - // if field does not exists: -1 <= lastField. if (field <= lastField || !(clazzAnalyzer.getField(field).setInitializer(expr))) { diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java index caf6441..7cf5a26 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -185,11 +185,7 @@ public class TransformExceptionHandlers { * @param ret the ReturnBlock. */ private void removeReturnLocal(ReturnBlock ret) { - if (ret.outer == null - || !(ret.outer instanceof SequentialBlock)) - return; - - StructuredBlock pred = ret.outer.getSubBlocks()[0]; + StructuredBlock pred = getPredecessor(ret); if (!(pred instanceof InstructionBlock)) return; Expression instr = ((InstructionBlock) pred).getInstruction(); @@ -208,15 +204,13 @@ public class TransformExceptionHandlers { } /** - * Remove the JSR's jumping to the specified subRoutine. It - * is checked if the next block is a leaving instruction, and - * otherwise the JsrBlock is not removed (to give the user a - * hint that something went wrong). This will also remove the - * local javac generates for returns. + * Remove the wrongly placed JSRs jumping to the specified + * subRoutine. The right JSRs are already removed, but we have to + * replace the wrong ones with a warning. * @param tryFlow the FlowBLock of the try block. * @param subRoutine the FlowBlock of the sub routine. */ - private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock, + private void removeBadJSR(FlowBlock tryFlow, StructuredBlock catchBlock, FlowBlock subRoutine) { Jump nextJump; for (Jump jumps = tryFlow.getJumps(subRoutine); @@ -230,105 +224,130 @@ public class TransformExceptionHandlers { /* This is the mandatory jsr in the catch block */ continue; } - if (prev.outer.getNextFlowBlock() != null) { - /* The jsr is directly before a jump, okay. */ - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - prev.outer.removeBlock(); - continue; - } - if (prev.outer.outer instanceof SequentialBlock - && prev.outer.outer.getSubBlocks()[0] == prev.outer) { - SequentialBlock seq = (SequentialBlock) prev.outer.outer; - if (seq.subBlocks[1] instanceof JsrBlock - || (seq.subBlocks[1] instanceof SequentialBlock - && seq.subBlocks[1].getSubBlocks()[0] - instanceof JsrBlock)) { - /* The jsr is followed by a jsr, okay. */ - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - prev.outer.removeBlock(); - continue; - } - if (seq.subBlocks[1] instanceof ReturnBlock - && !(seq.subBlocks[1] instanceof ThrowBlock)) { - - /* The jsr is followed by a return, okay. */ - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - ReturnBlock ret = (ReturnBlock) seq.subBlocks[1]; - prev.outer.removeBlock(); - - removeReturnLocal(ret); - continue; - } - } + /* We have a JSR to the subroutine, which is badly placed. + * We complain here. + */ + DescriptionBlock msg + = new DescriptionBlock("ERROR: JSR FINALLY BLOCK!"); + tryFlow.removeSuccessor(jumps); + prev.removeJump(); + msg.replace(prev.outer); + } else { + /* We have a jump to the subroutine, that is wrong. + * We complain here. + */ + DescriptionBlock msg + = new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); + tryFlow.removeSuccessor(jumps); + prev.removeJump(); + prev.appendBlock(msg); + } + } + } + + private static StructuredBlock getPredecessor(StructuredBlock stmt) + { + if (stmt.outer instanceof SequentialBlock) { + SequentialBlock seq = (SequentialBlock) stmt.outer; + if (seq.subBlocks[1] == stmt) + return seq.subBlocks[0]; + else if (seq.outer instanceof SequentialBlock) + return seq.outer.getSubBlocks()[0]; + } + return null; + } + + /** + * Gets the slot of the monitorexit instruction instr in the + * stmt, or -1 if stmt isn't a InstructionBlock with a + * monitorexit instruction. + * @param stmt the stmt, may be null. + */ + private static int getMonitorExitSlot(StructuredBlock stmt) { + if (stmt instanceof InstructionBlock) { + Expression instr = ((InstructionBlock) stmt).getInstruction(); + if (instr instanceof MonitorExitOperator) { + MonitorExitOperator monExit = (MonitorExitOperator)instr; + if (monExit.getFreeOperandCount() == 0 + && (monExit.getSubExpressions()[0] + instanceof LocalLoadOperator)) + return ((LocalLoadOperator) monExit.getSubExpressions()[0]) + .getLocalInfo().getSlot(); } - /* Now we have a jump to the subroutine, that is wrong. - * We complain here. - */ - DescriptionBlock msg - = new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - prev.appendBlock(msg); } + return -1; } - public void checkAndRemoveJSR(FlowBlock tryFlow, - StructuredBlock catchBlock, - FlowBlock subRoutine, - int startOutExit, int endOutExit) { - boolean foundSub = false; + private boolean isMonitorExitSubRoutine(FlowBlock subRoutine, + LocalInfo local) { + if (transformSubRoutine(subRoutine.block) + && getMonitorExitSlot(subRoutine.block) == local.getSlot()) + return true; + return false; + } + + private static StructuredBlock skipFinExitChain(StructuredBlock block) + { + StructuredBlock pred, result; + if (block instanceof ReturnBlock) + pred = getPredecessor(block); + else + pred = block; + result = null; + + while (pred instanceof JsrBlock + || getMonitorExitSlot(pred) >= 0) { + result = pred; + pred = getPredecessor(pred); + } + return result; + } + + + private void checkAndRemoveJSR(FlowBlock tryFlow, + StructuredBlock catchBlock, + FlowBlock subRoutine, + int startOutExit, int endOutExit) { Iterator iter = tryFlow.getSuccessors().iterator(); dest_loop: while (iter.hasNext()) { FlowBlock dest = (FlowBlock) iter.next(); - if (dest == subRoutine) { - foundSub = true; + if (dest == subRoutine) continue dest_loop; - } boolean isFirstJump = true; for (Jump jumps = tryFlow.getJumps(dest); jumps != null; jumps = jumps.next, isFirstJump = false) { StructuredBlock prev = jumps.prev; - if (prev instanceof JsrBlock) { - /* The jump is directly preceeded by a jsr. - * Everything okay. - */ - continue; - } - if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { - /* If jump is a jsr check the outer - * block instead. - */ - prev = prev.outer; - } - if ((prev instanceof ReturnBlock - || prev instanceof JsrBlock) - && prev.outer instanceof SequentialBlock) { - SequentialBlock seq = (SequentialBlock) prev.outer; - if (seq.subBlocks[1] == prev - && (seq.subBlocks[0] instanceof JsrBlock)) { - /* The jump is preceeded by another jsr, okay. - */ - continue; - } - if (seq.subBlocks[0] == prev - && seq.outer instanceof SequentialBlock - && (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) { - /* Again the jump is preceeded by another jsr, okay. - */ - continue; - } + /* This jump is really a jsr, since it doesn't + * leave the block forever, we can ignore it. + */ + continue; } - if (isFirstJump) { - /* Now we have a jump that is not preceded by the + StructuredBlock pred = skipFinExitChain(prev); + if (pred instanceof JsrBlock) { + StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; + if (jsrInner instanceof EmptyBlock + && jsrInner.jump != null + && jsrInner.jump.destination == subRoutine) { + /* The jump is preceeded by the right jsr. Remove + * the jsr. + */ + tryFlow.removeSuccessor(jsrInner.jump); + jsrInner.removeJump(); + pred.removeBlock(); + if (prev instanceof ReturnBlock) + removeReturnLocal((ReturnBlock) prev); + continue; + } + } + + if (pred == null && isFirstJump) { + /* Now we have a jump that is not preceded by any * jsr. There's a last chance: the jump jumps * directly to a correct jsr instruction, which * lies outside the try/catch block. @@ -359,45 +378,21 @@ public class TransformExceptionHandlers { */ DescriptionBlock msg = new DescriptionBlock("ERROR: NO JSR TO FINALLY"); - prev.appendBlock(msg); - msg.moveJump(jumps); - } - } - if (foundSub) - removeJSR(tryFlow, catchBlock, subRoutine); - } - - static boolean isMonitorExit(Expression instr, LocalInfo local) { - if (instr instanceof MonitorExitOperator) { - MonitorExitOperator monExit = (MonitorExitOperator)instr; - if (monExit.getFreeOperandCount() == 0 - && monExit.getSubExpressions()[0] instanceof LocalLoadOperator - && (((LocalLoadOperator) monExit.getSubExpressions()[0]) - .getLocalInfo().getSlot() == local.getSlot())) { - return true; + if (pred != null) + pred.prependBlock(msg); + else { + prev.appendBlock(msg); + msg.moveJump(prev.jump); + } } } - return false; - } - - boolean isMonitorExitSubRoutine(FlowBlock subRoutine, LocalInfo local) { - if (transformSubRoutine(subRoutine.block)) { - if (subRoutine.block instanceof InstructionBlock) { - Expression instr = - ((InstructionBlock)subRoutine.block) - .getInstruction(); - if (isMonitorExit(instr, local)) { - return true; - } - } - } - return false; + removeBadJSR(tryFlow, catchBlock, subRoutine); } - public void checkAndRemoveMonitorExit(FlowBlock tryFlow, - StructuredBlock catchBlock, - LocalInfo local, - int start, int end) { + private void checkAndRemoveMonitorExit(FlowBlock tryFlow, + StructuredBlock catchBlock, + LocalInfo local, + int start, int end) { FlowBlock subRoutine = null; Iterator succs = tryFlow.getSuccessors().iterator(); dest_loop: @@ -408,85 +403,51 @@ public class TransformExceptionHandlers { jumps != null; jumps = jumps.next, isFirstJump = false) { StructuredBlock prev = jumps.prev; - - if (prev instanceof JsrBlock) { - /* The jump is directly preceeded by a jsr. - */ - continue; - } if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { - /* If jump is a jsr check the outer - * block instead. - */ - prev = prev.outer; - } - - /* If the block is a jsr or a return block, check if - * it is preceeded by another jsr. - */ - if ((prev instanceof JsrBlock - || prev instanceof ReturnBlock) - && prev.outer instanceof SequentialBlock) { - SequentialBlock seq = (SequentialBlock) prev.outer; - StructuredBlock pred = null; - if (seq.subBlocks[1] == prev) - pred = seq.subBlocks[0]; - else if (seq.outer instanceof SequentialBlock) - pred = seq.outer.getSubBlocks()[0]; - - if (pred != null) { - if (pred instanceof JsrBlock) - /* The jump is preceeded by another jsr, okay. - */ - continue; - - if (pred instanceof InstructionBlock) { - Expression instr = - ((InstructionBlock)pred).getInstruction(); - if (isMonitorExit(instr, local)) { - pred.removeBlock(); - if (prev instanceof ReturnBlock) - removeReturnLocal((ReturnBlock) prev); - continue; - } - } - } - } - - if (prev instanceof InstructionBlock - && isMonitorExit(((InstructionBlock)prev).instr, local)) { - /* This is probably the last expression in the - * synchronized block, and has the right monitor exit - * attached. Remove this block. - */ - prev.removeBlock(); - continue; - } - - if (isFirstJump) { - /* This is the first jump to that destination. - * Check if the destination does the monitorExit + /* This jump is really a jsr, since it doesn't + * leave the block forever, we can ignore it. */ + continue; + } + StructuredBlock pred = skipFinExitChain(prev); + if (pred instanceof JsrBlock) { + StructuredBlock jsrInner = ((JsrBlock) pred).innerBlock; + if (jsrInner instanceof EmptyBlock + && jsrInner.jump != null) { + FlowBlock dest = jsrInner.jump.destination; - /* The block is a jsr that is not preceeded by - * another jsr. This must be the monitorexit - * subroutine. - */ - if (prev instanceof JsrBlock) { if (subRoutine == null - && successor.getBlockNr() >= start - && successor.getNextBlockNr() <= end) { - successor.analyze(start, end); - - if (isMonitorExitSubRoutine(successor, local)) - subRoutine = successor; + && dest.getBlockNr() >= start + && dest.getNextBlockNr() <= end) { + dest.analyze(start, end); + if (isMonitorExitSubRoutine(dest, local)) + subRoutine = dest; } - if (subRoutine == successor) - continue dest_loop; + if (dest == subRoutine) { + /* The jump is preceeded by the right jsr. Remove + * the jsr. + */ + tryFlow.removeSuccessor(jsrInner.jump); + jsrInner.removeJump(); + pred.removeBlock(); + if (prev instanceof ReturnBlock) + removeReturnLocal((ReturnBlock) prev); + continue; + } } + } else if (getMonitorExitSlot(pred) == local.getSlot()) { + /* The jump is preceeded by the right monitor + * exit instruction. + */ + pred.removeBlock(); + if (prev instanceof ReturnBlock) + removeReturnLocal((ReturnBlock) prev); + continue; + } + if (pred == null && isFirstJump) { /* Now we have a jump that is not preceded by a * monitorexit. There's a last chance: the jump * jumps directly to the correct monitorexit @@ -514,17 +475,15 @@ public class TransformExceptionHandlers { } if (subRoutine == dest) { + successor.removeSuccessor(jsrInner.jump); + jsrInner.removeJump(); sb.removeBlock(); continue dest_loop; } } - if (sb instanceof InstructionBlock) { - Expression instr = ((InstructionBlock)sb) - .getInstruction(); - if (isMonitorExit(instr, local)) { - sb.removeBlock(); - continue dest_loop; - } + if (getMonitorExitSlot(sb) == local.getSlot()) { + sb.removeBlock(); + continue dest_loop; } } } @@ -539,7 +498,7 @@ public class TransformExceptionHandlers { } if (subRoutine != null) { - removeJSR(tryFlow, catchBlock, subRoutine); + removeBadJSR(tryFlow, catchBlock, subRoutine); tryFlow.mergeBlockNr(subRoutine); } } diff --git a/jode/jode/jvm/CodeVerifier.java b/jode/jode/jvm/CodeVerifier.java index ef28192..5684e6d 100644 --- a/jode/jode/jvm/CodeVerifier.java +++ b/jode/jode/jvm/CodeVerifier.java @@ -286,19 +286,17 @@ public class CodeVerifier implements Opcodes { dimensions++; } - if (c1 == '[' || c2 == '[') { - // Only one of them is array now, the other must be an - // object, the common super is tObject - if (c1 == 'L' || c2 == 'L') { - if (dimensions == 0) - return cv.tObject; - StringBuffer result = new StringBuffer(dimensions + 18); - for (int i=0; i< dimensions; i++) - result.append("["); - result.append("Ljava/lang/Object;"); - return cv.tType(result.toString()); - } - return cv.tNone; + // One of them is array now, the other is an object, + // the common super is tObject + if ((c1 == '[' && c2 == 'L') + || (c1 == 'L' && c2 == '[')) { + if (dimensions == 0) + return cv.tObject; + StringBuffer result = new StringBuffer(dimensions + 18); + for (int i=0; i< dimensions; i++) + result.append("["); + result.append("Ljava/lang/Object;"); + return cv.tType(result.toString()); } if (c1 == 'L' && c2 == 'L') { @@ -338,6 +336,18 @@ public class CodeVerifier implements Opcodes { result.append("L"); return cv.tType(result.toString(), clazz1); } + + // Both were arrays, but of different primitive types. The + // common super is tObject with one dimension less. + if (dimensions > 0) { + if (dimensions == 1) + return cv.tObject; + StringBuffer result = new StringBuffer(dimensions + 17); + for (int i=0; i< dimensions - 1; i++) + result.append("["); + result.append("Ljava/lang/Object;"); + return cv.tType(result.toString()); + } return cv.tNone; } diff --git a/jode/jode/swingui/Main.java b/jode/jode/swingui/Main.java index e7e1376..5c285cd 100644 --- a/jode/jode/swingui/Main.java +++ b/jode/jode/swingui/Main.java @@ -168,6 +168,7 @@ public class Main public class AreaWriter extends Writer { boolean initialized = false; + boolean lastCR = false; private JTextArea area; public AreaWriter(JTextArea a) { @@ -180,7 +181,23 @@ public class Main area.setText(""); initialized = true; } - area.append(new String(b, off, len)); + String str = new String(b, off, len); + StringBuffer sb = new StringBuffer(len); + while (str != null && str.length() > 0) { + if (lastCR && str.charAt(0) == '\n') + str = str.substring(1); + int crIndex = str.indexOf('\r'); + if (crIndex >= 0) { + sb.append(str.substring(0, crIndex)); + sb.append("\n"); + str = str.substring(crIndex+1); + lastCR = true; + } else { + sb.append(str); + str = null; + } + } + area.append(sb.toString()); } public void flush() { diff --git a/jode/jode/type/ArrayType.java b/jode/jode/type/ArrayType.java index 6b89e43..50db1b9 100644 --- a/jode/jode/type/ArrayType.java +++ b/jode/jode/type/ArrayType.java @@ -122,8 +122,9 @@ public class ArrayType extends ClassType { if (type == tNull) return this; if (type.getTypeCode() == TC_ARRAY) { - return tArray(elementType.intersection - (((ArrayType)type).elementType)); + Type elType = elementType.intersection + (((ArrayType)type).elementType); + return elType != tError ? tArray(elType) : tError; } if (type.isSubTypeOf(this)) return this; @@ -138,7 +139,7 @@ public class ArrayType extends ClassType { public Type getGeneralizedType(Type type) { /* tArray(x), tNull -> tArray(x) * tArray(x), tClass(y) -> common ifaces of tArray and tClass - * tArray(x), tArray(y) -> tArray(x.intersection(y)) + * tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject * tArray(x), other -> tError */ if (type.getTypeCode() == TC_RANGE) { @@ -146,10 +147,11 @@ public class ArrayType extends ClassType { } if (type == tNull) return this; - if (type instanceof ArrayType) - return tArray(elementType.intersection - (((ArrayType)type).elementType)); - + if (type.getTypeCode() == TC_ARRAY) { + Type elType = elementType.intersection + (((ArrayType)type).elementType); + return elType != tError ? tArray(elType) : tObject; + } if (!(type instanceof ReferenceType)) return tError; diff --git a/jode/jode/type/ClassType.java b/jode/jode/type/ClassType.java index a00c8c7..7c00aea 100644 --- a/jode/jode/type/ClassType.java +++ b/jode/jode/type/ClassType.java @@ -271,6 +271,8 @@ public abstract class ClassType extends ReferenceType { private final static Hashtable keywords = new Hashtable(); static { + keywords.put("package", Boolean.TRUE); + keywords.put("import", Boolean.TRUE); keywords.put("if", Boolean.TRUE); keywords.put("else", Boolean.TRUE); keywords.put("for", Boolean.TRUE); diff --git a/jode/jode/util/UnifyHash.java b/jode/jode/util/UnifyHash.java index 1cadcb9..4ff3737 100644 --- a/jode/jode/util/UnifyHash.java +++ b/jode/jode/util/UnifyHash.java @@ -1,4 +1,4 @@ -/* UnifyHash Copyright (C) 1999 Jochen Hoenicke. +/* UnifyHash Copyright (C) 1999-2001 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -186,6 +186,9 @@ public class UnifyHash extends AbstractCollection { } public Iterator iterateHashCode(final int hash) { +///#ifdef JDK12 + cleanUp(); +///#endif return new Iterator() { private int known = modCount; private Bucket nextBucket