diff --git a/jode/ChangeLog b/jode/ChangeLog index 1e1ed4a..8a2d1d4 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -1,3 +1,56 @@ +2001-08-14 Jochen Hoenicke + + * build.xml: test is default. + (release-javadoc): New target. + (release-src): Get from dir test only source files. + (doc-javadoc): More parameters for nicer docu. + +2001-08-12 Jochen Hoenicke + + * net/sf/jode/bytecode/TypeSignature.java: + (getArgumentSize): Renamed to ... + (getParameterSize): ... this. Changed all callers. + (skipType): Made private. + + * net/sf/jode/jvm/CodeVerifier.java: + (initInfo): Use TypeSignature.getParameterTypes instead of skipType. + + * net/sf/jode/jvm/SyntheticAnalyzer.java: + (checkGetClass): Be more lenient with the types, they are already + checked by the CodeVerifier. This is to support jdk-1.4. + + * net/sf/jode/expr/InvokeOperator.java + (dumpExpression): Fixed the check for null outerExpr. + + * net/sf/jode/flow/FlowBlock.java: + (checkConsistent): Allow lastModified in a finally block. + * net/sf/jode/flow/TransformExceptionHandlers.java: Reworked exception + handlers again. This time checked with javac 1.3, javac 1.1 and + jikes. + (checkTryCatchOrder): New method that was previously part of + analyze. + (analyze): Use checkTryCatchOrder. Don't merge try and catch flow + blocks anymore, leave it to the analyzeXXX methods. + (mergeTryCatch): New method. + (analyzeCatchBlock): Get catchFlow as parameter. Call + mergeTryCatch. + (transformSubroutine): Handle POP-only subroutines. + (removeJSR): Don't do special case for catchBlock any more. This + is because catchFlow isn't yet merged when this method is called. + (checkAndRemoveJSR): Likewise. + (checkAndRemoveMonitorExit): Likewise. Merge subroutine only if + we are the only predecessor. + (analyzeSynchronized): Get catchFlow as parameter. Call + mergeTryCatch. + (mergeFinallyBlocks): New method, calls mergeTryCatch and does the + common part of mergeFinally and mergeSpecialFinally. + (analyzeFinally): Simplified, after checking and removing JSR, it + does immediately analyze and transform subroutine to get the + finallyBlock. Then it throws away the catchFlow and calls + mergeFinallyBlocks. + (analyzeSpecialFinally): Simplified, after checking it only handles + the jumps in the try part and then call mergeFinallyBlocks. + 2001-08-08 Jochen Hoenicke More Documentation updates. * build.xml: Release rules. @@ -304,7 +357,7 @@ type can't be intersected, return tObject as common super type. 2001-07-15 Jochen Hoenicke - Applied patch from Java 1.1 tree: + Applied patch from Jode 1.1 tree: * jode/expr/Expression.java (updateParentTypes): Call setType, instead of merging the types. Other childs want to know about the @@ -317,7 +370,7 @@ innermost. 2001-07-14 Jochen Hoenicke - Applied patches from the Java 1.1 tree: + Applied patches from the Jode 1.1 tree: * jode/decompiler/TabbedPrintWriter.java: Better gnu style handling: (openBraceClass) (closeBraceClass) diff --git a/jode/build.xml b/jode/build.xml index 153a52e..d22a039 100644 --- a/jode/build.xml +++ b/jode/build.xml @@ -19,7 +19,7 @@ --> - + @@ -32,7 +32,7 @@ - + @@ -164,7 +164,7 @@ - + @@ -221,7 +221,9 @@ - + + + @@ -231,6 +233,14 @@ + + + + + + + @@ -254,8 +264,15 @@ + + + diff --git a/jode/config.props b/jode/config.props index a78fa1d..3fd79b1 100644 --- a/jode/config.props +++ b/jode/config.props @@ -1,10 +1,11 @@ # Do you have online access for generating javadoc? # If not, where are your local files. javadoc.offline=false -#javadoc.href=http://java.sun.com/products/jdk/1.2/docs/api/ javadoc.packagelistLoc= +javadoc.href=http://java.sun.com/products/jdk/1.2/docs/api/ +#javadoc.href=file:/usr/doc/inet/java/jdk1.2/docs/api + #javadoc.offline=true -javadoc.href=file:/usr/doc/inet/java/jdk1.2/docs/api #javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api # Is Perl installed on your system? diff --git a/jode/doc/feedback.php b/jode/doc/feedback.php index a165044..e415db7 100644 --- a/jode/doc/feedback.php +++ b/jode/doc/feedback.php @@ -7,7 +7,7 @@ Please send me a short notice if you add a bug.

You can contact me per email via hoenicke at -users.sourceforge.net. Please mention jode in the +users.sourceforge.net. Please mention Jode in the subject.

There is a mailing list. Check this page for subscription informations.

diff --git a/jode/doc/footer.inc b/jode/doc/footer.inc index c97e581..1294ad1 100644 --- a/jode/doc/footer.inc +++ b/jode/doc/footer.inc @@ -3,7 +3,7 @@ All trademarks and copyrights on this page are properties of their respective owners.
Last updated on 3-Jul-2000, - Copyright © 1998-2000 by Jochen Hoenicke.
+ Copyright © 1998-2001 by Jochen Hoenicke.
Canonic URL is http://jode.sourceforge.net/
diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java index 7b65077..b388658 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlockReader.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlockReader.java @@ -693,7 +693,7 @@ class BasicBlockReader implements Opcodes { throw new ClassFormatException ("Illegal call of special method "+ref); int nargs = input.readUnsignedByte(); - if (TypeSignature.getArgumentSize(ref.getType()) + if (TypeSignature.getParameterSize(ref.getType()) != nargs - 1) throw new ClassFormatException ("Interface nargs mismatch: "+ref+" vs. "+nargs); @@ -890,7 +890,7 @@ class BasicBlockReader implements Opcodes { GlobalOptions.err.println("Illegal LVT length, ignoring it"); return; } - Vector[] lvt = new Vector[bb.maxLocals]; + Vector[] lvt = new Vector[maxLocals]; for (int i=0; i < count; i++) { LVTEntry lve = new LVTEntry(); lve.start = input.readUnsignedShort(); @@ -899,7 +899,8 @@ class BasicBlockReader implements Opcodes { int typeIndex = input.readUnsignedShort(); int slot = input.readUnsignedShort(); if (nameIndex == 0 || cp.getTag(nameIndex) != cp.UTF8 - || typeIndex == 0 || cp.getTag(typeIndex) != cp.UTF8) { + || typeIndex == 0 || cp.getTag(typeIndex) != cp.UTF8 + || slot >= maxLocals) { // This is probably an evil lvt as created by HashJava // simply ignore it. diff --git a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java index b820929..83960aa 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlockWriter.java @@ -894,7 +894,8 @@ class BasicBlockWriter implements Opcodes { output.writeShort (gcp.putRef(gcp.INTERFACEMETHODREF, ref)); output.writeByte - (TypeSignature.getArgumentSize(ref.getType()) + 1); + (TypeSignature + .getParameterSize(ref.getType()) + 1); output.writeByte(0); } else output.writeShort(gcp.putRef(gcp.METHODREF, ref)); diff --git a/jode/src/net/sf/jode/bytecode/BasicBlocks.java b/jode/src/net/sf/jode/bytecode/BasicBlocks.java index ba12142..42306d3 100644 --- a/jode/src/net/sf/jode/bytecode/BasicBlocks.java +++ b/jode/src/net/sf/jode/bytecode/BasicBlocks.java @@ -132,7 +132,7 @@ public class BasicBlocks extends BinaryInfo implements Opcodes { public BasicBlocks(MethodInfo mi) { methodInfo = mi; int paramSize = (mi.isStatic() ? 0 : 1) - + TypeSignature.getArgumentSize(mi.getType()); + + TypeSignature.getParameterSize(mi.getType()); paramInfos = new LocalVariableInfo[paramSize]; for (int i=0; i< paramSize; i++) paramInfos[i] = LocalVariableInfo.getInfo(i); diff --git a/jode/src/net/sf/jode/bytecode/Reference.java b/jode/src/net/sf/jode/bytecode/Reference.java index 1c1a8a8..d2cf195 100644 --- a/jode/src/net/sf/jode/bytecode/Reference.java +++ b/jode/src/net/sf/jode/bytecode/Reference.java @@ -24,7 +24,8 @@ import java.util.Iterator; ///#enddef /** - * This class represents a field or method reference. + * This class represents a field or method reference. It consists of + * the class name the method/field name and the type signature. */ public class Reference { /** diff --git a/jode/src/net/sf/jode/bytecode/ReferenceInstruction.java b/jode/src/net/sf/jode/bytecode/ReferenceInstruction.java index 021c510..5c3179b 100644 --- a/jode/src/net/sf/jode/bytecode/ReferenceInstruction.java +++ b/jode/src/net/sf/jode/bytecode/ReferenceInstruction.java @@ -61,7 +61,7 @@ class ReferenceInstruction extends Instruction { case opc_invokestatic: case opc_invokeinterface: poppush[0] = opcode != opc_invokestatic ? 1 : 0; - poppush[0] += TypeSignature.getArgumentSize(typeSig); + poppush[0] += TypeSignature.getParameterSize(typeSig); poppush[1] = TypeSignature.getReturnSize(typeSig); break; diff --git a/jode/src/net/sf/jode/bytecode/TypeSignature.java b/jode/src/net/sf/jode/bytecode/TypeSignature.java index a088afe..9f08e59 100644 --- a/jode/src/net/sf/jode/bytecode/TypeSignature.java +++ b/jode/src/net/sf/jode/bytecode/TypeSignature.java @@ -21,7 +21,37 @@ package net.sf.jode.bytecode; import net.sf.jode.util.UnifyHash; /** - * This class contains some static methods to handle type signatures. + * This class contains some static methods to handle type signatures.
+ * + * A type signature is a compact textual representation of a java + * types. It is described in the Java Virtual Machine Specification. + * Primitive types have a one letter type signature. Type signature + * of classes contains the class name. Type signatures for arrays and + * methods are recursively build from the type signatures of their + * elements.
+ * + * Here are a few examples: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
type signatureJava type
Zboolean
Bbyte
Sshort
Cchar
Iint
Ffloat
Jlong
Ddouble
Ljava/lang/Object;java.lang.Object
[[Iint[][]
(Ljava/lang/Object;I)Vmethod with argument types Object and + * int and void return type.
()I method without arguments + * and int return type.
+ * + * @author Jochen Hoenicke */ public class TypeSignature { /** @@ -61,22 +91,20 @@ public class TypeSignature { } /** - * Generate the signature for the given Class. + * Generates the type signature of the given Class. * @param clazz a java.lang.Class, this may also be a primitive or * array type. - * @return the type signature (see section 4.3.2 Field Descriptors - * of the JVM specification) + * @return the type signature. */ public static String getSignature(Class clazz) { return appendSignature(new StringBuffer(), clazz).toString(); } /** - * Generate a method signature. + * Generates a method signature. * @param paramT the java.lang.Class of the parameter types of the method. * @param returnT the java.lang.Class of the return type of the method. - * @return the method signature (see section 4.3.3 Method Descriptors - * of the JVM specification) + * @return the method type signature */ public static String getSignature(Class paramT[], Class returnT) { StringBuffer sig = new StringBuffer("("); @@ -86,8 +114,8 @@ public class TypeSignature { } /** - * Generate a Class for a type signature. This is the pendant to - * getSignature. + * Generates a Class object for a type signature. This is the + * inverse function of getSignature. * @param typeSig a single type signature * @return the Class object representing that type. */ @@ -124,25 +152,43 @@ public class TypeSignature { } /** - * Check if the given type is a two slot type. */ + * Check if the given type is a two slot type. The only two slot + * types are long and double. + */ private static boolean usingTwoSlots(char type) { return "JD".indexOf(type) >= 0; } /** * Returns the number of words, an object of the given simple type - * signature takes. + * signature takes. For long and double this is two, for all other + * types it is one. */ public static int getTypeSize(String typeSig) { return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1; } + /** + * Gets the element type of an array. + * @param typeSig type signature of the array. + * @return type signature for the element type. + * @exception IllegalArgumentException if typeSig is not an array + * type signature. + */ public static String getElementType(String typeSig) { if (typeSig.charAt(0) != '[') throw new IllegalArgumentException(); return typeSig.substring(1); } + /** + * Gets the ClassInfo for a class type. + * @param classpath the classpath in which the ClassInfo is searched. + * @param typeSig type signature of the class. + * @return the ClassInfo object for the class. + * @exception IllegalArgumentException if typeSig is not an class + * type signature. + */ public static ClassInfo getClassInfo(ClassPath classpath, String typeSig) { if (typeSig.charAt(0) != 'L') throw new IllegalArgumentException(); @@ -150,7 +196,13 @@ public class TypeSignature { (typeSig.substring(1, typeSig.length()-1).replace('/', '.')); } - public static int skipType(String methodTypeSig, int position) { + /** + * Skips the next entry of a method type signature + * @param methodTypeSig type signature of the method. + * @param position the index to the last entry. + * @return the index to the next entry. + */ + static int skipType(String methodTypeSig, int position) { char c = methodTypeSig.charAt(position++); while (c == '[') c = methodTypeSig.charAt(position++); @@ -160,10 +212,13 @@ public class TypeSignature { } /** - * Returns the number of words, the arguments for the given method - * type signature takes. + * Gets the number of words the parameters for the given method + * type signature takes. This is the sum of getTypeSize() for + * each parameter type. + * @param methodTypeSig the method type signature. + * @return the number of words the parameters take. */ - public static int getArgumentSize(String methodTypeSig) { + public static int getParameterSize(String methodTypeSig) { int nargs = 0; int i = 1; for (;;) { @@ -179,8 +234,11 @@ public class TypeSignature { } /** - * Returns the number of words, an object of the given simple type - * signature takes. + * Gets the size of the return type of the given method in words. + * This is zero for void return type, two for double or long return + * type and one otherwise. + * @param methodTypeSig the method type signature. + * @return the size of the return type in words. */ public static int getReturnSize(String methodTypeSig) { int length = methodTypeSig.length(); @@ -195,8 +253,9 @@ public class TypeSignature { } /** - * Returns the number of words, an object of the given simple type - * signature takes. + * Gets the parameter type signatures of the given method signature. + * @param methodTypeSig the method type signature. + * @return an array containing all parameter types in correct order. */ public static String[] getParameterTypes(String methodTypeSig) { int pos = 1; @@ -215,6 +274,26 @@ public class TypeSignature { return params; } + /** + * Gets the return type for a method signature + * @param methodTypeSig the method signature. + * @return the return type for a method signature, `V' for void methods. + */ + public static String getReturnType(String methodTypeSig) { + return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1); + } + + /** + * Gets the default value an object of the given type has. It is + * null for objects and arrays, Integer(0) for boolean and short + * integer types or Long(0L), Double(0.0), Float(0.0F) for long, + * double and float. This seems strange, but this way the type + * returned is the same as for FieldInfo.getConstant(). + * + * @param typeSig the type signature. + * @return the default value. + * @exception IllegalArgumentException if this is a method type signature. + */ public static Object getDefaultValue(String typeSig) { switch(typeSig.charAt(0)) { case 'Z': @@ -238,15 +317,7 @@ public class TypeSignature { } /** - * Returns the number of words, an object of the given simple type - * signature takes. - */ - public static String getReturnType(String methodTypeSig) { - return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1); - } - - /** - * Check if there is a valid class name starting at index + * Checks if there is a valid class name starting at index * in string typesig and ending with a semicolon. * @return the index at which the class name ends. * @exception IllegalArgumentException if there was an illegal character. @@ -266,7 +337,7 @@ public class TypeSignature { } /** - * Check if there is a valid simple type signature starting at index + * Checks if there is a valid simple type signature starting at index * in string typesig. * @return the index at which the type signature ends. * @exception IllegalArgumentException if there was an illegal character. @@ -285,6 +356,14 @@ public class TypeSignature { return index; } + /** + * Checks whether a given type signature is a valid (not method) + * type signature. Throws an exception otherwise. + * @param typeSig the type signature. + * @exception NullPointerException if typeSig is null. + * @exception IllegalArgumentException if typeSig is not a valid + * type signature or if it's a method type signature. + */ public static void checkTypeSig(String typesig) throws IllegalArgumentException { @@ -298,6 +377,14 @@ public class TypeSignature { } } + /** + * Checks whether a given type signature is a valid method + * type signature. Throws an exception otherwise. + * @param typeSig the type signature. + * @exception NullPointerException if typeSig is null. + * @exception IllegalArgumentException if typeSig is not a valid + * method type signature. + */ public static void checkMethodTypeSig(String typesig) throws IllegalArgumentException { diff --git a/jode/src/net/sf/jode/bytecode/package.html b/jode/src/net/sf/jode/bytecode/package.html index 758de1d..dcf3e90 100644 --- a/jode/src/net/sf/jode/bytecode/package.html +++ b/jode/src/net/sf/jode/bytecode/package.html @@ -18,7 +18,7 @@ along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --> -JODE Bytecode Package +Jode Bytecode Package @@ -71,11 +71,36 @@ You can also use this package to create and write new classes: ... +

Advantages of this bytecode package

+
    +
  • You don't need to think of the constant pool, except when you want +to write your custom attributes.
  • +
  • The set of opcodes is drastically reduced: For example you don't +have to handle 20 different opcodes that all push a constant value on +the stack. When reading it will automatically convert them to +ldc or ldc2 and on writing it will convert +them back.
  • +
  • Wide instructions are automatically generated when needed, large +methods are supported.
  • +
  • The code is organized in {@link net.sf.jode.bytecode.BasicBlocks} +which makes flow analysis much easier.
  • +
  • The memory consumption is quite moderate.
  • +
+ +

Disadvantages

+
    +
  • You can't change every byte. For example Jode decides itself if +a lookup switch or table switch is generated.
  • +
  • Jode does a lot of checks when reading the bytecode and it is +impossible to recover from errors. This makes it sometime hard to +find out why the bytecode of a particular class files is invalid.
  • +
+
Jochen Hoenicke
- + -Last modified: Sun Aug 5 17:53:03 MEST 2001 +Last modified: Sat Aug 11 18:44:19 MEST 2001 diff --git a/jode/src/net/sf/jode/expr/InvokeOperator.java b/jode/src/net/sf/jode/expr/InvokeOperator.java index 9b7efa3..6bb634e 100644 --- a/jode/src/net/sf/jode/expr/InvokeOperator.java +++ b/jode/src/net/sf/jode/expr/InvokeOperator.java @@ -1033,7 +1033,8 @@ public final class InvokeOperator extends Operator } } else { qualifiedNew = true; - if (outerExpr.getType() instanceof NullType) { + if (outerExpr.getType().getCanonic() + instanceof NullType) { writer.print("("); writer.startOp(writer.EXPL_PAREN, 1); writer.print("("); diff --git a/jode/src/net/sf/jode/flow/FlowBlock.java b/jode/src/net/sf/jode/flow/FlowBlock.java index 03dc7c4..89ecb9e 100644 --- a/jode/src/net/sf/jode/flow/FlowBlock.java +++ b/jode/src/net/sf/jode/flow/FlowBlock.java @@ -1,4 +1,4 @@ -/* FlowBlock Copyright (C) 1998-1999 Jochen Hoenicke. +/* FlowBlock 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 @@ -211,6 +211,9 @@ public class FlowBlock { * @return The remaining jumps, that couldn't be resolved. */ public Jump resolveSomeJumps(Jump jumps, FlowBlock succ) { + if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0) + GlobalOptions.err.println("before Resolve: "+this); + /* We will put all jumps that we can not resolve into this * linked list. */ @@ -721,7 +724,7 @@ public class FlowBlock { * begin of successor. * @param kills The slots that are always overwritten on the way to * successor. - * @return The variables that must be defined * in this block. + * @return The variables that must be defined in this block. */ void updateInOut(FlowBlock successor, VariableSet gens, SlotSet kills) { successor.updateGenKill(gens, kills); @@ -805,7 +808,9 @@ public class FlowBlock { try { if (block.outer != null || block.flowBlock != this) { - throw new InternalError("Inconsistency: outer:"+block.outer+" block.flow"+block.flowBlock +" this: "+this); + throw new InternalError("Inconsistency: outer:" + block.outer + + " block.flow"+block.flowBlock + + " this: "+this); } block.checkConsistent(); @@ -820,7 +825,8 @@ public class FlowBlock { StructuredBlock last = lastModified; while (last.outer instanceof SequentialBlock - || last.outer instanceof TryBlock) + || last.outer instanceof TryBlock + || last.outer instanceof FinallyBlock) last = last.outer; if (last.outer != null) throw new InternalError("Inconsistency"); @@ -999,8 +1005,6 @@ public class FlowBlock { /* Update the in/out-Vectors now */ updateInOut(succ, succInfo.gen, succInfo.kill); - if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0) - GlobalOptions.err.println("before Resolve: "+this); /* Try to eliminate as many jumps as possible. */ @@ -1055,9 +1059,6 @@ public class FlowBlock { jumps = jump; } - if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0) - GlobalOptions.err.println("before resolve: "+this); - /* Try to eliminate as many jumps as possible. */ jumps = resolveSomeJumps(jumps, END_OF_METHOD); diff --git a/jode/src/net/sf/jode/flow/StructuredBlock.java b/jode/src/net/sf/jode/flow/StructuredBlock.java index 820662f..f1c13ec 100644 --- a/jode/src/net/sf/jode/flow/StructuredBlock.java +++ b/jode/src/net/sf/jode/flow/StructuredBlock.java @@ -60,14 +60,13 @@ import java.util.Set; public abstract class StructuredBlock { /* Invariants: - * in.intersection(out) = empty - * outer != null => flowBlock = outer.flowBlock - * outer == null => flowBlock.block = this - * jump == null => outer != null - * either getNextBlock() != null - * or getNextFlowBlock() != null or outer == null - * either outer.getNextBlock(this) != null - * or outer.getNextFlowBlock(this) != null + * outer != null ==> flowBlock = outer.flowBlock; + * outer == null ==> flowBlock.block = this; + * jump == null ==> outer != null; + * getNextBlock() != null ^ getNextFlowBlock() != null; + * outer != null ==> + * outer.getNextBlock(this) != null + * ^ outer.getNextFlowBlock(this) != null; */ /** diff --git a/jode/src/net/sf/jode/flow/TransformExceptionHandlers.java b/jode/src/net/sf/jode/flow/TransformExceptionHandlers.java index a8e7716..c91aa0c 100644 --- a/jode/src/net/sf/jode/flow/TransformExceptionHandlers.java +++ b/jode/src/net/sf/jode/flow/TransformExceptionHandlers.java @@ -103,35 +103,62 @@ public class TransformExceptionHandlers { handlers.add(new Handler(tryBlock, endBlock, catchBlock, type)); } + /** + * Merge the try flow block with the catch flow block. This is a kind + * of special T2 transformation, as all jumps to the catch block are + * implicit (exception can be thrown everywhere).
+ * + * This method doesn't actually merge the contents of the blocks. The + * caller should do it right afterwards.
+ * + * The flow block catchFlow mustn't have any predecessors. + * @param tryFlow the flow block containing the try. + * @param catchFlow the flow block containing the catch handler. + */ + static void mergeTryCatch(FlowBlock tryFlow, FlowBlock catchFlow) { + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_ANALYZE) != 0) + GlobalOptions.err.println + ("mergeTryCatch(" + tryFlow.getBlockNr() + + ", " + catchFlow.getBlockNr() + ")"); + tryFlow.updateInOutCatch(catchFlow); + tryFlow.mergeSuccessors(catchFlow); + tryFlow.mergeBlockNr(catchFlow); + } - /* simple try catch block: + + /** + * Analyzes a simple try/catch block. The try and catch part are both + * analyzed, the try block is already created, but the catch block + * isn't.
+ * The catchFlow block mustn't have any predecessors. * - * try-header - * |- first instruction - * | ... - * | last instruction - * |- optional jump (last+1) - * | ... - * `- catch block + * @param type The type of the exception which is caught. + * @param tryFlow The flow block containing the try. The contained + * block must be a try block. + * @param catchFlow the flow block containing the catch handler. */ static void analyzeCatchBlock(Type type, FlowBlock tryFlow, - StructuredBlock catchBlock) { + FlowBlock catchFlow) { + /* Merge try and catch flow blocks */ + mergeTryCatch(tryFlow, catchFlow); + + /* Insert catch block into tryFlow */ CatchBlock newBlock = new CatchBlock(type); ((TryBlock)tryFlow.block).addCatchBlock(newBlock); - newBlock.setCatchBlock(catchBlock); + newBlock.setCatchBlock(catchFlow.block); + tryFlow.lastModified = tryFlow.block; } - /* And now the complicated parts. */ - /** - * This transforms a sub routine, that is checks if the beginning - * local assignment matches the final ret and then returns. + * This transforms a sub routine, i.e. it checks if the beginning + * local assignment matches the final ret and removes both. It also + * accepts sub routines that just pop their return address. */ - boolean transformSubRoutine(StructuredBlock subRoutine) { - if (!(subRoutine instanceof SequentialBlock)) - return false; - SequentialBlock sequBlock = (SequentialBlock) subRoutine; - StructuredBlock firstBlock = sequBlock.getSubBlocks()[0]; + boolean transformSubRoutine(StructuredBlock subRoutineBlock) { + StructuredBlock firstBlock = subRoutineBlock; + if (firstBlock instanceof SequentialBlock) + firstBlock = subRoutineBlock.getSubBlocks()[0]; LocalInfo local = null; if (firstBlock instanceof SpecialBlock) { @@ -156,24 +183,25 @@ public class TransformExceptionHandlers { } else return false; - /* We are now committed. Remove the first Statement which - * stores/removes the return address. + /* We are now committed and can start changing code. Remove + * the first Statement which stores/removes the return + * address. */ firstBlock.removeBlock(); - /* XXX - Replace any RET with a jump to end of this flow block. + /* We don't check if there is a RET in the middle. * * This is a complicated task which isn't needed for javac nor * jikes. We just check if the last instruction is a ret and - * replace this. This will never produce code with wrong semantic, + * remove this. This will never produce code with wrong semantic, * as long as the bytecode was verified correctly. */ - while (sequBlock.subBlocks[1] instanceof SequentialBlock) - sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; + while (subRoutineBlock instanceof SequentialBlock) + subRoutineBlock = subRoutineBlock.getSubBlocks()[1]; - if (sequBlock.subBlocks[1] instanceof RetBlock - && (((RetBlock) sequBlock.subBlocks[1]).local.equals(local))) { - sequBlock.subBlocks[1].removeBlock(); + if (subRoutineBlock instanceof RetBlock + && (((RetBlock) subRoutineBlock).local.equals(local))) { + subRoutineBlock.removeBlock(); } return true; } @@ -203,51 +231,32 @@ public class TransformExceptionHandlers { } /** - * 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. + * Remove the JSRs jumping to the specified subRoutine. The right + * JSRs are marked and we can just remove them. For the other JSR + * instructions we replace them 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, - FlowBlock subRoutine) { - Jump nextJump; - for (Jump jumps = tryFlow.getJumps(subRoutine); - jumps != null; jumps = nextJump) { + private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) { + for (Jump jumps = tryFlow.removeJumps(subRoutine); + jumps != null; jumps = jumps.next) { StructuredBlock prev = jumps.prev; - nextJump = jumps.next; - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock) { - JsrBlock jsr = (JsrBlock) prev.outer; - if (prev.outer == catchBlock) { - /* This is the mandatory jsr in the catch block */ - continue; - } + prev.removeJump(); - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - if (jsr.isGood()) { - StructuredBlock next = jsr.getNextBlock(); - jsr.removeBlock(); - if (next instanceof ReturnBlock) - removeReturnLocal((ReturnBlock) next); - } else { - /* We have a JSR to the subroutine, which is badly placed. - * We complain here. - */ - DescriptionBlock msg - = new DescriptionBlock("ERROR: JSR FINALLY BLOCK!"); - msg.replace(prev.outer); - } - } else { - /* We have a jump to the subroutine, that is wrong. + if (prev instanceof EmptyBlock + && prev.outer instanceof JsrBlock + && ((JsrBlock) prev.outer).isGood()) { + StructuredBlock next = prev.outer.getNextBlock(); + prev.outer.removeBlock(); + if (next instanceof ReturnBlock) + removeReturnLocal((ReturnBlock) next); + } else { + /* We have a jump to the subroutine, that is badly placed. * We complain here. */ - DescriptionBlock msg - = new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); - tryFlow.removeSuccessor(jumps); - prev.removeJump(); + DescriptionBlock msg = new DescriptionBlock + ("ERROR: invalid jump to finally block!"); prev.appendBlock(msg); } } @@ -313,7 +322,6 @@ public class TransformExceptionHandlers { private void checkAndRemoveJSR(FlowBlock tryFlow, - StructuredBlock catchBlock, FlowBlock subRoutine, int startOutExit, int endOutExit) { Iterator iter = tryFlow.getSuccessors().iterator(); @@ -330,8 +338,8 @@ public class TransformExceptionHandlers { StructuredBlock prev = jumps.prev; if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { - /* This jump is really a jsr, since it doesn't - * leave the block forever, we can ignore it. + /* This jump is a jsr, since it doesn't leave the + * block forever, we can ignore it. */ continue; } @@ -382,7 +390,7 @@ public class TransformExceptionHandlers { * Complain! */ DescriptionBlock msg - = new DescriptionBlock("ERROR: NO JSR TO FINALLY"); + = new DescriptionBlock("ERROR: no jsr to finally"); if (pred != null) pred.prependBlock(msg); else { @@ -392,11 +400,10 @@ public class TransformExceptionHandlers { } } if (tryFlow.getSuccessors().contains(subRoutine)) - removeJSR(tryFlow, catchBlock, subRoutine); + removeJSR(tryFlow, subRoutine); } private void checkAndRemoveMonitorExit(FlowBlock tryFlow, - StructuredBlock catchBlock, LocalInfo local, int start, int end) { FlowBlock subRoutine = null; @@ -494,7 +501,7 @@ public class TransformExceptionHandlers { /* Complain! */ DescriptionBlock msg - = new DescriptionBlock("ERROR: NO MONITOREXIT"); + = new DescriptionBlock("ERROR: no monitorexit"); prev.appendBlock(msg); msg.moveJump(jumps); } @@ -502,8 +509,9 @@ public class TransformExceptionHandlers { if (subRoutine != null) { if (tryFlow.getSuccessors().contains(subRoutine)) - removeJSR(tryFlow, catchBlock, subRoutine); - tryFlow.mergeBlockNr(subRoutine); + removeJSR(tryFlow, subRoutine); + if (subRoutine.predecessors.size() == 0) + tryFlow.mergeBlockNr(subRoutine); } } @@ -526,20 +534,25 @@ public class TransformExceptionHandlers { } private boolean analyzeSynchronized(FlowBlock tryFlow, - StructuredBlock catchBlock, + FlowBlock catchFlow, int endHandler) { + /* Check if this is a synchronized block. We mustn't change + * anything until we are sure. + */ + StructuredBlock catchBlock = catchFlow.block; + + /* Check for a optional exception store and skip it */ StoreInstruction excStore = getExceptionStore(catchBlock); if (excStore != null) catchBlock = catchBlock.getSubBlocks()[1]; + /* Check for the monitorexit instruction */ if (!(catchBlock instanceof SequentialBlock && catchBlock.getSubBlocks()[0] instanceof InstructionBlock)) return false; - Expression instr = ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); - if (!(instr instanceof MonitorExitOperator && instr.getFreeOperandCount() == 0 && (((MonitorExitOperator)instr).getSubExpressions()[0] @@ -547,9 +560,9 @@ public class TransformExceptionHandlers { && catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) return false; + /* Check for the throw instruction */ Expression throwInstr = ((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction(); - if (excStore != null) { if (!(throwInstr instanceof Operator && excStore.lvalueMatches((Operator)throwInstr))) @@ -583,6 +596,12 @@ public class TransformExceptionHandlers { * monitorexit local_x * return_n */ + + /* Merge try and catch flow blocks. No need to insert the + * catchFlow.block into the try flow though, since all its + * instruction are synthetic. + */ + mergeTryCatch(tryFlow, catchFlow); MonitorExitOperator monexit = (MonitorExitOperator) ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; @@ -597,7 +616,7 @@ public class TransformExceptionHandlers { + "," + tryFlow.getNextBlockNr() + "," + endHandler + ")"); checkAndRemoveMonitorExit - (tryFlow, catchBlock, local, tryFlow.getNextBlockNr(), endHandler); + (tryFlow, local, tryFlow.getNextBlockNr(), endHandler); SynchronizedBlock syncBlock = new SynchronizedBlock(local); TryBlock tryBlock = (TryBlock) tryFlow.block; @@ -609,8 +628,41 @@ public class TransformExceptionHandlers { return true; } + /** + * Merge try and finally flow blocks. + * @param tryFlow The try flow block. Its contained block must be + * a try block. + * @param catchFlow The catch flow block that contains the finally + * block. + * @param finallyBlock block that either contains the finally block. + * It is part of the catchFlow. The other parts of catchFlow are + * synthetic and can be removed. + */ + private void mergeFinallyBlock(FlowBlock tryFlow, FlowBlock catchFlow, + StructuredBlock finallyBlock) { + TryBlock tryBlock = (TryBlock) tryFlow.block; + if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { + /* A try { try { } catch {} } finally{} is equivalent + * to a try {} catch {} finally {} + * so remove the surrounding tryBlock. + */ + TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; + innerTry.gen = tryBlock.gen; + innerTry.replace(tryBlock); + tryBlock = innerTry; + tryFlow.lastModified = tryBlock; + tryFlow.block = tryBlock; + } + + /* Now merge try and catch flow blocks */ + mergeTryCatch(tryFlow, catchFlow); + FinallyBlock newBlock = new FinallyBlock(); + newBlock.setCatchBlock(finallyBlock); + tryBlock.addCatchBlock(newBlock); + } + private boolean analyzeFinally(FlowBlock tryFlow, - StructuredBlock catchBlock, int end) { + FlowBlock catchFlow, int end) { /* Layout of a try-finally block: * @@ -632,6 +684,7 @@ public class TransformExceptionHandlers { * return_n */ + StructuredBlock catchBlock = catchFlow.block; StoreInstruction excStore = getExceptionStore(catchBlock); if (excStore == null) return false; @@ -643,9 +696,10 @@ public class TransformExceptionHandlers { StructuredBlock finallyBlock = null; if (catchBlock.getSubBlocks()[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: + /* In case the try block has no exit (that means, it + * throws an exception or loops forever), the finallyBlock + * was already merged with the catchBlock. We have to + * check for this case separately: * * do { * JSR @@ -682,7 +736,7 @@ public class TransformExceptionHandlers { FlowBlock subRoutine; if (finallyBlock != null) { - /* Check if the jsr breaks (see two comments above). We don't + /* Check if the jsr breaks (see comment 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. */ @@ -690,269 +744,255 @@ public class TransformExceptionHandlers { return false; /* Check if the try block has no exit - * XXX - Unfortunately the try block already has the - * successors of catch block. */ -// if (tryFlow.getSuccessors().size() > 0) -// return false; + if (tryFlow.getSuccessors().size() > 0) + return false; catchBlock = finallyBlock; subRoutine = null; } else { if (!(jsrBlock.innerBlock instanceof EmptyBlock)) return false; - catchBlock = jsrBlock; - subRoutine = jsrBlock.innerBlock.jump.destination; - checkAndRemoveJSR(tryFlow, catchBlock, subRoutine, - tryFlow.getNextBlockNr(), - subRoutine.getBlockNr()); - } - - /* Wow that was complicated :-) - * But now we know that the catch block looks - * exactly like it should: - * - * local_n = POP - * catchBlock: - * JSR - * finally - * throw local_n - */ + finallyBlock = jsrBlock.innerBlock; + subRoutine = finallyBlock.jump.destination; - TryBlock tryBlock = (TryBlock) tryFlow.block; - if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { - /* remove the surrounding tryBlock */ - TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; - innerTry.gen = tryBlock.gen; - innerTry.replace(tryBlock); - tryBlock = innerTry; - tryFlow.lastModified = tryBlock; - tryFlow.block = tryBlock; - } - FinallyBlock newBlock = new FinallyBlock(); - newBlock.setCatchBlock(catchBlock); - tryBlock.addCatchBlock(newBlock); + /* We are committed now and can start changing the try + * block. + */ + checkAndRemoveJSR(tryFlow, subRoutine, + tryFlow.getNextBlockNr(), end); - if (subRoutine != null) { - while (subRoutine.analyze(tryFlow.getNextBlockNr(), end)); - /* Now check if the subroutine is correct and has only the - * catchFlow as predecessor. + /* Now analyze and transform the subroutine. */ - if (subRoutine.predecessors.size() == 1 - && transformSubRoutine(subRoutine.block)) { - - tryFlow.removeSuccessor(jsrBlock.innerBlock.jump); - tryFlow.mergeBlockNr(subRoutine); - tryFlow.mergeSuccessors(subRoutine); - subRoutine.block.replace(catchBlock); - tryFlow.updateInOutCatch(subRoutine); + while (subRoutine.analyze(tryFlow.getNextBlockNr(), end)); + if (subRoutine.predecessors.size() == 1) { + /* catchFlow is synthetic, so we can safely remove it + * here. + */ + subRoutine.mergeBlockNr(catchFlow); + catchFlow = subRoutine; + + if (!transformSubRoutine(subRoutine.block)) { + finallyBlock = subRoutine.block; + DescriptionBlock msg = new DescriptionBlock + ("ERROR: Missing return address handling"); + StructuredBlock subblock = subRoutine.block; + msg.replace(finallyBlock); + msg.appendBlock(finallyBlock); + } + finallyBlock = subRoutine.block; } } + + /* Now finish it. + */ + mergeFinallyBlock(tryFlow, catchFlow, finallyBlock); return true; } private boolean analyzeSpecialFinally(FlowBlock tryFlow, - StructuredBlock catchBlock, - int end) { + FlowBlock catchFlow, int end) { + StructuredBlock finallyBlock = catchFlow.block; StructuredBlock firstInstr = - catchBlock instanceof SequentialBlock - ? catchBlock.getSubBlocks()[0]: catchBlock; + finallyBlock instanceof SequentialBlock + ? finallyBlock.getSubBlocks()[0]: finallyBlock; if (!(firstInstr instanceof SpecialBlock && ((SpecialBlock)firstInstr).type == SpecialBlock.POP && ((SpecialBlock)firstInstr).count == 1)) return false; - /* This may be a special try/finally-block, where + /* This is a special try/finally-block, where * the finally block ends with a break, return or * similar. */ - FlowBlock succ = null; - /* remove the pop now */ - if (catchBlock instanceof SequentialBlock) - catchBlock = catchBlock.getSubBlocks()[1]; + /* Make sure that resolveJump only works on the inside of the try + */ + tryFlow.lastModified = tryFlow.block.getSubBlocks()[0]; + if (finallyBlock instanceof SequentialBlock) + finallyBlock = finallyBlock.getSubBlocks()[1]; else { - catchBlock = new EmptyBlock(); - catchBlock.moveJump(firstInstr.jump); + finallyBlock = new EmptyBlock(); + finallyBlock.moveJump(firstInstr.jump); - succ = firstInstr.jump.destination; + /* Handle the jumps in the tryFlow to finallyFlow. + */ + FlowBlock finallyFlow = finallyBlock.jump.destination; + if (tryFlow.getSuccessors().contains(finallyFlow)) { + Jump jumps = tryFlow.removeJumps(finallyFlow); + jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow); + tryFlow.resolveRemaining(jumps); + } } -// Set trySuccs = tryFlow.getSuccessors(); -// if (trySuccs.size() > 1 -// || (trySuccs.size() == 1 -// && trySuccs.iterator().next() != succ)) -// return false; + /* Complain about all other jumps in try block */ + Set trySuccs = tryFlow.getSuccessors(); + for (Iterator i = trySuccs.iterator(); i.hasNext(); ) { + for (Jump jumps = tryFlow.getJumps((FlowBlock) i.next()); + jumps != null; jumps = jumps.next) { + DescriptionBlock msg = + new DescriptionBlock + ("ERROR: doesn't go through finally block!"); + if (jumps.prev instanceof ReturnBlock) { + msg.replace(jumps.prev); + msg.appendBlock(jumps.prev); + } else { + jumps.prev.appendBlock(msg); + msg.moveJump(jumps); + } + } + } - if (succ != null) { - /* Handle the jumps in the tryFlow. - */ - Jump jumps = tryFlow.removeJumps(succ); - jumps = tryFlow.resolveSomeJumps(jumps, succ); - tryFlow.resolveRemaining(jumps); - } - - TryBlock tryBlock = (TryBlock)tryFlow.block; - if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { - /* remove the unnecessary tryBlock */ - TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; - innerTry.gen = tryBlock.gen; - innerTry.replace(tryBlock); - tryBlock = innerTry; - tryFlow.lastModified = innerTry; - } - FinallyBlock newBlock = new FinallyBlock(); - tryBlock.addCatchBlock(newBlock); - newBlock.setCatchBlock(catchBlock); + mergeFinallyBlock(tryFlow, catchFlow, finallyBlock); + /* Following code will work be put inside the finallyBlock */ + tryFlow.lastModified = finallyBlock; return true; } - /** - * Analyzes all exception handlers to try/catch/finally or - * synchronized blocks. - */ - public void analyze() { + void checkTryCatchOrder() { /* Check if try/catch ranges are okay. The following succeeds * for all classes generated by the sun java compiler, but hand * optimized classes (or generated by other compilers) will fail. */ - { - Handler last = null; - for (Iterator i = handlers.iterator(); i.hasNext(); ) { - Handler exc = (Handler) i.next(); - int start = exc.start.getBlockNr(); - int end = exc.end.getBlockNr(); - int handler = exc.handler.getBlockNr(); - if (start > end || handler <= end) - throw new InternalError + Handler last = null; + for (Iterator i = handlers.iterator(); i.hasNext(); ) { + Handler exc = (Handler) i.next(); + int start = exc.start.getBlockNr(); + int end = exc.end.getBlockNr(); + int handler = exc.handler.getBlockNr(); + if (start > end || handler <= end) + throw new InternalError ("ExceptionHandler order failed: not " + start + " < " + end + " <= " + handler); - if (last != null - && (last.start.getBlockNr() != start - || last.end.getBlockNr() != end)) { - /* The last handler does catch another range. - * Due to the order: - * start < last.start.getBlockNr() - * || end > last.end.getBlockNr() - */ - if (end >= last.start.getBlockNr() - && end < last.end.getBlockNr()) - throw new InternalError - ("Exception handlers ranges are intersecting: [" - + last.start.getBlockNr()+", " - + last.end.getBlockNr()+"] and [" - + start+", "+end+"]."); - } - last = exc; + if (last != null + && (last.start.getBlockNr() != start + || last.end.getBlockNr() != end)) { + /* The last handler does catch another range. + * Due to the order: + * start < last.start.getBlockNr() + * || end > last.end.getBlockNr() + */ + if (end >= last.start.getBlockNr() + && end < last.end.getBlockNr()) + throw new InternalError + ("Exception handlers ranges are intersecting: [" + + last.start.getBlockNr()+", " + + last.end.getBlockNr()+"] and [" + + start+", "+end+"]."); } + last = exc; } + } - { - Iterator i = handlers.iterator(); - Handler exc = null; - Handler next = i.hasNext() ? (Handler) i.next() : null; - while(next != null) { - Handler last = exc; - exc = next; - int startNr = exc.start.getBlockNr(); - int endNr = exc.end.getBlockNr(); - next = i.hasNext() ? (Handler) i.next() : null; - int endHandler = Integer.MAX_VALUE; - /* If the next exception handler catches a bigger range - * it must surround the handler completely. + /** + * Analyzes all exception handlers to try/catch/finally or + * synchronized blocks. + */ + public void analyze() { + checkTryCatchOrder(); + + Iterator i = handlers.iterator(); + Handler exc = null; + Handler next = i.hasNext() ? (Handler) i.next() : null; + while(next != null) { + Handler last = exc; + exc = next; + next = i.hasNext() ? (Handler) i.next() : null; + + int startNr = exc.start.getBlockNr(); + int endNr = exc.end.getBlockNr(); + int endHandler = Integer.MAX_VALUE; + /* If the next exception handler catches a bigger range + * it must surround the handler completely. + */ + if (next != null + && next.end.getBlockNr() > endNr) + endHandler = next.end.getBlockNr() + 1; + + FlowBlock tryFlow = exc.start; + tryFlow.checkConsistent(); + + if (last == null || exc.type == null + || last.start.getBlockNr() != startNr + || last.end.getBlockNr() != endNr) { + /* The last handler does catch another range. + * Create a new try block. */ - if (next != null - && next.end.getBlockNr() > endNr) - endHandler = next.end.getBlockNr() + 1; - - FlowBlock tryFlow = exc.start; - tryFlow.checkConsistent(); - - if (last == null || exc.type == null - || last.start.getBlockNr() != startNr - || last.end.getBlockNr() != endNr) { - /* The last handler does catch another range. - * Create a new try block. - */ - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_ANALYZE) != 0) - GlobalOptions.err.println - ("analyzeTry(" + startNr + ", " + endNr+")"); - while(true) { - while (tryFlow.analyze(startNr, - endNr+1)); - int nextNr = tryFlow.getNextBlockNr(); - if (nextNr > endNr) - break; - tryFlow = flowBlocks[nextNr]; - } - if (tryFlow.getBlockNr() != startNr) - GlobalOptions.err.println - ("Warning: Can't completely analyze try."); - TryBlock tryBlock = new TryBlock(tryFlow); - } else if (!(tryFlow.block instanceof TryBlock)) - throw new InternalError("no TryBlock"); - - FlowBlock catchFlow = exc.handler; - boolean isMultiUsed = catchFlow.predecessors.size() != 0; - if (!isMultiUsed && next != null) { - for (Iterator j = handlers.tailSet(next).iterator(); - j.hasNext();) { - Handler h = (Handler) j.next(); - if (h.handler == catchFlow) { - isMultiUsed = true; - break; - } - } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_ANALYZE) != 0) + GlobalOptions.err.println + ("analyzeTry(" + startNr + ", " + endNr+")"); + while(true) { + while (tryFlow.analyze(startNr, endNr+1)); + int nextNr = tryFlow.getNextBlockNr(); + if (nextNr > endNr) + break; + tryFlow = flowBlocks[nextNr]; } - - if (isMultiUsed) { - /* If this exception is used in other exception handlers, - * create a new flow block, that jumps to the handler. - * This will be our new exception handler. - */ - FlowBlock newFlow = new FlowBlock - (catchFlow.method, catchFlow.getBlockNr(), - catchFlow.prevByCodeOrder); - newFlow.setSuccessors(new FlowBlock[] { catchFlow }); - newFlow.nextByCodeOrder = catchFlow; - catchFlow.prevByCodeOrder = newFlow; - catchFlow = newFlow; - } else { - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_ANALYZE) != 0) - GlobalOptions.err.println - ("analyzeCatch(" - + catchFlow.getBlockNr() + ", " + endHandler + ")"); - while (catchFlow.analyze(catchFlow.getBlockNr(), - endHandler)); + if (tryFlow.getBlockNr() != startNr) { + GlobalOptions.err.println + ("Warning: Can't completely analyze try."); } - - /* Merge the try-block with the catch-block now */ - tryFlow.updateInOutCatch(catchFlow); - tryFlow.mergeSuccessors(catchFlow); - tryFlow.mergeBlockNr(catchFlow); - if (exc.type != null) - analyzeCatchBlock(exc.type, tryFlow, catchFlow.block); - - else if (!analyzeSynchronized(tryFlow, catchFlow.block, - endHandler) - && ! analyzeFinally(tryFlow, catchFlow.block, - endHandler) - && ! analyzeSpecialFinally(tryFlow, catchFlow.block, - endHandler)) - - analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); - - tryFlow.checkConsistent(); + TryBlock tryBlock = new TryBlock(tryFlow); + } else if (!(tryFlow.block instanceof TryBlock)) + throw new InternalError("no TryBlock"); + + FlowBlock catchFlow = exc.handler; + boolean isMultiUsed = catchFlow.predecessors.size() != 0; + if (!isMultiUsed && next != null) { + for (Iterator j = handlers.tailSet(next).iterator(); + j.hasNext();) { + Handler h = (Handler) j.next(); + if (h.handler == catchFlow) { + isMultiUsed = true; + break; + } + } + } + + if (isMultiUsed) { + /* If this exception is used in other exception handlers, + * create a new flow block, that jumps to the handler. + * This will be our new exception handler. + */ + FlowBlock newFlow = new FlowBlock + (catchFlow.method, catchFlow.getBlockNr(), + catchFlow.prevByCodeOrder); + newFlow.setSuccessors(new FlowBlock[] { catchFlow }); + newFlow.nextByCodeOrder = catchFlow; + catchFlow.prevByCodeOrder = newFlow; + catchFlow = newFlow; + } else { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println - ("analyzeTryCatch(" + tryFlow.getBlockNr() + ", " - + tryFlow.getNextBlockNr() + ") done."); + ("analyzeCatch(" + + catchFlow.getBlockNr() + ", " + endHandler + ")"); + while (catchFlow.analyze(catchFlow.getBlockNr(), + endHandler)); } + + if (exc.type != null) + analyzeCatchBlock(exc.type, tryFlow, catchFlow); + + else if (! analyzeSynchronized(tryFlow, catchFlow, endHandler) + && ! analyzeFinally(tryFlow, catchFlow, endHandler) + && ! analyzeSpecialFinally(tryFlow, catchFlow, + endHandler)) + /* As last resort make a catch(Object) block. This doesn't + * compile, but at least it gives a hint what the code + * does. + */ + analyzeCatchBlock(Type.tObject, tryFlow, catchFlow); + + tryFlow.checkConsistent(); + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_ANALYZE) != 0) + GlobalOptions.err.println + ("analyzeTryCatch(" + tryFlow.getBlockNr() + ", " + + tryFlow.getNextBlockNr() + ") done."); } } } diff --git a/jode/src/net/sf/jode/flow/TryBlock.java b/jode/src/net/sf/jode/flow/TryBlock.java index 7529279..0323e8d 100644 --- a/jode/src/net/sf/jode/flow/TryBlock.java +++ b/jode/src/net/sf/jode/flow/TryBlock.java @@ -26,17 +26,15 @@ import net.sf.jode.expr.LocalLoadOperator; /** * A TryBlock is created for each exception in the - * ExceptionHandlers-Attribute.

+ * ExceptionHandlers attribute.
* * For each catch block (there may be more than one catch block * appending a single try block) and for each finally and each * synchronized block such a TryBlock is created. The * finally/synchronized-blocks have a null exception type so that they - * are easily distinguishable from the catch blocks.

+ * are easily distinguishable from the catch blocks.
* - * A TryBlock is an intermediate representation that gets - * converted later to a CatchBlock, a FinallyBlock or a - * SynchronizedBlock (after the body is parsed). + * A TryBlock may be converted later into a SynchronizedBlock. * * @date 1998/09/16 * @author Jochen Hoenicke diff --git a/jode/src/net/sf/jode/jvm/CodeVerifier.java b/jode/src/net/sf/jode/jvm/CodeVerifier.java index 3dd33c2..a42d5f2 100644 --- a/jode/src/net/sf/jode/jvm/CodeVerifier.java +++ b/jode/src/net/sf/jode/jvm/CodeVerifier.java @@ -596,7 +596,6 @@ public class CodeVerifier implements Opcodes { private VerifyInfo initInfo() throws VerifyException { VerifyInfo info = new VerifyInfo(); - int pos = 1; int slot = 0; if (!mi.isStatic()) { if (slot >= bb.getMaxLocals()) @@ -606,14 +605,13 @@ public class CodeVerifier implements Opcodes { else info.locals[slot++] = tType("L", ci); } - while (methodType.charAt(pos) != ')') { - int start = pos; - pos = TypeSignature.skipType(methodType, pos); - String paramType = methodType.substring(start, pos); + + String[] paramTypes = TypeSignature.getParameterTypes(methodType); + for (int i = 0; i < paramTypes.length; i++) { if (slot >= bb.getMaxLocals()) throw new VerifyException("Too few local slots"); - info.locals[slot++] = tType(paramType); - if (TypeSignature.getTypeSize(paramType) == 2) { + info.locals[slot++] = tType(paramTypes[i]); + if (TypeSignature.getTypeSize(paramTypes[i]) == 2) { if (slot >= bb.getMaxLocals()) throw new VerifyException("Too few local slots"); info.locals[slot++] = tSecondPart; diff --git a/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java b/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java index b1f2349..02970a5 100644 --- a/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java +++ b/jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java @@ -98,16 +98,6 @@ public class SyntheticAnalyzer implements Opcodes { opc_astore, opc_new, opc_dup, opc_aload, opc_invokevirtual, opc_invokespecial, opc_athrow }; - private static final Reference[] getClassRefs = { - null, Reference.getReference("Ljava/lang/Class;", "forName", - "(Ljava/lang/String;)Ljava/lang/Class;"), - null, null, null, null, null, - Reference.getReference("Ljava/lang/Throwable;", "getMessage", - "()Ljava/lang/String;"), - Reference.getReference("Ljava/lang/NoClassDefFoundError;", "", - "(Ljava/lang/String;)V"), null - }; - boolean checkGetClass() { if (!method.isStatic() @@ -133,9 +123,12 @@ public class SyntheticAnalyzer implements Opcodes { Instruction instr = startBlock.getInstructions()[i]; if (instr.getOpcode() != getClassOpcodes[i]) return false; - if (getClassRefs[i] != null - && !getClassRefs[i].equals(instr.getReference())) - return false; + if (i == 1) { + Reference ref = instr.getReference(); + if (!ref.getClazz().equals("Ljava/lang/Class;") + || !ref.getName().equals("forName")) + return false; + } if (i == 0 && instr.getLocalSlot() != 0) return false; } @@ -148,9 +141,6 @@ public class SyntheticAnalyzer implements Opcodes { Instruction instr = catchBlock.getInstructions()[i]; if (instr.getOpcode() != getClassOpcodes[3+i]) return false; - if (getClassRefs[3+i] != null - && !getClassRefs[3+i].equals(instr.getReference())) - return false; if (i == 0) excSlot = instr.getLocalSlot(); if (i == 1 && !instr.getClazzType().equals @@ -158,6 +148,12 @@ public class SyntheticAnalyzer implements Opcodes { return false; if (i == 3 && instr.getLocalSlot() != excSlot) return false; + if (i == 4 + && !instr.getReference().getName().equals("getMessage")) + return false; + if (i == 5 + && !instr.getReference().getName().equals("")) + return false; } this.kind = GETCLASS; return true;