Documentation updates.

build.xml updates.
javac-1.1 finally/synchronized blocks work again.
some other changes, see ChangeLog for details.


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1346 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 23 years ago
parent 6acf9d8d7a
commit 7714d5d503
  1. 57
      jode/ChangeLog
  2. 25
      jode/build.xml
  3. 5
      jode/config.props
  4. 2
      jode/doc/feedback.php
  5. 2
      jode/doc/footer.inc
  6. 7
      jode/src/net/sf/jode/bytecode/BasicBlockReader.java
  7. 3
      jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
  8. 2
      jode/src/net/sf/jode/bytecode/BasicBlocks.java
  9. 3
      jode/src/net/sf/jode/bytecode/Reference.java
  10. 2
      jode/src/net/sf/jode/bytecode/ReferenceInstruction.java
  11. 145
      jode/src/net/sf/jode/bytecode/TypeSignature.java
  12. 31
      jode/src/net/sf/jode/bytecode/package.html
  13. 3
      jode/src/net/sf/jode/expr/InvokeOperator.java
  14. 19
      jode/src/net/sf/jode/flow/FlowBlock.java
  15. 15
      jode/src/net/sf/jode/flow/StructuredBlock.java
  16. 632
      jode/src/net/sf/jode/flow/TransformExceptionHandlers.java
  17. 8
      jode/src/net/sf/jode/flow/TryBlock.java
  18. 12
      jode/src/net/sf/jode/jvm/CodeVerifier.java
  19. 28
      jode/src/net/sf/jode/jvm/SyntheticAnalyzer.java

@ -1,3 +1,56 @@
2001-08-14 Jochen Hoenicke <jochen@gnu.org>
* 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 <jochen@gnu.org>
* 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 <jochen@gnu.org> 2001-08-08 Jochen Hoenicke <jochen@gnu.org>
More Documentation updates. More Documentation updates.
* build.xml: Release rules. * build.xml: Release rules.
@ -304,7 +357,7 @@
type can't be intersected, return tObject as common super type. type can't be intersected, return tObject as common super type.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patch from Java 1.1 tree: Applied patch from Jode 1.1 tree:
* jode/expr/Expression.java (updateParentTypes): Call setType, * jode/expr/Expression.java (updateParentTypes): Call setType,
instead of merging the types. Other childs want to know about the instead of merging the types. Other childs want to know about the
@ -317,7 +370,7 @@
innermost. innermost.
2001-07-14 Jochen Hoenicke <jochen@gnu.org> 2001-07-14 Jochen Hoenicke <jochen@gnu.org>
Applied patches from the Java 1.1 tree: Applied patches from the Jode 1.1 tree:
* jode/decompiler/TabbedPrintWriter.java: Better gnu style handling: * jode/decompiler/TabbedPrintWriter.java: Better gnu style handling:
(openBraceClass) (closeBraceClass) (openBraceClass) (closeBraceClass)

@ -19,7 +19,7 @@
--> -->
<!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd"> <!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd">
<project name="jode" default="all" basedir="."> <project name="jode" default="test" basedir=".">
<!-- set global properties for this build --> <!-- set global properties for this build -->
<property name="version" value="1.90-CVS"/> <property name="version" value="1.90-CVS"/>
@ -32,7 +32,7 @@
<property name="distdir" value="${release}/jode-${version}"/> <property name="distdir" value="${release}/jode-${version}"/>
<property name="scripts" value="${basedir}/scripts"/> <property name="scripts" value="${basedir}/scripts"/>
<property name="api.doc" value="doc/api"/> <property name="api.doc" value="${doc}/api"/>
<property name="test" value="${basedir}/test"/> <property name="test" value="${basedir}/test"/>
<property name="test.src" value="${test}/src"/> <property name="test.src" value="${test}/src"/>
@ -164,7 +164,7 @@
<!-- ********* Create Release files ******* --> <!-- ********* Create Release files ******* -->
<target name="release" depends="release-bin,release-bin11,release-src"/> <target name="release" depends="release-bin,release-bin11,release-src,release-javadoc"/>
<target name="release-bindist" depends="build"> <target name="release-bindist" depends="build">
<jar jarfile="${distdir}/jode.jar"> <jar jarfile="${distdir}/jode.jar">
@ -221,7 +221,9 @@
<include name="doc/**"/> <include name="doc/**"/>
<include name="scripts/**"/> <include name="scripts/**"/>
<include name="src/**"/> <include name="src/**"/>
<include name="test/**"/> <include name="test/*.java"/>
<include name="test/*.j"/>
<include name="test/src/**"/>
<include name="props/**"/> <include name="props/**"/>
<include name="lib/**"/> <include name="lib/**"/>
</fileset> </fileset>
@ -231,6 +233,14 @@
<delete dir="${distdir}"/> <delete dir="${distdir}"/>
</target> </target>
<target name="release-javadoc">
<antcall target="doc-javadoc"/>
<mkdir dir="${release}"/>
<jar jarfile="${release}/jode-${version}-API.jar"
basedir="${doc}" includes="api/**"/>
<antcall target="clean-doc"/>
</target>
<target name="clean-release"> <target name="clean-release">
<delete dir="${release}"/> <delete dir="${release}"/>
</target> </target>
@ -254,8 +264,15 @@
</execon> </execon>
</target> </target>
<target name="doc-javadoc"> <target name="doc-javadoc">
<tstamp>
<format property="date" pattern="MMM d, yyyy"/>
</tstamp>
<mkdir dir="${api.doc}"/> <mkdir dir="${api.doc}"/>
<javadoc packagenames="net.sf.jode.*" <javadoc packagenames="net.sf.jode.*"
windowtitle="Jode ${version} API Specification"
header='&lt;b&gt;&lt;a href="http://jode.sourceforge.net/"&gt;Jode&lt;/a&gt; ${version}&lt;/b&gt;&lt;br&gt;&lt;font size="-2"&gt;Build ${date}&lt;/font&gt;'
overview="${src}/net/sf/jode/overview.html"
bottom='Copyright &amp;copy; 1998-2001 by Jochen Hoenicke.'
sourcepath="${src}" sourcepath="${src}"
destdir="${api.doc}" destdir="${api.doc}"
use="yes"> use="yes">

@ -1,10 +1,11 @@
# Do you have online access for generating javadoc? # Do you have online access for generating javadoc?
# If not, where are your local files. # If not, where are your local files.
javadoc.offline=false javadoc.offline=false
#javadoc.href=http://java.sun.com/products/jdk/1.2/docs/api/
javadoc.packagelistLoc= 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.offline=true
javadoc.href=file:/usr/doc/inet/java/jdk1.2/docs/api
#javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api #javadoc.packagelistLoc=/usr/doc/inet/java/jdk1.2/docs/api
# Is Perl installed on your system? # Is Perl installed on your system?

@ -7,7 +7,7 @@ Please send me a short notice if you add a bug.</p>
<p>You can contact me per email via <a <p>You can contact me per email via <a
href="http://sourceforge.net/sendmessage.php?touser=18252">hoenicke at href="http://sourceforge.net/sendmessage.php?touser=18252">hoenicke at
users.sourceforge.net</a>. Please mention <i>jode</i> in the users.sourceforge.net</a>. Please mention <i>Jode</i> in the
subject.</p> subject.</p>
<p>There is a mailing list. Check <a href="http://lists.sourceforge.net/mailman/listinfo/jode-users">this page</a> for subscription informations.</p> <p>There is a mailing list. Check <a href="http://lists.sourceforge.net/mailman/listinfo/jode-users">this page</a> for subscription informations.</p>

@ -3,7 +3,7 @@
<TD align="center"><SPAN class=footer> <TD align="center"><SPAN class=footer>
All trademarks and copyrights on this page are properties of their respective owners. <br> All trademarks and copyrights on this page are properties of their respective owners. <br>
Last updated on 3-Jul-2000, Last updated on 3-Jul-2000,
Copyright &copy; 1998-2000 by Jochen Hoenicke.<br> Copyright &copy; 1998-2001 by Jochen Hoenicke.<br>
Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/">http://jode.sourceforge.net/</a></SPAN> Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/">http://jode.sourceforge.net/</a></SPAN>
</TD> </TD>
</TR> </TR>

@ -693,7 +693,7 @@ class BasicBlockReader implements Opcodes {
throw new ClassFormatException throw new ClassFormatException
("Illegal call of special method "+ref); ("Illegal call of special method "+ref);
int nargs = input.readUnsignedByte(); int nargs = input.readUnsignedByte();
if (TypeSignature.getArgumentSize(ref.getType()) if (TypeSignature.getParameterSize(ref.getType())
!= nargs - 1) != nargs - 1)
throw new ClassFormatException throw new ClassFormatException
("Interface nargs mismatch: "+ref+" vs. "+nargs); ("Interface nargs mismatch: "+ref+" vs. "+nargs);
@ -890,7 +890,7 @@ class BasicBlockReader implements Opcodes {
GlobalOptions.err.println("Illegal LVT length, ignoring it"); GlobalOptions.err.println("Illegal LVT length, ignoring it");
return; return;
} }
Vector[] lvt = new Vector[bb.maxLocals]; Vector[] lvt = new Vector[maxLocals];
for (int i=0; i < count; i++) { for (int i=0; i < count; i++) {
LVTEntry lve = new LVTEntry(); LVTEntry lve = new LVTEntry();
lve.start = input.readUnsignedShort(); lve.start = input.readUnsignedShort();
@ -899,7 +899,8 @@ class BasicBlockReader implements Opcodes {
int typeIndex = input.readUnsignedShort(); int typeIndex = input.readUnsignedShort();
int slot = input.readUnsignedShort(); int slot = input.readUnsignedShort();
if (nameIndex == 0 || cp.getTag(nameIndex) != cp.UTF8 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 // This is probably an evil lvt as created by HashJava
// simply ignore it. // simply ignore it.

@ -894,7 +894,8 @@ class BasicBlockWriter implements Opcodes {
output.writeShort output.writeShort
(gcp.putRef(gcp.INTERFACEMETHODREF, ref)); (gcp.putRef(gcp.INTERFACEMETHODREF, ref));
output.writeByte output.writeByte
(TypeSignature.getArgumentSize(ref.getType()) + 1); (TypeSignature
.getParameterSize(ref.getType()) + 1);
output.writeByte(0); output.writeByte(0);
} else } else
output.writeShort(gcp.putRef(gcp.METHODREF, ref)); output.writeShort(gcp.putRef(gcp.METHODREF, ref));

@ -132,7 +132,7 @@ public class BasicBlocks extends BinaryInfo implements Opcodes {
public BasicBlocks(MethodInfo mi) { public BasicBlocks(MethodInfo mi) {
methodInfo = mi; methodInfo = mi;
int paramSize = (mi.isStatic() ? 0 : 1) int paramSize = (mi.isStatic() ? 0 : 1)
+ TypeSignature.getArgumentSize(mi.getType()); + TypeSignature.getParameterSize(mi.getType());
paramInfos = new LocalVariableInfo[paramSize]; paramInfos = new LocalVariableInfo[paramSize];
for (int i=0; i< paramSize; i++) for (int i=0; i< paramSize; i++)
paramInfos[i] = LocalVariableInfo.getInfo(i); paramInfos[i] = LocalVariableInfo.getInfo(i);

@ -24,7 +24,8 @@ import java.util.Iterator;
///#enddef ///#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 { public class Reference {
/** /**

@ -61,7 +61,7 @@ class ReferenceInstruction extends Instruction {
case opc_invokestatic: case opc_invokestatic:
case opc_invokeinterface: case opc_invokeinterface:
poppush[0] = opcode != opc_invokestatic ? 1 : 0; poppush[0] = opcode != opc_invokestatic ? 1 : 0;
poppush[0] += TypeSignature.getArgumentSize(typeSig); poppush[0] += TypeSignature.getParameterSize(typeSig);
poppush[1] = TypeSignature.getReturnSize(typeSig); poppush[1] = TypeSignature.getReturnSize(typeSig);
break; break;

@ -21,7 +21,37 @@ package net.sf.jode.bytecode;
import net.sf.jode.util.UnifyHash; 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. <br>
*
* 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. <br>
*
* Here are a few examples:
* <table><tr><th>type signature</th><th>Java type</th></tr>
* <tr><td><code>Z</code></td><td><code>boolean</code></td></tr>
* <tr><td><code>B</code></td><td><code>byte</code></td></tr>
* <tr><td><code>S</code></td><td><code>short</code></td></tr>
* <tr><td><code>C</code></td><td><code>char</code></td></tr>
* <tr><td><code>I</code></td><td><code>int</code></td></tr>
* <tr><td><code>F</code></td><td><code>float</code></td></tr>
* <tr><td><code>J</code></td><td><code>long</code></td></tr>
* <tr><td><code>D</code></td><td><code>double</code></td></tr>
* <tr><td><code>Ljava/lang/Object;</code></td>
* <td><code>java.lang.Object</code></td></tr>
* <tr><td><code>[[I</code></td><td><code>int[][]</code></td></tr>
* <tr><td><code>(Ljava/lang/Object;I)V</code></td>
* <td>method with argument types <code>Object</code> and
* <code>int</code> and <code>void</code> return type.</td></tr>
* <tr><td><code>()I</code></td>
* <td> method without arguments
* and <code>int</code> return type.</td></tr>
* </table>
*
* @author Jochen Hoenicke
*/ */
public class TypeSignature { 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 * @param clazz a java.lang.Class, this may also be a primitive or
* array type. * array type.
* @return the type signature (see section 4.3.2 Field Descriptors * @return the type signature.
* of the JVM specification)
*/ */
public static String getSignature(Class clazz) { public static String getSignature(Class clazz) {
return appendSignature(new StringBuffer(), clazz).toString(); 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 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. * @param returnT the java.lang.Class of the return type of the method.
* @return the method signature (see section 4.3.3 Method Descriptors * @return the method type signature
* of the JVM specification)
*/ */
public static String getSignature(Class paramT[], Class returnT) { public static String getSignature(Class paramT[], Class returnT) {
StringBuffer sig = new StringBuffer("("); StringBuffer sig = new StringBuffer("(");
@ -86,8 +114,8 @@ public class TypeSignature {
} }
/** /**
* Generate a Class for a type signature. This is the pendant to * Generates a Class object for a type signature. This is the
* getSignature. * inverse function of getSignature.
* @param typeSig a single type signature * @param typeSig a single type signature
* @return the Class object representing that type. * @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) { private static boolean usingTwoSlots(char type) {
return "JD".indexOf(type) >= 0; return "JD".indexOf(type) >= 0;
} }
/** /**
* Returns the number of words, an object of the given simple type * 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) { public static int getTypeSize(String typeSig) {
return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1; 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) { public static String getElementType(String typeSig) {
if (typeSig.charAt(0) != '[') if (typeSig.charAt(0) != '[')
throw new IllegalArgumentException(); throw new IllegalArgumentException();
return typeSig.substring(1); 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) { public static ClassInfo getClassInfo(ClassPath classpath, String typeSig) {
if (typeSig.charAt(0) != 'L') if (typeSig.charAt(0) != 'L')
throw new IllegalArgumentException(); throw new IllegalArgumentException();
@ -150,7 +196,13 @@ public class TypeSignature {
(typeSig.substring(1, typeSig.length()-1).replace('/', '.')); (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++); char c = methodTypeSig.charAt(position++);
while (c == '[') while (c == '[')
c = methodTypeSig.charAt(position++); c = methodTypeSig.charAt(position++);
@ -160,10 +212,13 @@ public class TypeSignature {
} }
/** /**
* Returns the number of words, the arguments for the given method * Gets the number of words the parameters for the given method
* type signature takes. * 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 nargs = 0;
int i = 1; int i = 1;
for (;;) { for (;;) {
@ -179,8 +234,11 @@ public class TypeSignature {
} }
/** /**
* Returns the number of words, an object of the given simple type * Gets the size of the return type of the given method in words.
* signature takes. * 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) { public static int getReturnSize(String methodTypeSig) {
int length = methodTypeSig.length(); int length = methodTypeSig.length();
@ -195,8 +253,9 @@ public class TypeSignature {
} }
/** /**
* Returns the number of words, an object of the given simple type * Gets the parameter type signatures of the given method signature.
* signature takes. * @param methodTypeSig the method type signature.
* @return an array containing all parameter types in correct order.
*/ */
public static String[] getParameterTypes(String methodTypeSig) { public static String[] getParameterTypes(String methodTypeSig) {
int pos = 1; int pos = 1;
@ -215,6 +274,26 @@ public class TypeSignature {
return params; 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) { public static Object getDefaultValue(String typeSig) {
switch(typeSig.charAt(0)) { switch(typeSig.charAt(0)) {
case 'Z': case 'Z':
@ -238,15 +317,7 @@ public class TypeSignature {
} }
/** /**
* Returns the number of words, an object of the given simple type * Checks if there is a valid class name starting at index
* 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
* in string typesig and ending with a semicolon. * in string typesig and ending with a semicolon.
* @return the index at which the class name ends. * @return the index at which the class name ends.
* @exception IllegalArgumentException if there was an illegal character. * @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. * in string typesig.
* @return the index at which the type signature ends. * @return the index at which the type signature ends.
* @exception IllegalArgumentException if there was an illegal character. * @exception IllegalArgumentException if there was an illegal character.
@ -285,6 +356,14 @@ public class TypeSignature {
return index; 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) public static void checkTypeSig(String typesig)
throws IllegalArgumentException 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) public static void checkMethodTypeSig(String typesig)
throws IllegalArgumentException throws IllegalArgumentException
{ {

@ -18,7 +18,7 @@
along with this program; see the file COPYING. If not, write to along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
--> -->
<title>JODE Bytecode Package</title> <title>Jode Bytecode Package</title>
</head> </head>
<body> <body>
@ -71,11 +71,36 @@ You can also use this package to create and write new classes:
... ...
</pre> </pre>
<h3><a name="advantages">Advantages of this bytecode package</a></h3>
<ul>
<li>You don't need to think of the constant pool, except when you want
to write your custom attributes.</li>
<li>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
<code>ldc</code> or <code>ldc2</code> and on writing it will convert
them back.</li>
<li>Wide instructions are automatically generated when needed, large
methods are supported.</li>
<li>The code is organized in {@link net.sf.jode.bytecode.BasicBlocks}
which makes flow analysis much easier.</li>
<li>The memory consumption is quite moderate.</li>
</ul>
<h3>Disadvantages</h3>
<ul>
<li>You can't change every byte. For example Jode decides itself if
a lookup switch or table switch is generated.</li>
<li>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.</li>
</ul>
<hr> <hr>
<address><a href="mailto:jochen@gnu.org">Jochen Hoenicke</a></address> <address><a href="mailto:jochen@gnu.org">Jochen Hoenicke</a></address>
<!-- Created: Thu Jun 22 16:56:39 MET DST 2000 --> <!-- Created: Thu Jun 22 2000 -->
<!-- hhmts start --> <!-- hhmts start -->
Last modified: Sun Aug 5 17:53:03 MEST 2001 Last modified: Sat Aug 11 18:44:19 MEST 2001
<!-- hhmts end --> <!-- hhmts end -->
</body> </body>
</html> </html>

@ -1033,7 +1033,8 @@ public final class InvokeOperator extends Operator
} }
} else { } else {
qualifiedNew = true; qualifiedNew = true;
if (outerExpr.getType() instanceof NullType) { if (outerExpr.getType().getCanonic()
instanceof NullType) {
writer.print("("); writer.print("(");
writer.startOp(writer.EXPL_PAREN, 1); writer.startOp(writer.EXPL_PAREN, 1);
writer.print("("); writer.print("(");

@ -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 * 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 * 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. * @return The remaining jumps, that couldn't be resolved.
*/ */
public Jump resolveSomeJumps(Jump jumps, FlowBlock succ) { 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 /* We will put all jumps that we can not resolve into this
* linked list. * linked list.
*/ */
@ -721,7 +724,7 @@ public class FlowBlock {
* begin of successor. * begin of successor.
* @param kills The slots that are always overwritten on the way to * @param kills The slots that are always overwritten on the way to
* successor. * 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) { void updateInOut(FlowBlock successor, VariableSet gens, SlotSet kills) {
successor.updateGenKill(gens, kills); successor.updateGenKill(gens, kills);
@ -805,7 +808,9 @@ public class FlowBlock {
try { try {
if (block.outer != null || block.flowBlock != this) { 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(); block.checkConsistent();
@ -820,7 +825,8 @@ public class FlowBlock {
StructuredBlock last = lastModified; StructuredBlock last = lastModified;
while (last.outer instanceof SequentialBlock while (last.outer instanceof SequentialBlock
|| last.outer instanceof TryBlock) || last.outer instanceof TryBlock
|| last.outer instanceof FinallyBlock)
last = last.outer; last = last.outer;
if (last.outer != null) if (last.outer != null)
throw new InternalError("Inconsistency"); throw new InternalError("Inconsistency");
@ -999,8 +1005,6 @@ public class FlowBlock {
/* Update the in/out-Vectors now */ /* Update the in/out-Vectors now */
updateInOut(succ, succInfo.gen, succInfo.kill); 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. /* Try to eliminate as many jumps as possible.
*/ */
@ -1055,9 +1059,6 @@ public class FlowBlock {
jumps = jump; jumps = jump;
} }
if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_FLOW) != 0)
GlobalOptions.err.println("before resolve: "+this);
/* Try to eliminate as many jumps as possible. /* Try to eliminate as many jumps as possible.
*/ */
jumps = resolveSomeJumps(jumps, END_OF_METHOD); jumps = resolveSomeJumps(jumps, END_OF_METHOD);

@ -60,14 +60,13 @@ import java.util.Set;
public abstract class StructuredBlock { public abstract class StructuredBlock {
/* Invariants: /* Invariants:
* in.intersection(out) = empty * outer != null ==> flowBlock = outer.flowBlock;
* outer != null => flowBlock = outer.flowBlock * outer == null ==> flowBlock.block = this;
* outer == null => flowBlock.block = this * jump == null ==> outer != null;
* jump == null => outer != null * getNextBlock() != null ^ getNextFlowBlock() != null;
* either getNextBlock() != null * outer != null ==>
* or getNextFlowBlock() != null or outer == null * outer.getNextBlock(this) != null
* either outer.getNextBlock(this) != null * ^ outer.getNextFlowBlock(this) != null;
* or outer.getNextFlowBlock(this) != null
*/ */
/** /**

@ -103,35 +103,62 @@ public class TransformExceptionHandlers {
handlers.add(new Handler(tryBlock, endBlock, catchBlock, type)); 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). <br>
*
* This method doesn't actually merge the contents of the blocks. The
* caller should do it right afterwards. <br>
*
* 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. <br>
* The catchFlow block mustn't have any predecessors.
* *
* try-header * @param type The type of the exception which is caught.
* |- first instruction * @param tryFlow The flow block containing the try. The contained
* | ... * block must be a try block.
* | last instruction * @param catchFlow the flow block containing the catch handler.
* |- optional jump (last+1)
* | ...
* `- catch block
*/ */
static void analyzeCatchBlock(Type type, FlowBlock tryFlow, 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); CatchBlock newBlock = new CatchBlock(type);
((TryBlock)tryFlow.block).addCatchBlock(newBlock); ((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 * This transforms a sub routine, i.e. it checks if the beginning
* local assignment matches the final ret and then returns. * local assignment matches the final ret and removes both. It also
* accepts sub routines that just pop their return address.
*/ */
boolean transformSubRoutine(StructuredBlock subRoutine) { boolean transformSubRoutine(StructuredBlock subRoutineBlock) {
if (!(subRoutine instanceof SequentialBlock)) StructuredBlock firstBlock = subRoutineBlock;
return false; if (firstBlock instanceof SequentialBlock)
SequentialBlock sequBlock = (SequentialBlock) subRoutine; firstBlock = subRoutineBlock.getSubBlocks()[0];
StructuredBlock firstBlock = sequBlock.getSubBlocks()[0];
LocalInfo local = null; LocalInfo local = null;
if (firstBlock instanceof SpecialBlock) { if (firstBlock instanceof SpecialBlock) {
@ -156,24 +183,25 @@ public class TransformExceptionHandlers {
} else } else
return false; return false;
/* We are now committed. Remove the first Statement which /* We are now committed and can start changing code. Remove
* stores/removes the return address. * the first Statement which stores/removes the return
* address.
*/ */
firstBlock.removeBlock(); 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 * This is a complicated task which isn't needed for javac nor
* jikes. We just check if the last instruction is a ret and * 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. * as long as the bytecode was verified correctly.
*/ */
while (sequBlock.subBlocks[1] instanceof SequentialBlock) while (subRoutineBlock instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; subRoutineBlock = subRoutineBlock.getSubBlocks()[1];
if (sequBlock.subBlocks[1] instanceof RetBlock if (subRoutineBlock instanceof RetBlock
&& (((RetBlock) sequBlock.subBlocks[1]).local.equals(local))) { && (((RetBlock) subRoutineBlock).local.equals(local))) {
sequBlock.subBlocks[1].removeBlock(); subRoutineBlock.removeBlock();
} }
return true; return true;
} }
@ -203,51 +231,32 @@ public class TransformExceptionHandlers {
} }
/** /**
* Remove the wrongly placed JSRs jumping to the specified * Remove the JSRs jumping to the specified subRoutine. The right
* subRoutine. The right JSRs are already removed, but we have to * JSRs are marked and we can just remove them. For the other JSR
* replace the wrong ones with a warning. * instructions we replace them with a warning.
* @param tryFlow the FlowBLock of the try block. * @param tryFlow the FlowBlock of the try block.
* @param subRoutine the FlowBlock of the sub routine. * @param subRoutine the FlowBlock of the sub routine.
*/ */
private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock, private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
FlowBlock subRoutine) { for (Jump jumps = tryFlow.removeJumps(subRoutine);
Jump nextJump; jumps != null; jumps = jumps.next) {
for (Jump jumps = tryFlow.getJumps(subRoutine);
jumps != null; jumps = nextJump) {
StructuredBlock prev = jumps.prev; StructuredBlock prev = jumps.prev;
nextJump = jumps.next; prev.removeJump();
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;
}
tryFlow.removeSuccessor(jumps); if (prev instanceof EmptyBlock
prev.removeJump(); && prev.outer instanceof JsrBlock
if (jsr.isGood()) { && ((JsrBlock) prev.outer).isGood()) {
StructuredBlock next = jsr.getNextBlock(); StructuredBlock next = prev.outer.getNextBlock();
jsr.removeBlock(); prev.outer.removeBlock();
if (next instanceof ReturnBlock) if (next instanceof ReturnBlock)
removeReturnLocal((ReturnBlock) next); removeReturnLocal((ReturnBlock) next);
} else { } else {
/* We have a JSR to the subroutine, which is badly placed. /* We have a jump to the subroutine, that 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.
* We complain here. * We complain here.
*/ */
DescriptionBlock msg DescriptionBlock msg = new DescriptionBlock
= new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); ("ERROR: invalid jump to finally block!");
tryFlow.removeSuccessor(jumps);
prev.removeJump();
prev.appendBlock(msg); prev.appendBlock(msg);
} }
} }
@ -313,7 +322,6 @@ public class TransformExceptionHandlers {
private void checkAndRemoveJSR(FlowBlock tryFlow, private void checkAndRemoveJSR(FlowBlock tryFlow,
StructuredBlock catchBlock,
FlowBlock subRoutine, FlowBlock subRoutine,
int startOutExit, int endOutExit) { int startOutExit, int endOutExit) {
Iterator iter = tryFlow.getSuccessors().iterator(); Iterator iter = tryFlow.getSuccessors().iterator();
@ -330,8 +338,8 @@ public class TransformExceptionHandlers {
StructuredBlock prev = jumps.prev; StructuredBlock prev = jumps.prev;
if (prev instanceof EmptyBlock if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) { && prev.outer instanceof JsrBlock) {
/* This jump is really a jsr, since it doesn't /* This jump is a jsr, since it doesn't leave the
* leave the block forever, we can ignore it. * block forever, we can ignore it.
*/ */
continue; continue;
} }
@ -382,7 +390,7 @@ public class TransformExceptionHandlers {
* Complain! * Complain!
*/ */
DescriptionBlock msg DescriptionBlock msg
= new DescriptionBlock("ERROR: NO JSR TO FINALLY"); = new DescriptionBlock("ERROR: no jsr to finally");
if (pred != null) if (pred != null)
pred.prependBlock(msg); pred.prependBlock(msg);
else { else {
@ -392,11 +400,10 @@ public class TransformExceptionHandlers {
} }
} }
if (tryFlow.getSuccessors().contains(subRoutine)) if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine); removeJSR(tryFlow, subRoutine);
} }
private void checkAndRemoveMonitorExit(FlowBlock tryFlow, private void checkAndRemoveMonitorExit(FlowBlock tryFlow,
StructuredBlock catchBlock,
LocalInfo local, LocalInfo local,
int start, int end) { int start, int end) {
FlowBlock subRoutine = null; FlowBlock subRoutine = null;
@ -494,7 +501,7 @@ public class TransformExceptionHandlers {
/* Complain! /* Complain!
*/ */
DescriptionBlock msg DescriptionBlock msg
= new DescriptionBlock("ERROR: NO MONITOREXIT"); = new DescriptionBlock("ERROR: no monitorexit");
prev.appendBlock(msg); prev.appendBlock(msg);
msg.moveJump(jumps); msg.moveJump(jumps);
} }
@ -502,8 +509,9 @@ public class TransformExceptionHandlers {
if (subRoutine != null) { if (subRoutine != null) {
if (tryFlow.getSuccessors().contains(subRoutine)) if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine); removeJSR(tryFlow, subRoutine);
tryFlow.mergeBlockNr(subRoutine); if (subRoutine.predecessors.size() == 0)
tryFlow.mergeBlockNr(subRoutine);
} }
} }
@ -526,20 +534,25 @@ public class TransformExceptionHandlers {
} }
private boolean analyzeSynchronized(FlowBlock tryFlow, private boolean analyzeSynchronized(FlowBlock tryFlow,
StructuredBlock catchBlock, FlowBlock catchFlow,
int endHandler) { 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); StoreInstruction excStore = getExceptionStore(catchBlock);
if (excStore != null) if (excStore != null)
catchBlock = catchBlock.getSubBlocks()[1]; catchBlock = catchBlock.getSubBlocks()[1];
/* Check for the monitorexit instruction */
if (!(catchBlock instanceof SequentialBlock if (!(catchBlock instanceof SequentialBlock
&& catchBlock.getSubBlocks()[0] && catchBlock.getSubBlocks()[0]
instanceof InstructionBlock)) instanceof InstructionBlock))
return false; return false;
Expression instr = Expression instr =
((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
if (!(instr instanceof MonitorExitOperator if (!(instr instanceof MonitorExitOperator
&& instr.getFreeOperandCount() == 0 && instr.getFreeOperandCount() == 0
&& (((MonitorExitOperator)instr).getSubExpressions()[0] && (((MonitorExitOperator)instr).getSubExpressions()[0]
@ -547,9 +560,9 @@ public class TransformExceptionHandlers {
&& catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) && catchBlock.getSubBlocks()[1] instanceof ThrowBlock))
return false; return false;
/* Check for the throw instruction */
Expression throwInstr = Expression throwInstr =
((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction(); ((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction();
if (excStore != null) { if (excStore != null) {
if (!(throwInstr instanceof Operator if (!(throwInstr instanceof Operator
&& excStore.lvalueMatches((Operator)throwInstr))) && excStore.lvalueMatches((Operator)throwInstr)))
@ -583,6 +596,12 @@ public class TransformExceptionHandlers {
* monitorexit local_x * monitorexit local_x
* return_n * 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) MonitorExitOperator monexit = (MonitorExitOperator)
((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr;
@ -597,7 +616,7 @@ public class TransformExceptionHandlers {
+ "," + tryFlow.getNextBlockNr() + "," + endHandler + ")"); + "," + tryFlow.getNextBlockNr() + "," + endHandler + ")");
checkAndRemoveMonitorExit checkAndRemoveMonitorExit
(tryFlow, catchBlock, local, tryFlow.getNextBlockNr(), endHandler); (tryFlow, local, tryFlow.getNextBlockNr(), endHandler);
SynchronizedBlock syncBlock = new SynchronizedBlock(local); SynchronizedBlock syncBlock = new SynchronizedBlock(local);
TryBlock tryBlock = (TryBlock) tryFlow.block; TryBlock tryBlock = (TryBlock) tryFlow.block;
@ -609,8 +628,41 @@ public class TransformExceptionHandlers {
return true; 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, private boolean analyzeFinally(FlowBlock tryFlow,
StructuredBlock catchBlock, int end) { FlowBlock catchFlow, int end) {
/* Layout of a try-finally block: /* Layout of a try-finally block:
* *
@ -632,6 +684,7 @@ public class TransformExceptionHandlers {
* return_n * return_n
*/ */
StructuredBlock catchBlock = catchFlow.block;
StoreInstruction excStore = getExceptionStore(catchBlock); StoreInstruction excStore = getExceptionStore(catchBlock);
if (excStore == null) if (excStore == null)
return false; return false;
@ -643,9 +696,10 @@ public class TransformExceptionHandlers {
StructuredBlock finallyBlock = null; StructuredBlock finallyBlock = null;
if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) { if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) {
/* In case the try block has no exit (that means, it throws /* In case the try block has no exit (that means, it
* an exception), the finallyBlock was already merged with * throws an exception or loops forever), the finallyBlock
* the catchBlock. We have to check for this case separately: * was already merged with the catchBlock. We have to
* check for this case separately:
* *
* do { * do {
* JSR * JSR
@ -682,7 +736,7 @@ public class TransformExceptionHandlers {
FlowBlock subRoutine; FlowBlock subRoutine;
if (finallyBlock != null) { 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 * need to check if it breaks to the right block, because
* we know that there is only one Block around the jsr. * we know that there is only one Block around the jsr.
*/ */
@ -690,269 +744,255 @@ public class TransformExceptionHandlers {
return false; return false;
/* Check if the try block has no exit /* 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) if (tryFlow.getSuccessors().size() > 0)
// return false; return false;
catchBlock = finallyBlock; catchBlock = finallyBlock;
subRoutine = null; subRoutine = null;
} else { } else {
if (!(jsrBlock.innerBlock instanceof EmptyBlock)) if (!(jsrBlock.innerBlock instanceof EmptyBlock))
return false; return false;
catchBlock = jsrBlock; finallyBlock = jsrBlock.innerBlock;
subRoutine = jsrBlock.innerBlock.jump.destination; subRoutine = finallyBlock.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
*/
TryBlock tryBlock = (TryBlock) tryFlow.block; /* We are committed now and can start changing the try
if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { * block.
/* remove the surrounding tryBlock */ */
TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; checkAndRemoveJSR(tryFlow, subRoutine,
innerTry.gen = tryBlock.gen; tryFlow.getNextBlockNr(), end);
innerTry.replace(tryBlock);
tryBlock = innerTry;
tryFlow.lastModified = tryBlock;
tryFlow.block = tryBlock;
}
FinallyBlock newBlock = new FinallyBlock();
newBlock.setCatchBlock(catchBlock);
tryBlock.addCatchBlock(newBlock);
if (subRoutine != null) {
while (subRoutine.analyze(tryFlow.getNextBlockNr(), end));
/* Now check if the subroutine is correct and has only the /* Now analyze and transform the subroutine.
* catchFlow as predecessor.
*/ */
if (subRoutine.predecessors.size() == 1 while (subRoutine.analyze(tryFlow.getNextBlockNr(), end));
&& transformSubRoutine(subRoutine.block)) { if (subRoutine.predecessors.size() == 1) {
/* catchFlow is synthetic, so we can safely remove it
tryFlow.removeSuccessor(jsrBlock.innerBlock.jump); * here.
tryFlow.mergeBlockNr(subRoutine); */
tryFlow.mergeSuccessors(subRoutine); subRoutine.mergeBlockNr(catchFlow);
subRoutine.block.replace(catchBlock); catchFlow = subRoutine;
tryFlow.updateInOutCatch(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; return true;
} }
private boolean analyzeSpecialFinally(FlowBlock tryFlow, private boolean analyzeSpecialFinally(FlowBlock tryFlow,
StructuredBlock catchBlock, FlowBlock catchFlow, int end) {
int end) { StructuredBlock finallyBlock = catchFlow.block;
StructuredBlock firstInstr = StructuredBlock firstInstr =
catchBlock instanceof SequentialBlock finallyBlock instanceof SequentialBlock
? catchBlock.getSubBlocks()[0]: catchBlock; ? finallyBlock.getSubBlocks()[0]: finallyBlock;
if (!(firstInstr instanceof SpecialBlock if (!(firstInstr instanceof SpecialBlock
&& ((SpecialBlock)firstInstr).type == SpecialBlock.POP && ((SpecialBlock)firstInstr).type == SpecialBlock.POP
&& ((SpecialBlock)firstInstr).count == 1)) && ((SpecialBlock)firstInstr).count == 1))
return false; 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 * the finally block ends with a break, return or
* similar. * similar.
*/ */
FlowBlock succ = null;
/* remove the pop now */ /* Make sure that resolveJump only works on the inside of the try
if (catchBlock instanceof SequentialBlock) */
catchBlock = catchBlock.getSubBlocks()[1]; tryFlow.lastModified = tryFlow.block.getSubBlocks()[0];
if (finallyBlock instanceof SequentialBlock)
finallyBlock = finallyBlock.getSubBlocks()[1];
else { else {
catchBlock = new EmptyBlock(); finallyBlock = new EmptyBlock();
catchBlock.moveJump(firstInstr.jump); 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(); /* Complain about all other jumps in try block */
// if (trySuccs.size() > 1 Set trySuccs = tryFlow.getSuccessors();
// || (trySuccs.size() == 1 for (Iterator i = trySuccs.iterator(); i.hasNext(); ) {
// && trySuccs.iterator().next() != succ)) for (Jump jumps = tryFlow.getJumps((FlowBlock) i.next());
// return false; 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) { mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
/* Handle the jumps in the tryFlow. /* Following code will work be put inside the finallyBlock */
*/ tryFlow.lastModified = finallyBlock;
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);
return true; return true;
} }
/** void checkTryCatchOrder() {
* Analyzes all exception handlers to try/catch/finally or
* synchronized blocks.
*/
public void analyze() {
/* Check if try/catch ranges are okay. The following succeeds /* Check if try/catch ranges are okay. The following succeeds
* for all classes generated by the sun java compiler, but hand * for all classes generated by the sun java compiler, but hand
* optimized classes (or generated by other compilers) will fail. * optimized classes (or generated by other compilers) will fail.
*/ */
{ Handler last = null;
Handler last = null; for (Iterator i = handlers.iterator(); i.hasNext(); ) {
for (Iterator i = handlers.iterator(); i.hasNext(); ) { Handler exc = (Handler) i.next();
Handler exc = (Handler) i.next(); int start = exc.start.getBlockNr();
int start = exc.start.getBlockNr(); int end = exc.end.getBlockNr();
int end = exc.end.getBlockNr(); int handler = exc.handler.getBlockNr();
int handler = exc.handler.getBlockNr(); if (start > end || handler <= end)
if (start > end || handler <= end) throw new InternalError
throw new InternalError
("ExceptionHandler order failed: not " ("ExceptionHandler order failed: not "
+ start + " < " + end + " <= " + handler); + start + " < " + end + " <= " + handler);
if (last != null if (last != null
&& (last.start.getBlockNr() != start && (last.start.getBlockNr() != start
|| last.end.getBlockNr() != end)) { || last.end.getBlockNr() != end)) {
/* The last handler does catch another range. /* The last handler does catch another range.
* Due to the order: * Due to the order:
* start < last.start.getBlockNr() * start < last.start.getBlockNr()
* || end > last.end.getBlockNr() * || end > last.end.getBlockNr()
*/ */
if (end >= last.start.getBlockNr() if (end >= last.start.getBlockNr()
&& end < last.end.getBlockNr()) && end < last.end.getBlockNr())
throw new InternalError throw new InternalError
("Exception handlers ranges are intersecting: [" ("Exception handlers ranges are intersecting: ["
+ last.start.getBlockNr()+", " + last.start.getBlockNr()+", "
+ last.end.getBlockNr()+"] and [" + last.end.getBlockNr()+"] and ["
+ start+", "+end+"]."); + start+", "+end+"].");
}
last = exc;
} }
last = exc;
} }
}
{ /**
Iterator i = handlers.iterator(); * Analyzes all exception handlers to try/catch/finally or
Handler exc = null; * synchronized blocks.
Handler next = i.hasNext() ? (Handler) i.next() : null; */
while(next != null) { public void analyze() {
Handler last = exc; checkTryCatchOrder();
exc = next;
int startNr = exc.start.getBlockNr(); Iterator i = handlers.iterator();
int endNr = exc.end.getBlockNr(); Handler exc = null;
next = i.hasNext() ? (Handler) i.next() : null; Handler next = i.hasNext() ? (Handler) i.next() : null;
int endHandler = Integer.MAX_VALUE; while(next != null) {
/* If the next exception handler catches a bigger range Handler last = exc;
* it must surround the handler completely. 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 if ((GlobalOptions.debuggingFlags
&& next.end.getBlockNr() > endNr) & GlobalOptions.DEBUG_ANALYZE) != 0)
endHandler = next.end.getBlockNr() + 1; GlobalOptions.err.println
("analyzeTry(" + startNr + ", " + endNr+")");
FlowBlock tryFlow = exc.start; while(true) {
tryFlow.checkConsistent(); while (tryFlow.analyze(startNr, endNr+1));
int nextNr = tryFlow.getNextBlockNr();
if (last == null || exc.type == null if (nextNr > endNr)
|| last.start.getBlockNr() != startNr break;
|| last.end.getBlockNr() != endNr) { tryFlow = flowBlocks[nextNr];
/* 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 (tryFlow.getBlockNr() != startNr) {
if (isMultiUsed) { GlobalOptions.err.println
/* If this exception is used in other exception handlers, ("Warning: Can't completely analyze try.");
* 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));
} }
TryBlock tryBlock = new TryBlock(tryFlow);
/* Merge the try-block with the catch-block now */ } else if (!(tryFlow.block instanceof TryBlock))
tryFlow.updateInOutCatch(catchFlow); throw new InternalError("no TryBlock");
tryFlow.mergeSuccessors(catchFlow);
tryFlow.mergeBlockNr(catchFlow); FlowBlock catchFlow = exc.handler;
if (exc.type != null) boolean isMultiUsed = catchFlow.predecessors.size() != 0;
analyzeCatchBlock(exc.type, tryFlow, catchFlow.block); if (!isMultiUsed && next != null) {
for (Iterator j = handlers.tailSet(next).iterator();
else if (!analyzeSynchronized(tryFlow, catchFlow.block, j.hasNext();) {
endHandler) Handler h = (Handler) j.next();
&& ! analyzeFinally(tryFlow, catchFlow.block, if (h.handler == catchFlow) {
endHandler) isMultiUsed = true;
&& ! analyzeSpecialFinally(tryFlow, catchFlow.block, break;
endHandler)) }
}
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); }
tryFlow.checkConsistent(); 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 if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeTryCatch(" + tryFlow.getBlockNr() + ", " ("analyzeCatch("
+ tryFlow.getNextBlockNr() + ") done."); + 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.");
} }
} }
} }

@ -26,17 +26,15 @@ import net.sf.jode.expr.LocalLoadOperator;
/** /**
* A TryBlock is created for each exception in the * A TryBlock is created for each exception in the
* ExceptionHandlers-Attribute. <p> * ExceptionHandlers attribute. <br>
* *
* For each catch block (there may be more than one catch block * For each catch block (there may be more than one catch block
* appending a single try block) and for each finally and each * appending a single try block) and for each finally and each
* synchronized block such a TryBlock is created. The * synchronized block such a TryBlock is created. The
* finally/synchronized-blocks have a null exception type so that they * finally/synchronized-blocks have a null exception type so that they
* are easily distinguishable from the catch blocks. <p> * are easily distinguishable from the catch blocks. <br>
* *
* A TryBlock is an intermediate representation that gets * A TryBlock may be converted later into a SynchronizedBlock.
* converted later to a CatchBlock, a FinallyBlock or a
* SynchronizedBlock (after the body is parsed).
* *
* @date 1998/09/16 * @date 1998/09/16
* @author Jochen Hoenicke * @author Jochen Hoenicke

@ -596,7 +596,6 @@ public class CodeVerifier implements Opcodes {
private VerifyInfo initInfo() throws VerifyException { private VerifyInfo initInfo() throws VerifyException {
VerifyInfo info = new VerifyInfo(); VerifyInfo info = new VerifyInfo();
int pos = 1;
int slot = 0; int slot = 0;
if (!mi.isStatic()) { if (!mi.isStatic()) {
if (slot >= bb.getMaxLocals()) if (slot >= bb.getMaxLocals())
@ -606,14 +605,13 @@ public class CodeVerifier implements Opcodes {
else else
info.locals[slot++] = tType("L", ci); info.locals[slot++] = tType("L", ci);
} }
while (methodType.charAt(pos) != ')') {
int start = pos; String[] paramTypes = TypeSignature.getParameterTypes(methodType);
pos = TypeSignature.skipType(methodType, pos); for (int i = 0; i < paramTypes.length; i++) {
String paramType = methodType.substring(start, pos);
if (slot >= bb.getMaxLocals()) if (slot >= bb.getMaxLocals())
throw new VerifyException("Too few local slots"); throw new VerifyException("Too few local slots");
info.locals[slot++] = tType(paramType); info.locals[slot++] = tType(paramTypes[i]);
if (TypeSignature.getTypeSize(paramType) == 2) { if (TypeSignature.getTypeSize(paramTypes[i]) == 2) {
if (slot >= bb.getMaxLocals()) if (slot >= bb.getMaxLocals())
throw new VerifyException("Too few local slots"); throw new VerifyException("Too few local slots");
info.locals[slot++] = tSecondPart; info.locals[slot++] = tSecondPart;

@ -98,16 +98,6 @@ public class SyntheticAnalyzer implements Opcodes {
opc_astore, opc_new, opc_dup, opc_aload, opc_astore, opc_new, opc_dup, opc_aload,
opc_invokevirtual, opc_invokespecial, opc_athrow 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;", "<init>",
"(Ljava/lang/String;)V"), null
};
boolean checkGetClass() { boolean checkGetClass() {
if (!method.isStatic() if (!method.isStatic()
@ -133,9 +123,12 @@ public class SyntheticAnalyzer implements Opcodes {
Instruction instr = startBlock.getInstructions()[i]; Instruction instr = startBlock.getInstructions()[i];
if (instr.getOpcode() != getClassOpcodes[i]) if (instr.getOpcode() != getClassOpcodes[i])
return false; return false;
if (getClassRefs[i] != null if (i == 1) {
&& !getClassRefs[i].equals(instr.getReference())) Reference ref = instr.getReference();
return false; if (!ref.getClazz().equals("Ljava/lang/Class;")
|| !ref.getName().equals("forName"))
return false;
}
if (i == 0 && instr.getLocalSlot() != 0) if (i == 0 && instr.getLocalSlot() != 0)
return false; return false;
} }
@ -148,9 +141,6 @@ public class SyntheticAnalyzer implements Opcodes {
Instruction instr = catchBlock.getInstructions()[i]; Instruction instr = catchBlock.getInstructions()[i];
if (instr.getOpcode() != getClassOpcodes[3+i]) if (instr.getOpcode() != getClassOpcodes[3+i])
return false; return false;
if (getClassRefs[3+i] != null
&& !getClassRefs[3+i].equals(instr.getReference()))
return false;
if (i == 0) if (i == 0)
excSlot = instr.getLocalSlot(); excSlot = instr.getLocalSlot();
if (i == 1 && !instr.getClazzType().equals if (i == 1 && !instr.getClazzType().equals
@ -158,6 +148,12 @@ public class SyntheticAnalyzer implements Opcodes {
return false; return false;
if (i == 3 && instr.getLocalSlot() != excSlot) if (i == 3 && instr.getLocalSlot() != excSlot)
return false; return false;
if (i == 4
&& !instr.getReference().getName().equals("getMessage"))
return false;
if (i == 5
&& !instr.getReference().getName().equals("<init>"))
return false;
} }
this.kind = GETCLASS; this.kind = GETCLASS;
return true; return true;

Loading…
Cancel
Save