Documentation updates (INSTALL, javadoc).

Added JUnit Test cases.
* build.xml: Big update.
* net/sf/jode/bytecode/BasicBlock.java:
(updateMaxStackLocals): new method to calculate maxStack and
maxLocals.
(setBlocks): fixed calculation of handlers, call updateMaxLocals.
* net/sf/jode/bytecode/BasicBlockReader.java:
(maxLocals, maxStack): new fields.
(readCode): read maxStack/Locals into private fields.
(convert): check that maxStack/Locals match what we calculate.
* net/sf/jode/bytecode/BinaryInfo.java:
(getKnownAttributeCount): renamed to...
(getAttributeCount): ... this, and also count internal attributes.
Made it protected.
(readAttribute): made protected.
(drop): made protected.
(prepareAttributes): made protected.
(writeKnownAttributes): removed.
(writeAttributes): made protected, use getAttributeCount.
Changed policy: it doesn't call writeKnownAttribute, but instead
it expects sub classes to override this method.
(getAttributeSize): made protected, subclasses should override it.
Changed all subclasses to new policy.
* net/sf/jode/bytecode/Block.java:
(lineNr): Removed, it wasn't used.
(pop,push): Removed, replaced by ...
(maxpop,maxpush,delta): ... these, with slightly changed semantics.
(stackHeight): New variable.
(Block): Default Constructor doesn't initialize fields now.
(getCatchers): Renamed to ...
(getHandlers): ... this, changed all callers.
(initCode): Calculate maxpop, maxpush, delta correctly.
(getStackPopPush): Changed accordingly to new fields.
(setCode): Removed debugging output for illegal contents.
* net/sf/jode/bytecode/Classes.java:  Reworked handling of inner
classes.
(innerClasses): Field mustn't be null anymore when loaded.
(setName): Update class in classpath.
* net/sf/jode/bytecode/ClassPath.java:
(renameClassInfo): new function, should only used by ClassInfo.
* net/sf/jode/bytecode/ConstantPool.java: made public.
(getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
(iterateClassNames): New method.
* net/sf/jode/decompiler/Main.java:
(decompileClass): Catch ClassFormatExceptions and decompile
 remaining classes.
* net/sf/jode/obfuscator/ClassIdentifier.java:
Updated handling of inner/extra classes to new ClassInfo behaviour.
(initSuperClasses): Load DECLARATION of super classes.
* net/sf/jode/obfuscator/PackageIdentifier.java:
Replace deprecated methods of ClassInfo with corresponding classpath
calls.
(loadMatchingClasses): Initialize packages loaded on demand if we
are initialize.
* net/sf/jode/obfuscator/modules/ConstantAnalyzer.java:
Now extends SimpleAnalyzer.
(canonizeIfaceRef): Removed; it is now inherited.
(canonizeRef): likewise.
Big updates to handle jsr correctly.
(handleOpcode):  Moved method to BlockInfo.
* net/sf/jode/obfuscator/modules/SimpleAnalyzer.java:
(canonizeIfaceRef): New method, copied from ConstantAnalyzer.
(canonizeRef): call canonizeIfaceRef for interfaces.
* net/sf/jode/util/UnifyHash.java
(iterateHashCode): iterator now supports remove().
(remove): New method.


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1337 379699f6-c40d-0410-875b-85095c16579e
master
hoenicke 24 years ago
parent 1ce57d3614
commit 4352b285ab
  1. 98
      jode/ChangeLog
  2. 62
      jode/INSTALL
  3. 300
      jode/build.xml
  4. 6
      jode/scripts/jcpp.pl
  5. 16
      jode/src/net/sf/jode/bytecode/BasicBlockReader.java
  6. 206
      jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
  7. 108
      jode/src/net/sf/jode/bytecode/BasicBlocks.java
  8. 133
      jode/src/net/sf/jode/bytecode/BinaryInfo.java
  9. 63
      jode/src/net/sf/jode/bytecode/Block.java
  10. 404
      jode/src/net/sf/jode/bytecode/ClassInfo.java
  11. 22
      jode/src/net/sf/jode/bytecode/ClassPath.java
  12. 62
      jode/src/net/sf/jode/bytecode/ConstantPool.java
  13. 19
      jode/src/net/sf/jode/bytecode/FieldInfo.java
  14. 4
      jode/src/net/sf/jode/bytecode/Handler.java
  15. 58
      jode/src/net/sf/jode/bytecode/MethodInfo.java
  16. 73
      jode/src/net/sf/jode/bytecode/Subroutine.java
  17. 228
      jode/src/net/sf/jode/bytecode/TransformSubroutine.java
  18. 16
      jode/src/net/sf/jode/bytecode/package.html
  19. 3
      jode/src/net/sf/jode/decompiler/Decompiler.java
  20. 5
      jode/src/net/sf/jode/decompiler/Main.java
  21. 2
      jode/src/net/sf/jode/decompiler/ProgressListener.java
  22. 2
      jode/src/net/sf/jode/flow/FlowBlock.java
  23. 2
      jode/src/net/sf/jode/flow/Jump.java
  24. 4
      jode/src/net/sf/jode/flow/VariableStack.java
  25. 16
      jode/src/net/sf/jode/jvm/CodeVerifier.java
  26. 2
      jode/src/net/sf/jode/jvm/Interpreter.java
  27. 64
      jode/src/net/sf/jode/obfuscator/ClassIdentifier.java
  28. 22
      jode/src/net/sf/jode/obfuscator/PackageIdentifier.java
  29. 1753
      jode/src/net/sf/jode/obfuscator/modules/ConstantAnalyzer.java
  30. 34
      jode/src/net/sf/jode/obfuscator/modules/SimpleAnalyzer.java
  31. 31
      jode/src/net/sf/jode/util/UnifyHash.java

@ -1,3 +1,73 @@
2001-08-05 Jochen Hoenicke <jochen@gnu.org>
Documentation updates (INSTALL, javadoc).
Added JUnit Test cases.
* build.xml: Big update.
* net/sf/jode/bytecode/BasicBlock.java:
(updateMaxStackLocals): new method to calculate maxStack and
maxLocals.
(setBlocks): fixed calculation of handlers, call updateMaxLocals.
* net/sf/jode/bytecode/BasicBlockReader.java:
(maxLocals, maxStack): new fields.
(readCode): read maxStack/Locals into private fields.
(convert): check that maxStack/Locals match what we calculate.
* net/sf/jode/bytecode/BinaryInfo.java:
(getKnownAttributeCount): renamed to...
(getAttributeCount): ... this, and also count internal attributes.
Made it protected.
(readAttribute): made protected.
(drop): made protected.
(prepareAttributes): made protected.
(writeKnownAttributes): removed.
(writeAttributes): made protected, use getAttributeCount.
Changed policy: it doesn't call writeKnownAttribute, but instead
it expects sub classes to override this method.
(getAttributeSize): made protected, subclasses should override it.
Changed all subclasses to new policy.
* net/sf/jode/bytecode/Block.java:
(lineNr): Removed, it wasn't used.
(pop,push): Removed, replaced by ...
(maxpop,maxpush,delta): ... these, with slightly changed semantics.
(stackHeight): New variable.
(Block): Default Constructor doesn't initialize fields now.
(getCatchers): Renamed to ...
(getHandlers): ... this, changed all callers.
(initCode): Calculate maxpop, maxpush, delta correctly.
(getStackPopPush): Changed accordingly to new fields.
(setCode): Removed debugging output for illegal contents.
* net/sf/jode/bytecode/Classes.java: Reworked handling of inner
classes.
(innerClasses): Field mustn't be null anymore when loaded.
(setName): Update class in classpath.
* net/sf/jode/bytecode/ClassPath.java:
(renameClassInfo): new function, should only used by ClassInfo.
* net/sf/jode/bytecode/ConstantPool.java: made public.
(getUTF8,getRef,getClassType,getClassName): Don't allow the 0 index.
(iterateClassNames): New method.
* net/sf/jode/decompiler/Main.java:
(decompileClass): Catch ClassFormatExceptions and decompile
remaining classes.
* net/sf/jode/obfuscator/ClassIdentifier.java:
Updated handling of inner/extra classes to new ClassInfo behaviour.
(initSuperClasses): Load DECLARATION of super classes.
* net/sf/jode/obfuscator/PackageIdentifier.java:
Replace deprecated methods of ClassInfo with corresponding classpath
calls.
(loadMatchingClasses): Initialize packages loaded on demand if we
are initialize.
* net/sf/jode/obfuscator/modules/ConstantAnalyzer.java:
Now extends SimpleAnalyzer.
(canonizeIfaceRef): Removed; it is now inherited.
(canonizeRef): likewise.
Big updates to handle jsr correctly.
(handleOpcode): Moved method to BlockInfo.
* net/sf/jode/obfuscator/modules/SimpleAnalyzer.java:
(canonizeIfaceRef): New method, copied from ConstantAnalyzer.
(canonizeRef): call canonizeIfaceRef for interfaces.
* net/sf/jode/util/UnifyHash.java
(iterateHashCode): iterator now supports remove().
(remove): New method.
2001-07-30 Jochen Hoenicke <jochen@gnu.org> 2001-07-30 Jochen Hoenicke <jochen@gnu.org>
Changed compilation procedure to ant. Changed compilation procedure to ant.
@ -149,8 +219,8 @@
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-07-15 Jochen Hoenicke <jochen@gnu.org>
Applied patch from 2001-05-08 of Jode 1.1 tree: Applied patch from 2001-05-08 of Jode 1.1 tree:
* jode/jvm/CodeVerifier.java (doVerify): Don't check for * jode/jvm/CodeVerifier.java (doVerify): Don't check for
uninitialized objects in local or stack slots on backwards jump or uninitialized objects in local or stack slots on backwards jump or
exception blocks. Sun's jdk also doesn't check it, and I never exception blocks. Sun's jdk also doesn't check it, and I never
understood why it is necessary. But see JVM Spec 4.9.4. understood why it is necessary. But see JVM Spec 4.9.4.
2001-07-15 Jochen Hoenicke <jochen@gnu.org> 2001-07-15 Jochen Hoenicke <jochen@gnu.org>
@ -238,12 +308,12 @@
* jode/decompiler/Options.java (setOptions): changed gnu style * jode/decompiler/Options.java (setOptions): changed gnu style
to include GNU_SPACING. to include GNU_SPACING.
* jode/decompiler/ClassAnalyzer.java (dumpSource): Use * jode/decompiler/ClassAnalyzer.java (dumpSource): Use
open/closeBraceClass. open/closeBraceClass.
* jode/decompiler/MethodAnalyzer.java (dumpSource): Use * jode/decompiler/MethodAnalyzer.java (dumpSource): Use
open/closeBraceNoIndent. Call printOptionalSpace. open/closeBraceNoIndent. Call printOptionalSpace.
* jode/decompiler/InvokeOperator.java (dumpExpression): * jode/decompiler/InvokeOperator.java (dumpExpression):
Call printOptionalSpace, use open/closeBraceClass for inner Call printOptionalSpace, use open/closeBraceClass for inner
classes. classes.
* jode/decompiler/UnaryOperator.java (dumpExpression): Call * jode/decompiler/UnaryOperator.java (dumpExpression): Call
printOptionalSpace. printOptionalSpace.
@ -252,21 +322,21 @@
* jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT): * jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT):
new constant. new constant.
(openBrace, openBraceContinue, closeBrace, closeBraceNoSpace, (openBrace, openBraceContinue, closeBrace, closeBraceNoSpace,
closeBraceContinue): handle flush left. closeBraceContinue): handle flush left.
* jode/type/NullType.java (intersection): Removed, since the * jode/type/NullType.java (intersection): Removed, since the
version in ReferenceType is more correct. Before version in ReferenceType is more correct. Before
tNull.isOfType(tRange(X,tNull)) returned false, which lead to tNull.isOfType(tRange(X,tNull)) returned false, which lead to
incorrect behaviour in InvokeOperator.needsCast. incorrect behaviour in InvokeOperator.needsCast.
* jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the * jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the
"= null" hack for final fields; it was not correct, since the "= null" hack for final fields; it was not correct, since the
field could be initialized in a constructor. field could be initialized in a constructor.
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp):
Simplified the code, copy options always from child. Simplified the code, copy options always from child.
* jode/expr/InvokeOperator.java (isGetClass): Allow the method to * jode/expr/InvokeOperator.java (isGetClass): Allow the method to
be declared inside an outer class: We simply check if we can get be declared inside an outer class: We simply check if we can get
the method analyzer. the method analyzer.
(simplify): handle unifyParam. (simplify): handle unifyParam.
* jode/expr/PopOperator.java (getBreakPenalty): return penalty of * jode/expr/PopOperator.java (getBreakPenalty): return penalty of
inner expression. (dumpExpression): Call dumpExpression of inner expression. (dumpExpression): Call dumpExpression of
subexpression immediately without priority. subexpression immediately without priority.

@ -1,45 +1,23 @@
Before installing, make sure you have at least version 1.1 of the java Before installing, make sure you have at least version 1.1 of the java
developement kit installed. If you want to run this program you only developement kit installed. If you want to run this program you only
need the java runtime environment. Version 1.1 is quite old, I need the java runtime environment. Version 1.1 is quite old, I
recommend using Java 2 (jdk1.2 or above). recommend using Java 2 (jdk1.2 or above). You need perl if you want
to compile a 1.1 version.
This package was designed to use the GNU standard for configuration
and makefiles. To build and install do the following: This package was designed to use the ANT from the jakarta.apache.org
tools. I assume you have installed it correctly.
0). If you have downloaded the code from the CVS repository create
configure and Makefile.in with autoconf-2.13 and automake-1.4. Type Take some time to edit config.props. There are a few options you need
"aclocal; automake -a; autoconf". to take care of. (Unfortunately ant can't test for executables).
1). You need a java development kit (at least version 1.1), some unix Now you are ready to invoke ant. There are many possible targets, here
tools and some java packages. Make sure that you have all java are the most useful ones:
packages that are needed in your classpath. This are gnu.getopt, and
if you have JDK 1.1 you also need the collection classes and swing for all builds class files and documentation.
1.1. These packages are accessible from the following urls: build builds class files only (autodetects java version).
build-1.1 builds JDK1.1 class files.
CYGWIN (unix tools for win95/NT): doc builds documentation.
http://sourceware.cygnus.com/cygwin/ dist creates all release files.
test does some self tests. You need to have junit installed for this.
JDK 1.1: clean cleans everything that doesn't belong to the source distribution.
http://java.sun.com/products/jdk/1.1/index.htm cvsclean cleans everything that doesn't belong into the cvs repository.
Collection classes and Swing for JDK 1.1:
http://java.sun.com/beans/infobus/#DOWNLOAD_COLLECTIONS
http://java.sun.com/products/jfc/index.html#download-swing
JDK 1.2:
http://java.sun.com/products/jdk/1.2/index.html
Getopt:
http://www.urbanophile.com/arenn/hacking/download.html#getopt
2). Run the "configure" script to configure the package. There are
various options you might want to pass to configure to control how the
package is built. "configure --help" will give a complete list.
Use the --with-java option to specify the install directory of the jdk.
If you have jikes, you should specify it with --with-jikes. You can
give a path to the directory where it resides, otherwise it is
searched in the path.
3). Type "make" to build the package.

@ -19,52 +19,75 @@
--> -->
<!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd"> <!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd">
<project name="jode" default="build" basedir="."> <project name="jode" default="all" basedir=".">
<!-- set global properties for this build --> <!-- set global properties for this build -->
<property name="version" value="1.90"/> <property name="version" value="1.90-CVS"/>
<property name="build" value="build"/>
<property name="props" value="props"/>
<property name="dist" value="dist"/>
<property name="docs" value="docs"/>
<property name="lib" value="lib"/>
<property name="jcpp" value="scripts/jcpp.pl"/> <property name="build" value="${basedir}/build"/>
<property name="props" value="${basedir}/props"/>
<property name="dist" value="${basedir}/dist"/>
<property name="doc" value="${basedir}/doc"/>
<property name="lib" value="${basedir}/lib"/>
<property name="src" value="${basedir}/src"/>
<property name="scripts" value="${basedir}/scripts"/>
<property name="api.doc" value="doc/api"/>
<property name="test.src" value="test/src"/>
<property name="test.build" value="test/build"/>
<property name="test.log" value="test/log"/>
<property name="jcpp" value="${scripts}/jcpp.pl"/>
<property name="php2html" value="${scripts}/php2html.pl"/>
<property name="versionfile" value="${src}/jode/GlobalOptions.java"/> <property name="versionfile" value="${src}/jode/GlobalOptions.java"/>
<property file="config.props"/>
<path id="project.classpath"> <path id="project.classpath">
<pathelement path="${classpath}"/> <pathelement path="${classpath}"/>
<fileset dir="lib" includes="*.jar"/> <fileset dir="lib" includes="*.jar"/>
</path> </path>
<!-- --> <!-- ********* General targets ******* -->
<!-- compiles jode and creates its javadoc-files --> <!-- compiles jode and creates its javadoc-files -->
<target name="all" depends="dist,javadoc"/> <target name="all" depends="build,doc"/>
<!-- build the Jode files --> <!-- clean all -->
<target name="build" depends="check-jdk,preconfig"> <target name="clean" depends="clean-jcpp,clean-build,clean-doc,clean-test,clean-dist"/>
<mkdir dir="${build}"/> <target name="cvsclean" depends="clean,clean-html"/>
<javac srcdir="${src}"
destdir="${build}" <!-- ********* jcpp targets ******* -->
classpathref="project.classpath">
<exclude name="net/sf/jode/obfuscator/" /> <target name="check-jcpp" unless="perl.present">
<exclude name="net/sf/jode/bytecode/*Subroutine*" /> <fail message="need perl to configure for JDK 1.1"/>
</javac>
<copy todir="${build}">
<fileset dir="src"/>
</copy>
</target> </target>
<target name="build-1.1"> <target name="run-jcpp" depends="check-packages,check-jcpp">
<property name="collections.package" <execon dir="." executable="perl" parallel="true">
value="gnu.java.util.collections"/> <arg file="${jcpp}"/>
<property name="swing.package" <arg value="-DJDK11"/>
value="javax.swing"/> <arg value="-DCOLLECTIONS=${collections.package}"/>
<property name="jdk1.2+" value="false"/> <arg value="-DCOLLECTIONEXTRA=${collections.package}"/>
<antcall name="build"/> <arg value="-DJAVAX_SWING=${swing.package}"/>
<fileset dir="${src}" includes="**/*.java"/>
</execon>
</target>
<target name="clean-jcpp" if="perl.present">
<execon dir="." executable="perl" parallel="true">
<arg file="${jcpp}"/>
<arg value="-DJDK12"/>
<arg value="-DCOLLECTIONS=java.util"/>
<arg value="-DCOLLECTIONEXTRA=java.lang"/>
<arg value="-DJAVAX_SWING=javax.swing"/>
<fileset dir="${src}" includes="**/*.java"/>
</execon>
</target> </target>
<!-- ********* Check Environment ******* -->
<target name="check-jdk" unless="jdk1.1.forced"> <target name="check-jdk" unless="jdk1.1.forced">
<available property="jdk1.2+" classname="java.lang.ThreadLocal" /> <available property="jdk1.2+" classname="java.lang.ThreadLocal" />
<available property="jdk1.3+" classname="java.lang.StrictMath" /> <available property="jdk1.3+" classname="java.lang.StrictMath" />
@ -93,91 +116,191 @@
value="com.sun.java.util.collections" value="com.sun.java.util.collections"
classname="com.sun.java.util.collections.Set" classname="com.sun.java.util.collections.Set"
classpathref="project.classpath" /> classpathref="project.classpath" />
<available property="swing.package" value="javax.swing"
classname="javax.swing.JFrame"
classpathref="project.classpath" />
<available property="swing.package" value="com.sun.java.swing" <available property="swing.package" value="com.sun.java.swing"
classname="com.sun.java.swing.JFrame" classname="com.sun.java.swing.JFrame"
classpathref="project.classpath" /> classpathref="project.classpath" />
<available property="swing.package" value="javax.swing"
classname="javax.swing.JFrame"
classpathref="project.classpath" />
</target> </target>
<target name="preconfig" depends="check-jdk,check-getopt,preconfig.11,preconfig.12"/> <!-- ********* Build targets ******* -->
<target name="preconfig" depends="check-jdk,check-getopt,preconfig.11"/>
<target name="preconfig.11" unless="jdk1.2+"> <target name="preconfig.11" unless="jdk1.2+">
<property name="src" value="src-1.1"/> <antcall target="run-jcpp"/>
<antcall target="runjcpp"/>
</target> </target>
<target name="preconfig.12" if="jdk1.2+"> <target name="preconfig.12" if="jdk1.2+">
<property name="src" value="src"/> <antcall target="clean-jcpp"/>
</target> </target>
<target name="runjcpp" depends="check-packages"> <target name="build-1.1">
<copy todir="${src}"> <property name="jdk1.1.forced" value="on"/>
<fileset dir="src"/> <antcall target="build"/>
</target>
<target name="build" depends="check-jdk,preconfig">
<mkdir dir="${build}"/>
<javac srcdir="${src}"
destdir="${build}"
classpathref="project.classpath"
deprecation="on">
<!--
<exclude name="net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalOptimizer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java"/>
<exclude name="net/sf/jode/bytecode/*Subroutine*" />
-->
</javac>
<copy todir="${build}">
<fileset dir="${props}"/>
</copy> </copy>
<execon dir="." executable="perl" parallel="true">
<arg file="${jcpp}"/>
<arg value="-DJDK11"/>
<arg value="-DCOLLECTIONS=${collections.package}"/>
<arg value="-DCOLLECTIONEXTRA=${collections.package}"/>
<arg value="-DJAVAX_SWING=${swing.package}"/>
<fileset dir="${src}" includes="**/*.java"/>
</execon>
</target> </target>
<!-- create the jar-files --> <!-- clean the class files -->
<target name="dist" depends="distclass,distdoc"> <target name="clean-build">
<jar jarfile="${dist}/jode-${version}.jar" <delete dir="${build}"/>
basedir="${dist}" </target>
includes="jode.jar,doc.jar"/>
<!-- ********* Create Jar files ******* -->
<target name="dist-name1" unless="jdk1.2+">
<property name="dist.file" value="jode-${version}-JDK1.1"/>
</target>
<target name="dist-name2" if="jdk1.2+">
<property name="dist.file" value="jode-${version}"/>
</target>
<target name="dist-name" depends="preconfig,dist-name1,dist-name2"/>
<target name="dist" depends="dist-name,clean-dist,dist-class,dist-src,doc">
<jar jarfile="${dist}/${dist.name}.jar">
<fileset dir="${dist}"/>
<fileset dir=".">
<patternset>
<include name="${doc}/*.html"/>
<include name="${doc}/api/**"/>
</patternset>
</fileset>
<fileset dir="${lib}">
<patternset>
<include name="*getopt*.jar"/>
<include name="*collection*.jar" unless="jdk1.2+" />
</patternset>
</fileset>
</jar>
</target> </target>
<target name="distclass" depends="build"> <target name="dist-class" depends="build">
<mkdir dir="${dist}"/> <mkdir dir="${dist}"/>
<jar jarfile="${dist}/jode.jar" <jar jarfile="${dist}/jode.jar"
basedir="${build}"/> basedir="${build}"/>
</target> </target>
<target name="distdoc" depends="javadoc"> <target name="dist-src">
<jar jarfile="${dist}/doc.jar" <mkdir dir="${dist}"/>
basedir="${docs}"/> <jar jarfile="${dist}/src.jar"
basedir="${src}"/>
</target>
<target name="clean-dist">
<delete dir="${dist}"/>
</target>
<!-- ********* Javadoc targets ********** -->
<target name="doc" depends="doc-javadoc,doc-html"/>
<target name="check-html">
<uptodate property="html.uptodate">
<srcfiles dir="${basedir}" includes="${doc}/*.php"/>
<mapper type="glob" from="*.php" to="*.html"/>
</uptodate>
</target> </target>
<!-- javadoc --> <target name="doc-html" depends="check-html"
<target name="javadoc"> if="php.present" unless="html.uptodate">
<mkdir dir="${docs}/api"/> <execon dir="${doc}" executable="perl" parallel="true">
<arg value="${php2html}"/>
<fileset dir="${doc}" includes="*.php"/>
</execon>
</target>
<target name="doc-javadoc">
<mkdir dir="${api.doc}"/>
<javadoc packagenames="net.sf.jode.*" <javadoc packagenames="net.sf.jode.*"
sourcepath="${src}" sourcepath="${src}"
destdir="${docs}/api"/> destdir="${api.doc}"
use="yes">
<link offline="${javadoc.offline}" href="http://java.sun.com/products/jdk/1.2/docs/api/" packagelistLoc="${javadoc.packagelistLoc}"/>
</javadoc>
</target> </target>
<!-- ********* clean targets ************* --> <target name="clean-doc">
<delete dir="${api.doc}"/>
</target>
<!-- clean all --> <target name="clean-html">
<target name="clean" depends="cleanbuild,cleanjavadoc"/> <delete>
<fileset dir="${doc}" includes="*.html"/>
</delete>
</target>
<!-- ********* test targets ************* -->
<!-- clean the class files --> <target name="build-test" depends="build">
<target name="cleanbuild"> <mkdir dir="${test.build}"/>
<delete dir="${build}"/> <javac srcdir="${test.src}"
destdir="${test.build}"
classpathref="project.classpath"
classpath="${build}"
deprecation="on">
</javac>
</target> </target>
<!-- clean javadocs --> <target name="test" depends="build-test">
<target name="cleanjavadoc"> <mkdir dir="${test.log}"/>
<delete dir="${docs}/api"/> <junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement path="${test.build}"/>
<pathelement path="${build}"/>
<fileset dir="lib" includes="*.jar"/>
<fileset dir="/opt/ant/lib" includes="*.jar"/>
</classpath>
<formatter type="plain" />
<batchtest fork="no" todir="${test.log}">
<fileset dir="${test.src}">
<include name="**/*.java"/>
</fileset>
</batchtest>
</junit>
</target> </target>
<!-- version stuff --> <target name="test-cvs" depends="build-test">
<mkdir dir="${test.log}"/>
<junit printsummary="yes" fork="yes" haltonfailure="yes">
<classpath>
<pathelement path="${test.build}"/>
<pathelement path="${build}"/>
<fileset dir="lib" includes="*.jar"/>
<fileset dir="/usr/local/ant/lib" includes="*.jar"/>
</classpath>
<formatter type="plain" />
<batchtest fork="no" todir="${test.log}">
<fileset dir="${test.src}">
<include name="**/*.java"/>
</fileset>
</batchtest>
</junit>
</target>
<target name="clean-test">
<delete dir="${test.build}"/>
<delete dir="${test.log}"/>
</target>
<!-- ********* version targets ************* -->
<target name="setversion" if="version"> <target name="setversion" if="version">
<echo message="---------------------------------------------------"/>
<echo message=' Creating new Jode version: "${version}" !!!'/>
<echo message="==================================================="/>
<!--
search the old version information and replace it with the new version
we will search the $(mainclass) for 'String VERSION = "..."' and
replace the contents of the String with the new version.
-->
<echo message="updating version in ${versionfile} ..."/> <echo message="updating version in ${versionfile} ..."/>
<exec executable="perl"> <exec executable="perl">
<arg value="-i"/> <arg value="-i"/>
@ -185,13 +308,30 @@
<arg value='s/(String\s*version\s*=\s*")[^"]*/$1${version}/' /> <arg value='s/(String\s*version\s*=\s*")[^"]*/$1${version}/' />
<arg value="${versionfile}"/> <arg value="${versionfile}"/>
</exec> </exec>
</target>
<target name="commit" depends="setversion,test-cvs" if="version">
<antcall target="cvsclean"/>
<echo message="---------------------------------------------------"/>
<echo message=' Commiting new Jode version: ${version} !!!'/>
<echo message="==================================================="/>
<!-- <!--
commit the new $(VERSIONFILE) to the CVS search the old version information and replace it with the new version
we will search the $(mainclass) for 'String VERSION = "..."' and
replace the contents of the String with the new version.
-->
<!-- commit the new $(VERSIONFILE) to the CVS
<echo message="commiting updated file to CVS..."/> <echo message="commiting updated file to CVS..."/>
<cvs command='ci -m"new version ${version}" ${versionfile}'/> <cvs command='ci -m"new version ${version}" ${versionfile}'/>
--> -->
<!-- commit the new $(VERSIONFILE) to the CVS
<echo message="tagging files in CVS..."/>
<property
<cvs command="tag ${cvstag}"/>
-->
<echo message="...done!"/> <echo message="...done!"/>
<echo message="---------------------------------------------------"/> <echo message="---------------------------------------------------"/>
</target> </target>

@ -1,6 +1,6 @@
#!/usr/bin/perl -w #!/usr/bin/perl -w
# #
# jcpp Copyright (C) 1999 Jochen Hoenicke. # jcpp Copyright (C) 1999-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
@ -39,8 +39,8 @@
# After running jcpp the false branch is commented out. If the true # After running jcpp the false branch is commented out. If the true
# branch was commented out it will get commented in. # branch was commented out it will get commented in.
# #
# jcpp also definitions, useful for package renaming. The java file # jcpp can also change definitions, useful for package renaming. The
# must look like this: # java file should look like this:
# #
# ///#def COLLECTIONS java.util # ///#def COLLECTIONS java.util
# import java.util.Vector # import java.util.Vector

@ -75,6 +75,9 @@ class BasicBlockReader implements Opcodes {
BasicBlocks bb; BasicBlocks bb;
Block[] blocks; Block[] blocks;
int maxStack;
int maxLocals;
public BasicBlockReader(BasicBlocks bb) { public BasicBlockReader(BasicBlocks bb) {
this.bb = bb; this.bb = bb;
} }
@ -298,13 +301,20 @@ class BasicBlockReader implements Opcodes {
if (start != -1) if (start != -1)
convertBlock(start, count); convertBlock(start, count);
bb.setBlocks(blocks, getSuccBlock(0), convertHandlers()); bb.setBlocks(blocks, getSuccBlock(0), convertHandlers());
if (bb.maxStack > maxStack)
throw new ClassFormatException("Only allocated "+maxStack
+" stack slots for method, needs "
+bb.maxStack);
if (bb.maxLocals > maxLocals)
throw new ClassFormatException("Only allocated "+maxLocals
+" local slots for method, needs "
+bb.maxLocals);
} }
public void readCode(ConstantPool cp, public void readCode(ConstantPool cp,
DataInputStream input) throws IOException { DataInputStream input) throws IOException {
bb.maxStack = input.readUnsignedShort(); maxStack = input.readUnsignedShort();
int maxLocals = input.readUnsignedShort(); maxLocals = input.readUnsignedShort();
bb.maxLocals = maxLocals;
int codeLength = input.readInt(); int codeLength = input.readInt();
infos = new InstrInfo[codeLength]; infos = new InstrInfo[codeLength];

@ -76,8 +76,10 @@ class BasicBlockWriter implements Opcodes {
new LocalVariableInfo[blocks.length][]; new LocalVariableInfo[blocks.length][];
int startBlockNr = startBlock.getBlockNr(); int startBlockNr = startBlock.getBlockNr();
atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()]; atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()];
for (int i=0; i < bb.getParamCount(); i++) for (int i=0; i < bb.getParamCount(); i++) {
atStart[startBlockNr][i] = bb.getParamInfo(i); LocalVariableInfo lvi = bb.getParamInfo(i);
atStart[startBlockNr][i] = lvi.getName() != null ? lvi : null;
}
/* We currently ignore the jsr/ret issue. Should be okay, /* We currently ignore the jsr/ret issue. Should be okay,
* though, since it can only generate a bit too much local * though, since it can only generate a bit too much local
@ -94,36 +96,47 @@ class BasicBlockWriter implements Opcodes {
if (instrs[i].hasLocal()) { if (instrs[i].hasLocal()) {
LocalVariableInfo lvi = instrs[i].getLocalInfo(); LocalVariableInfo lvi = instrs[i].getLocalInfo();
int slot = lvi.getSlot(); int slot = lvi.getSlot();
if (life[slot] != null life[slot] = lvi.getName() != null ? lvi : null;
&& life[slot] != lvi)
life[slot] = null;
if (life[slot] == null
&& lvi.getName() != null)
life[slot] = lvi;
} }
} }
Block[] succs = block.getSuccs(); Block[] succs = block.getSuccs();
if (succs != null) { for (int j = 0; j < succs.length; j++) {
for (int j = 0; j < succs.length; j++) { if (succs[j] == null)
if (succs[j] == null) continue;
continue; int succNr = succs[j].getBlockNr();
int succNr = succs[j].getBlockNr(); if (atStart[succNr] == null) {
if (atStart[succNr] == null) { atStart[succNr] = (LocalVariableInfo[]) life.clone();
atStart[succNr] = (LocalVariableInfo[]) life.clone(); todo.push(succs[j]);
} else {
boolean changed = false;
for (int k = 0; k < life.length; k++) {
if (atStart[succNr][k] != life[k]
&& atStart[succNr][k] != null) {
atStart[succNr][k] = null;
changed = true;
}
}
if (changed && !todo.contains(succs[j]))
todo.push(succs[j]); todo.push(succs[j]);
} else { }
boolean changed = false; }
for (int k = 0; k < life.length; k++) { Handler[] handlers = block.getHandlers();
if (atStart[succNr][k] != life[k] for (int j = 0; j < handlers.length; j++) {
&& atStart[succNr][k] != null) { int succNr = handlers[j].getCatcher().getBlockNr();
atStart[succNr][k] = null; if (atStart[succNr] == null) {
changed = true; atStart[succNr] = (LocalVariableInfo[]) life.clone();
} todo.push(handlers[j].getCatcher());
} else {
boolean changed = false;
for (int k = 0; k < life.length; k++) {
if (atStart[succNr][k] != life[k]
&& atStart[succNr][k] != null) {
atStart[succNr][k] = null;
changed = true;
} }
if (changed && !todo.contains(succs[j]))
todo.push(succs[j]);
} }
if (changed && !todo.contains(handlers[j].getCatcher()))
todo.push(handlers[j].getCatcher());
} }
} }
} }
@ -137,6 +150,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry(); current[slot] = new LVTEntry();
current[slot].startAddr = 0; current[slot].startAddr = 0;
current[slot].lvi = lvi; current[slot].lvi = lvi;
System.err.println("lvi at init,"+slot+": "+lvi);
} }
} }
@ -158,6 +172,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry(); current[slot] = new LVTEntry();
current[slot].startAddr = addr; current[slot].startAddr = addr;
current[slot].lvi = atStart[i][slot]; current[slot].lvi = atStart[i][slot];
System.err.println("lvi at "+i+","+slot+": "+current[slot].lvi);
} }
} }
@ -178,6 +193,7 @@ class BasicBlockWriter implements Opcodes {
current[slot] = new LVTEntry(); current[slot] = new LVTEntry();
current[slot].startAddr = addr; current[slot].startAddr = addr;
current[slot].lvi = lvi; current[slot].lvi = lvi;
System.err.println("lvi at "+i+","+k+","+slot+": "+current[slot].lvi);
} }
} }
addr += instrLength[i][k]; addr += instrLength[i][k];
@ -222,8 +238,8 @@ class BasicBlockWriter implements Opcodes {
gotos[0] = startBlock.getBlockNr(); gotos[0] = startBlock.getBlockNr();
} }
next_block:
for (int i = 0; i < blocks.length; i++) { for (int i = 0; i < blocks.length; i++) {
boolean hasDefaultSucc = true;
blockAddr[i] = addr; blockAddr[i] = addr;
Instruction[] instrs = blocks[i].getInstructions(); Instruction[] instrs = blocks[i].getInstructions();
instrLength[i] = new int[instrs.length]; instrLength[i] = new int[instrs.length];
@ -265,7 +281,7 @@ class BasicBlockWriter implements Opcodes {
} else if (value >= Short.MIN_VALUE } else if (value >= Short.MIN_VALUE
&& value <= Short.MAX_VALUE) { && value <= Short.MAX_VALUE) {
length = 3; length = 3;
break switch_opc; break switch_opc;
} }
} }
if (gcp.putConstant(constant) < 256) { if (gcp.putConstant(constant) < 256) {
@ -304,30 +320,28 @@ class BasicBlockWriter implements Opcodes {
length = 2; length = 2;
else else
length = 4; length = 4;
gotos[i+1] = -2; hasDefaultSucc = false;
break; break;
} }
case opc_lookupswitch: { case opc_lookupswitch: {
length = 3-(addr % 4); length = (~addr) & 3; /* padding */
int[] values = instr.getValues(); int[] values = instr.getValues();
int npairs = values.length; int npairs = values.length;
for (int k=0; k< succs.length; k++) { for (int k=0; k< succs.length; k++) {
if (succs[k] == null) if (succs[k] == null)
needRet = true; needRet = true;
} }
if (npairs > 0) { if (npairs > 0
int tablesize = values[npairs-1] - values[0] + 1; && 4 + 4 * (values[npairs-1] - values[0] + 1)
if (4 + tablesize * 4 < 8 * npairs) { <= 8 * npairs) {
// Use a table switch // Use a table switch
length += 13 + 4 * tablesize; length += 13 + 4 * (values[npairs-1] - values[0] + 1);
break; } else {
} // Use a lookup switch
length += 9 + 8 * npairs;
} }
// Use a lookup switch hasDefaultSucc = false;
length += 9 + 8 * npairs; break;
// The goto is inclusive through the default part.
gotos[i+1] = -2;
continue next_block;
} }
case opc_jsr: case opc_jsr:
conds[i+1] = succs[0].getBlockNr(); conds[i+1] = succs[0].getBlockNr();
@ -391,8 +405,8 @@ class BasicBlockWriter implements Opcodes {
case opc_freturn: case opc_dreturn: case opc_areturn: case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_return: case opc_return:
case opc_athrow: case opc_athrow:
gotos[i+1] = -2;
length = 1; length = 1;
hasDefaultSucc = false;
break; break;
case opc_nop: case opc_nop:
case opc_iaload: case opc_laload: case opc_faload: case opc_iaload: case opc_laload: case opc_faload:
@ -434,20 +448,26 @@ class BasicBlockWriter implements Opcodes {
instrLength[i][j] = length; instrLength[i][j] = length;
addr += length; addr += length;
} }
Block defaultSucc = succs[succs.length-1]; if (hasDefaultSucc) {
if (defaultSucc == null) { Block defaultSucc = succs[succs.length-1];
// This is a return if (defaultSucc == null) {
gotos[i+1] = -1; // This is a return
isRet.set(i+1); gotos[i+1] = -1;
lastRetAddr = addr; isRet.set(i+1);
addr++; lastRetAddr = addr;
} else if (defaultSucc.getBlockNr() == i + 1) { hasRet = true;
// no need for any jump addr++;
gotos[i+1] = succs[succs.length-1].getBlockNr(); } else if (defaultSucc.getBlockNr() == i + 1) {
// no need for any jump
gotos[i+1] = succs[succs.length-1].getBlockNr();
} else {
// Reserve space for a normal goto.
gotos[i+1] = succs[succs.length-1].getBlockNr();
addr += 3;
}
} else { } else {
// Reserve space for a normal goto. // No goto needed for this block
gotos[i+1] = succs[succs.length-1].getBlockNr(); gotos[i+1] = -2;
addr += 3;
} }
} }
if (needRet && !hasRet) { if (needRet && !hasRet) {
@ -470,9 +490,9 @@ class BasicBlockWriter implements Opcodes {
int from = blockAddr[i] - 3; int from = blockAddr[i] - 3;
if (gotoNr != i + 1) if (gotoNr != i + 1)
from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3; from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3;
int dist; int dist = Integer.MAX_VALUE;
if (condNr == -1) { if (condNr == -1) {
if (!retAtEnd) { if (retAtEnd) {
dist = blockAddr[blockAddr.length-1] - 1 - from; dist = blockAddr[blockAddr.length-1] - 1 - from;
} else { } else {
for (int j = 0; j < gotos.length; j++) { for (int j = 0; j < gotos.length; j++) {
@ -483,7 +503,8 @@ class BasicBlockWriter implements Opcodes {
break; break;
} }
} }
throw new InternalError(); if (dist == Integer.MAX_VALUE)
throw new InternalError();
} }
} else { } else {
dist = blockAddr[condNr] - from; dist = blockAddr[condNr] - from;
@ -565,6 +586,7 @@ class BasicBlockWriter implements Opcodes {
gcp.putUTF8("LocalVariableTable"); gcp.putUTF8("LocalVariableTable");
int count = lvt.length; int count = lvt.length;
for (int i=0; i < count; i++) { for (int i=0; i < count; i++) {
System.err.println("lvt: "+lvt[i].lvi);
gcp.putUTF8(lvt[i].lvi.getName()); gcp.putUTF8(lvt[i].lvi.getName());
gcp.putUTF8(lvt[i].lvi.getType()); gcp.putUTF8(lvt[i].lvi.getType());
} }
@ -590,13 +612,12 @@ class BasicBlockWriter implements Opcodes {
output.writeShort(lvt[i].lvi.getSlot()); output.writeShort(lvt[i].lvi.getSlot());
} }
} }
if (lnt != null) { if (lntCount > 0) {
output.writeShort(gcp.putUTF8("LineNumberTable")); output.writeShort(gcp.putUTF8("LineNumberTable"));
int count = lnt.length / 2; int length = 2 + 4 * lntCount;
int length = 2 + 4 * count;
output.writeInt(length); output.writeInt(length);
output.writeShort(count); output.writeShort(lntCount);
for (int i=0; i < count; i++) { for (int i = 0; i < lntCount; i++) {
output.writeShort(lnt[2*i]); output.writeShort(lnt[2*i]);
output.writeShort(lnt[2*i+1]); output.writeShort(lnt[2*i+1]);
} }
@ -629,11 +650,11 @@ class BasicBlockWriter implements Opcodes {
} }
int lntPtr = 0; int lntPtr = 0;
next_block:
for (int i = 0; i< blocks.length; i++) { for (int i = 0; i< blocks.length; i++) {
boolean hasDefaultSucc = true;
Block[] succs = blocks[i].getSuccs(); Block[] succs = blocks[i].getSuccs();
if (addr != blockAddr[i]) if (addr != blockAddr[i])
throw new InternalError("Address calculation broken!"); throw new InternalError("Address calculation broken for "+i+": "+blockAddr[i]+"!="+addr+"!");
Instruction[] instructions = blocks[i].getInstructions(); Instruction[] instructions = blocks[i].getInstructions();
int size = instructions.length; int size = instructions.length;
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
@ -679,7 +700,8 @@ class BasicBlockWriter implements Opcodes {
output.writeByte(opcode); output.writeByte(opcode);
output.writeShort(slot); output.writeShort(slot);
} }
continue next_block; hasDefaultSucc = false;
break;
} }
case opc_ldc: case opc_ldc:
case opc_ldc2_w: { case opc_ldc2_w: {
@ -782,18 +804,19 @@ class BasicBlockWriter implements Opcodes {
} else { } else {
int dist; int dist;
if (dest == null) { if (dest == null) {
if (!retAtEnd) { if (retAtEnd) {
dist = blockAddr[blocks.length] - 1 - addr; dist = blockAddr[blocks.length] - 1 - addr;
} else { } else {
for (int k = 0; k < blocks.length + 1; k++) { for (int k = 0; ; k++) {
if (isRet.get(k)) { if (isRet.get(k)) {
dist = blockAddr[k] - 1 - addr; dist = blockAddr[k] - 1 - addr;
if (dist >= Short.MIN_VALUE if (dist >= Short.MIN_VALUE
&& dist <= Short.MAX_VALUE) && dist <= Short.MAX_VALUE)
break; break;
} }
if (k == blocks.length)
throw new InternalError();
} }
throw new InternalError();
} }
} else { } else {
dist = blockAddr[dest.getBlockNr()] - addr; dist = blockAddr[dest.getBlockNr()] - addr;
@ -814,7 +837,7 @@ class BasicBlockWriter implements Opcodes {
if (npairs > 0) { if (npairs > 0) {
int tablesize = values[npairs-1] - values[0] + 1; int tablesize = values[npairs-1] - values[0] + 1;
if (4 + tablesize * 4 < 8 * npairs) { if (4 + tablesize * 4 <= 8 * npairs) {
// Use a table switch // Use a table switch
output.writeByte(opc_tableswitch); output.writeByte(opc_tableswitch);
output.write(new byte[align]); output.write(new byte[align]);
@ -832,7 +855,8 @@ class BasicBlockWriter implements Opcodes {
: blockAddr[succs[k].getBlockNr()]; : blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr); output.writeInt(dest - addr);
} }
continue next_block; hasDefaultSucc = false;
break;
} }
} }
// Use a lookup switch // Use a lookup switch
@ -847,7 +871,8 @@ class BasicBlockWriter implements Opcodes {
: blockAddr[succs[k].getBlockNr()]; : blockAddr[succs[k].getBlockNr()];
output.writeInt(dest - addr); output.writeInt(dest - addr);
} }
continue next_block; hasDefaultSucc = false;
break;
} }
case opc_getstatic: case opc_getstatic:
@ -903,7 +928,9 @@ class BasicBlockWriter implements Opcodes {
case opc_ireturn: case opc_lreturn: case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn: case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_athrow: case opc_return: case opc_athrow: case opc_return:
continue next_block; output.writeByte(opcode);
hasDefaultSucc = false;
break;
case opc_nop: case opc_nop:
case opc_iaload: case opc_laload: case opc_faload: case opc_iaload: case opc_laload: case opc_faload:
@ -944,19 +971,23 @@ class BasicBlockWriter implements Opcodes {
} }
addr += instrLength[i][j]; addr += instrLength[i][j];
} }
// Check which type of goto we should use at end of this block. if (hasDefaultSucc) {
Block defaultSucc = succs[succs.length - 1]; // Check which type of goto we should use at end of this block.
if (isRet.get(i+1)) { Block defaultSucc = succs[succs.length - 1];
output.writeByte(opc_return); if (isRet.get(i+1)) {
addr++; output.writeByte(opc_return);
} else if (isWide.get(i+1)) { addr++;
output.writeByte(opc_goto_w); } else if (isWide.get(i+1)) {
output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr); output.writeByte(opc_goto_w);
addr+=5; output.writeInt(blockAddr[defaultSucc.getBlockNr()]
} else if (defaultSucc.getBlockNr() != i+1) { - addr);
output.writeByte(opc_goto); addr+=5;
output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr); } else if (defaultSucc.getBlockNr() != i+1) {
addr+=3; output.writeByte(opc_goto);
output.writeShort(blockAddr[defaultSucc.getBlockNr()]
- addr);
addr+=3;
}
} }
} }
if (retAtEnd) { if (retAtEnd) {
@ -977,4 +1008,3 @@ class BasicBlockWriter implements Opcodes {
} }
} }
} }

@ -26,6 +26,8 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.BitSet;
import java.util.Stack;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -68,8 +70,9 @@ import java.lang.UnsupportedOperationException;
* necessary, you don't have to care about that.</p> * necessary, you don't have to care about that.</p>
* *
* @see net.sf.jode.bytecode.Block * @see net.sf.jode.bytecode.Block
* @see net.sf.jode.bytecode.Instruction */ * @see net.sf.jode.bytecode.Instruction
public class BasicBlocks extends BinaryInfo { */
public class BasicBlocks extends BinaryInfo implements Opcodes {
/** /**
* The method info which contains the basic blocks. * The method info which contains the basic blocks.
@ -153,12 +156,84 @@ public class BasicBlocks extends BinaryInfo {
return paramInfos.length; return paramInfos.length;
} }
public void setMaxStack(int ms) { /**
maxStack = ms; * Updates the maxStack and maxLocals according to the current code.
} * Call this every time you change the code.
*/
public void updateMaxStackLocals() {
maxLocals = getParamCount();
maxStack = 0;
public void setMaxLocals(int ml) { if (startBlock == null)
maxLocals = ml; return;
BitSet visited = new BitSet();
Stack todo = new Stack();
int[] poppush = new int[2];
startBlock.stackHeight = 0;
todo.push(startBlock);
while (!todo.isEmpty()) {
Block block = (Block) todo.pop();
int stackHeight = block.stackHeight;
if (stackHeight + block.maxpush > maxStack)
maxStack = stackHeight + block.maxpush;
stackHeight += block.delta;
Block[] succs = block.getSuccs();
Instruction[] instr = block.getInstructions();
for (int i = 0; i < instr.length; i++) {
if (instr[i].hasLocal()) {
int slotlimit = instr[i].getLocalSlot() + 1;
int opcode = instr[i].getOpcode();
if (opcode == opc_lstore || opcode == opc_dstore
|| opcode == opc_lload || opcode == opc_dload)
slotlimit++;
if (slotlimit > maxLocals)
maxLocals = slotlimit;
}
}
if (instr.length > 0
&& instr[instr.length-1].getOpcode() == opc_jsr) {
if (!visited.get(succs[0].blockNr)) {
succs[0].stackHeight = stackHeight + 1;
todo.push(succs[0]);
visited.set(succs[0].blockNr);
} else if (succs[0].stackHeight != stackHeight + 1)
throw new IllegalArgumentException
("Block has two different stack heights.");
if (succs[1] != null && !visited.get(succs[1].blockNr)) {
succs[1].stackHeight = stackHeight;
todo.push(succs[1]);
visited.set(succs[1].blockNr);
} else if ((succs[1] == null ? 0 : succs[1].stackHeight)
!= stackHeight)
throw new IllegalArgumentException
("Block has two different stack heights.");
} else {
for (int i = 0; i < succs.length; i++) {
if (succs[i] != null && !visited.get(succs[i].blockNr)) {
succs[i].stackHeight = stackHeight;
todo.push(succs[i]);
visited.set(succs[i].blockNr);
} else if ((succs[i] == null ? 0 : succs[i].stackHeight)
!= stackHeight)
throw new IllegalArgumentException
("Block has two different stack heights.");
}
}
Handler[] handler = block.getHandlers();
for (int i = 0; i < handler.length; i++) {
if (!visited.get(handler[i].getCatcher().blockNr)) {
handler[i].getCatcher().stackHeight = 1;
todo.push(handler[i].getCatcher());
visited.set(handler[i].getCatcher().blockNr);
} else if (handler[i].getCatcher().stackHeight != 1)
throw new IllegalArgumentException
("Block has two different stack heights.");
}
}
} }
public void setBlocks(Block[] blocks, Block startBlock, public void setBlocks(Block[] blocks, Block startBlock,
@ -173,15 +248,18 @@ public class BasicBlocks extends BinaryInfo {
for (int j = 0; j < handlers.length; j++) { for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getStart() == blocks[i]) if (handlers[j].getStart() == blocks[i])
activeHandlers.add(handlers[j]); activeHandlers.add(handlers[j]);
if (handlers[j].getEnd() == blocks[i])
activeHandlers.remove(handlers[j]);
} }
if (activeHandlers.size() == 0) if (activeHandlers.size() == 0)
blocks[i].catchers = Handler.EMPTY; blocks[i].catchers = Handler.EMPTY;
else else
blocks[i].catchers = blocks[i].catchers =
(Handler[]) activeHandlers.toArray(Handler.EMPTY); (Handler[]) activeHandlers.toArray(Handler.EMPTY);
for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getEnd() == blocks[i])
activeHandlers.remove(handlers[j]);
}
} }
updateMaxStackLocals();
// TransformSubroutine.createSubroutineInfo(this); // TransformSubroutine.createSubroutineInfo(this);
} }
@ -212,7 +290,7 @@ public class BasicBlocks extends BinaryInfo {
dumpCode(GlobalOptions.err); dumpCode(GlobalOptions.err);
} }
void readAttribute(String name, int length, ConstantPool cp, protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input, DataInputStream input,
int howMuch) throws IOException { int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL if (howMuch >= ClassInfo.ALMOSTALL
@ -256,15 +334,17 @@ public class BasicBlocks extends BinaryInfo {
BasicBlockWriter bbw; BasicBlockWriter bbw;
void prepareWriting(GrowableConstantPool gcp) { void prepareWriting(GrowableConstantPool gcp) {
bbw = new BasicBlockWriter(this, gcp); bbw = new BasicBlockWriter(this, gcp);
prepareAttributes(gcp);
} }
int getKnownAttributeCount() { protected int getAttributeCount() {
return bbw.getAttributeCount(); return super.getAttributeCount() + bbw.getAttributeCount();
} }
void writeKnownAttributes(GrowableConstantPool gcp, protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output) DataOutputStream output)
throws IOException { throws IOException {
super.writeAttributes(gcp, output);
bbw.writeAttributes(gcp, output); bbw.writeAttributes(gcp, output);
} }

@ -45,8 +45,15 @@ import java.util.Iterator;
* this package as appropriate. This methods are only useful for non * this package as appropriate. This methods are only useful for non
* standard attributes.</p> * standard attributes.</p>
* *
* <p>One application of this attributes are installation classes. * <p>You can provide new attributes by overriding the protected
* These classes have a special attribute containing a zip of the * methods of this class. This makes it possible to use constant pool
* entries in the attributes.</p>
*
* <p>Another possibility is to add the attributes with the public
* method. This way you don't need to extend the classes, but you
* can't use a constant pool for the contents of the attributes. One
* possible application of this are installation classes. These
* classes have a special attribute containing a zip archive of the
* files that should be installed. There are other possible uses, * files that should be installed. There are other possible uses,
* e.g. putting native machine code for some architectures into the * e.g. putting native machine code for some architectures into the
* class.</p> * class.</p>
@ -70,14 +77,22 @@ public class BinaryInfo {
} }
} }
int getKnownAttributeCount() { /**
return 0; * Reads in an attributes of this class. Overwrite this method if
} * you want to handle your own attributes. If you don't know how
* to handle an attribute call this method for the super class.
void readAttribute(String name, int length, * @param name the attribute name.
ConstantPool constantPool, * @param length the length of the attribute.
DataInputStream input, * @param constantPool the constant pool of the class.
int howMuch) throws IOException { * @param input a data input stream where you can read the attribute
* from. It will protect you to read more over the attribute boundary.
* @param howMuch the constant that was given to the {@link
* ClassInfo#load} function when loading this class.
*/
protected void readAttribute(String name, int length,
ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
byte[] data = new byte[length]; byte[] data = new byte[length];
input.readFully(data); input.readFully(data);
if (howMuch >= ClassInfo.ALL) { if (howMuch >= ClassInfo.ALL) {
@ -156,12 +171,37 @@ public class BinaryInfo {
} }
} }
void drop(int keep) { /**
* Drops information from this info. Override this to drop your
* own info and don't forget to call the method of the super class.
* @param howMuch the constant that was given to the {@link
* ClassInfo#drop} function when loading this class.
*/
protected void drop(int keep) {
if (keep < ClassInfo.ALL) if (keep < ClassInfo.ALL)
unknownAttributes = null; unknownAttributes = null;
} }
void prepareAttributes(GrowableConstantPool gcp) { /**
* Returns the number of attributes of this class. Overwrite this
* method if you want to add your own attributes by providing a
* writeAttributes method. You should call this method for the
* super class and add the number of your own attributes to the
* returned value.
* @return the number of attributes of this class.
*/
protected int getAttributeCount() {
return unknownAttributes != null ? unknownAttributes.size() : 0;
}
/**
* Prepare writing your attributes. Overwrite this method if you
* want to add your own attributes, which need constants on the
* class pool. Add the necessary constants to the constant pool
* and call this method for the super class.
* @param gcp The growable constant pool.
*/
protected void prepareAttributes(GrowableConstantPool gcp) {
if (unknownAttributes == null) if (unknownAttributes == null)
return; return;
Iterator i = unknownAttributes.keySet().iterator(); Iterator i = unknownAttributes.keySet().iterator();
@ -169,19 +209,27 @@ public class BinaryInfo {
gcp.putUTF8((String) i.next()); gcp.putUTF8((String) i.next());
} }
void writeKnownAttributes /**
(GrowableConstantPool constantPool, * <p>Writes the attributes to the output stream.
DataOutputStream output) throws IOException { * Overwrite this method if you want to add your own attributes.
} * All constants you need from the growable constant pool must
* have been previously registered by the {@link #prepareAttributes}
void writeAttributes * method.</p>
*
* First call the method of the super class. Afterwrites write
* each of your own attributes including the attribute header
* (name and length entry).
*
* @param gcp The growable constant pool, which is not growable anymore.
* @param output the data output stream. You must write exactly
* as many bytes to it as you have told with the {@link
* #getAttributeSize} method.
*/
protected void writeAttributes
(GrowableConstantPool constantPool, (GrowableConstantPool constantPool,
DataOutputStream output) throws IOException { DataOutputStream output) throws IOException {
int count = getKnownAttributeCount(); int count = getAttributeCount();
if (unknownAttributes != null)
count += unknownAttributes.size();
output.writeShort(count); output.writeShort(count);
writeKnownAttributes(constantPool, output);
if (unknownAttributes != null) { if (unknownAttributes != null) {
Iterator i = unknownAttributes.entrySet().iterator(); Iterator i = unknownAttributes.entrySet().iterator();
while (i.hasNext()) { while (i.hasNext()) {
@ -195,7 +243,19 @@ public class BinaryInfo {
} }
} }
int getAttributeSize() { /**
* Gets the total length of all attributes in this binary info.
* Overwrite this method if you want to add your own attributes
* and add the size of your attributes to the value returned by
* the super class.<br>
*
* Currently you only need to write this if you extend
* BasicBlocks.
*
* @return the total length of all attributes, including their
* headers and the number of attributes field.
*/
protected int getAttributeSize() {
int size = 2; /* attribute count */ int size = 2; /* attribute count */
if (unknownAttributes != null) { if (unknownAttributes != null) {
Iterator i = unknownAttributes.values().iterator(); Iterator i = unknownAttributes.values().iterator();
@ -206,9 +266,12 @@ public class BinaryInfo {
} }
/** /**
* Finds a non standard attribute with the given name. * Finds a non standard attribute with the given name. You don't
* have access to the constant pool. Instead extend this class
* and override readAttribute method if you need the pool.
* @param name the name of the attribute. * @param name the name of the attribute.
* @return the contents of the attribute, null if not found. * @return the contents of the attribute, null if not found.
* @see #readAttribute
*/ */
public byte[] findAttribute(String name) { public byte[] findAttribute(String name) {
if (unknownAttributes != null) if (unknownAttributes != null)
@ -217,7 +280,10 @@ public class BinaryInfo {
} }
/** /**
* Gets all non standard attributes * Gets all non standard attributes.
* @return an iterator for all attributes. The values returned by
* the next() method of the iterator are of byte[] type.
* @see #findAttribute
*/ */
public Iterator getAttributes() { public Iterator getAttributes() {
if (unknownAttributes != null) if (unknownAttributes != null)
@ -226,19 +292,24 @@ public class BinaryInfo {
} }
/** /**
* Adds a new non standard attribute or replaces an old one with the * Adds a new non standard attribute or replaces an old one with
* same name. * the same name. If it already exists, it will be overwritten.
* Note that there's now way to correlate the contents with a
* constant pool. If you need that extend this class and override
* the methods {@link #getAttributeCount}, {@link
* #prepareAttributes}, {@link #writeAttributes}, and {@link
* #getAttributeSize}.
* @param name the name of the attribute. * @param name the name of the attribute.
* @param contents the new contens. * @param contents the new contens.
*/ */
public void setAttribute(String name, byte[] contents) { public void addAttribute(String name, byte[] contents) {
if (unknownAttributes == null) if (unknownAttributes == null)
unknownAttributes = new SimpleMap(); unknownAttributes = new SimpleMap();
unknownAttributes.put(name, contents); unknownAttributes.put(name, contents);
} }
/** /**
* Removes a new non standard attribute. * Removes a non standard attributes.
* @param name the name of the attribute. * @param name the name of the attribute.
* @return the old contents of the attribute. * @return the old contents of the attribute.
*/ */
@ -249,11 +320,9 @@ public class BinaryInfo {
} }
/** /**
* Removes all non standard attribute. * Removes all non standard attributes.
*/ */
public void removeAllAttributes() { public void removeAllAttributes() {
unknownAttributes = null; unknownAttributes = null;
} }
} }

@ -92,26 +92,32 @@ public final class Block {
int blockNr; int blockNr;
/** /**
* The blockNr of this block. Set by BasicBlocks. * The number of items this block takes from the stack with
* respect to the stack items at the beginning of the block.
*/ */
int lineNr; int maxpop;
/** /**
* The number of items this block takes from the stack. * The maximum number of items the stack may grow.
*/ */
int pop; int maxpush;
/** /**
* The number of items this block puts on the stack. * The difference stack items after the block minus stack items
* before block.
*/ */
int push; int delta;
/**
* The stack height at the beginning of this block.
* Only valid after the block was inserted in a BasicBlocks and
* the updateMaxStackLocals() of BasicBlocks was called.
*/
int stackHeight;
/** /**
* Creates a new empty block, with a null successor array. * Creates a new block uninitialized block. You mustn't really
* use it (except as successor for other blocks) until you have
* set the code.
*/ */
public Block() { public Block() {
instrs = new Instruction[0];
succs = null;
} }
/** /**
@ -134,12 +140,13 @@ public final class Block {
} }
/** /**
* Gets the exception handlers which try part contains this block. * Gets the exception handlers whose try region contains this
* You can't set them since they are calculated automatically. * block. You can't set them since they are calculated
* automatically.
* @return the exception handlers. * @return the exception handlers.
* @see BasicBlocks#setExceptionHandlers * @see BasicBlocks#setBlocks
*/ */
public Handler[] getCatchers() { public Handler[] getHandlers() {
return catchers; return catchers;
} }
@ -158,15 +165,18 @@ public final class Block {
private void initCode() { private void initCode() {
int size = instrs.length; int size = instrs.length;
maxpop = maxpush = 0;
int depth = 0; int depth = 0;
int poppush[] = new int[2]; int poppush[] = new int[2];
boolean needGoto = true; boolean needGoto = true;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
instrs[i].getStackPopPush(poppush); instrs[i].getStackPopPush(poppush);
depth += poppush[0]; depth -= poppush[0];
if (pop < depth) if (maxpop < -depth)
pop = depth; maxpop = -depth;
depth -= poppush[1]; depth += poppush[1];
if (maxpush < depth)
maxpush = depth;
int opcode = instrs[i].getOpcode(); int opcode = instrs[i].getOpcode();
switch (opcode) { switch (opcode) {
@ -176,7 +186,7 @@ public final class Block {
case Opcodes.opc_lookupswitch: case Opcodes.opc_lookupswitch:
if (succs.length != instrs[i].getValues().length + 1) if (succs.length != instrs[i].getValues().length + 1)
throw new IllegalArgumentException throw new IllegalArgumentException
("no successors for switch"); ("number of successors for switch doesn't match");
if (i != size - 1) if (i != size - 1)
throw new IllegalArgumentException throw new IllegalArgumentException
("switch in the middle!"); ("switch in the middle!");
@ -217,14 +227,14 @@ public final class Block {
needGoto = false; needGoto = false;
} }
} }
push = pop - depth; delta = depth;
if (needGoto && succs.length != 1) if (needGoto && succs.length != 1)
throw new IllegalArgumentException("no single successor block"); throw new IllegalArgumentException("no single successor block");
} }
public void getStackPopPush (int[] poppush) { public void getStackPopPush (int[] poppush) {
poppush[0] = pop; poppush[0] = maxpop;
poppush[1] = push; poppush[1] = delta + maxpop;
return; return;
} }
@ -235,12 +245,7 @@ public final class Block {
public void setCode(Instruction[] instrs, Block[] succs) { public void setCode(Instruction[] instrs, Block[] succs) {
this.instrs = instrs; this.instrs = instrs;
this.succs = succs; this.succs = succs;
try { initCode();
initCode();
} catch (IllegalArgumentException ex) {
dumpCode(net.sf.jode.GlobalOptions.err);
throw ex;
}
} }
public void dumpCode(PrintWriter output) { public void dumpCode(PrintWriter output) {

@ -59,13 +59,13 @@ import java.lang.reflect.Modifier;
* now have two different possibilities to fill it with informations: * now have two different possibilities to fill it with informations:
* You load the class from its classpath (from which it was created) * You load the class from its classpath (from which it was created)
* or you build it from scratch by setting its contents with the * or you build it from scratch by setting its contents with the
* various <code>setXXX</code> methods. * various <code>setSomething</code> methods.
* *
* <h3>Changing a class</h3> * <h3>Changing a class</h3>
* Even if the class is already filled with information you can change * Even if the class is already filled with information you can change
* it. You can, for example, set another array of methods, change the * it. You can, for example, set another array of methods, change the
* modifiers, or rename the class. Use the various * modifiers, or rename the class. Use the various
* <code>setXXX</code> methods. * <code>setSomething</code> methods.
* *
* <h3>The components of a class</h3> * <h3>The components of a class</h3>
* A class consists of several components: * A class consists of several components:
@ -79,7 +79,7 @@ import java.lang.reflect.Modifier;
* <dt>class name</dt><dd> * <dt>class name</dt><dd>
* The short java name of this class, i.e. the name that appears * The short java name of this class, i.e. the name that appears
* behind the "class" keyword in the java source file. While * behind the "class" keyword in the java source file. While
* <code>getClassName()</code> also works for top level classes, * <code>getClassName()</code> also works for package scope classes,
* setClassName() must only be called on inner classes and will not * setClassName() must only be called on inner classes and will not
* change the bytecode name.<br> * change the bytecode name.<br>
* *
@ -121,22 +121,17 @@ import java.lang.reflect.Modifier;
* Fields are represented as {@link MethodInfo} objects. * Fields are represented as {@link MethodInfo} objects.
* </dd> * </dd>
* <dt>method scoped</dt><dd> * <dt>method scoped</dt><dd>
* True if this class is an anonymous or method scoped class. * A boolean value; true if this class is an anonymous or method
* scoped class.
* </dd> * </dd>
* <dt>outer class</dt><dd> * <dt>outer class</dt><dd>
* the class of which this class is the inner. It returns null for * the class of which this class is the inner class. It returns
* top level classes and for method scoped classes. <br> * null for package scoped and method scoped classes. <br>
* </dd> * </dd>
* <dt>classes</dt><dd> * <dt>classes</dt><dd>
* the inner classes which is an array of ClassInfo. This doesn't * the inner classes which is an array of ClassInfo. This doesn't
* include method scoped classes.<br> * include method scoped classes.<br>
* </dd> * </dd>
* <dt>extra classes</dt><dd>
* this field tells only, for which additional classes the class
* files contains an InnerClass Attribute. This can be the method
* scoped classes or the inner inner classes, but there is no
* promise. <br>
* </dd>
* <dt>source file</dt><dd> * <dt>source file</dt><dd>
* The name of source file. The JVM uses this field when a stack * The name of source file. The JVM uses this field when a stack
* trace is produced. * trace is produced.
@ -155,12 +150,12 @@ import java.lang.reflect.Modifier;
* <code>getOuterClass()</code> returns <code>null</code> and * <code>getOuterClass()</code> returns <code>null</code> and
* <code>isMethodScoped()</code> returns <code>false</code>. * <code>isMethodScoped()</code> returns <code>false</code>.
* </dd> * </dd>
* <dt>inner class scoped classes</dt><dd> * <dt>class scoped classes (inner classes)</dt><dd>
* A class is class scoped if, and only if * A class is class scoped if, and only if
* <code>getOuterClass()</code> returns not <code>null</code>. * <code>getOuterClass()</code> returns not <code>null</code>.
* *
* The bytecode name (<code>getName()</code>) of an inner class is * The bytecode name (<code>getName()</code>) of an inner class is
* most times of the form <code>Package.Outer$Inner</code>. But * in most cases of the form <code>Package.Outer$Inner</code>. But
* ClassInfo also supports differently named classes, as long as the * ClassInfo also supports differently named classes, as long as the
* InnerClass attribute is present. The method * InnerClass attribute is present. The method
* <code>getClassName()</code> returns the name of the inner class * <code>getClassName()</code> returns the name of the inner class
@ -177,12 +172,13 @@ import java.lang.reflect.Modifier;
* too.<br><br> * too.<br><br>
* *
* The bytecode name (<code>getName()</code>) of an method scoped class is * The bytecode name (<code>getName()</code>) of an method scoped class is
* most times of the form <code>Package.Outer$Number$Inner</code>. But * in most cases of the form <code>Package.Outer$Number$Inner</code>. But
* ClassInfo also supports differently named classes, as long as the * ClassInfo also supports differently named classes, as long as the
* InnerClass attribute is present. <br><br> * InnerClass attribute is present. <br><br>
* *
* There's no way to get the method scoped classes of a method, except * There's no way to get the method scoped classes of a method, except
* by analyzing its instructions. * by analyzing its instructions. And even that is error prone, since
* it could just be a method scoped class of an outer method.
* </dd> * </dd>
* <dt>anonymous classes</dt><dd> * <dt>anonymous classes</dt><dd>
* A class is an anonymous class if, and only if * A class is an anonymous class if, and only if
@ -191,18 +187,20 @@ import java.lang.reflect.Modifier;
* case <code>getOuterClass()</code> returns <code>null</code>, * case <code>getOuterClass()</code> returns <code>null</code>,
* too.<br><br> * too.<br><br>
* *
* The bytecode name (<code>getName()</code>) of an method scoped class is * The bytecode name (<code>getName()</code>) of an method scoped
* most times of the form <code>Package.Outer$Number</code>. But * class is in most cases of the form
* ClassInfo also supports differently named classes, as long as the * <code>Package.Outer$Number</code>. But ClassInfo also supports
* InnerClass attribute is present. <br><br> * differently named classes, as long as the InnerClass attribute is
* present. <br><br>
* *
* There's no way to get the anonymous classes of a method, except * There's no way to get the anonymous classes of a method, except
* by analyzing its instructions. * by analyzing its instructions. And even that is error prone, since
* it could just be an anonymous class of an outer method.
* </dd> * </dd>
* </dl> * </dl>
* *
* <hr> * <hr>
* <h3>open questions...</h3> * <h3>Open Questions</h3>
* *
* I represent most types as <code>java/lang/String</code> (type * I represent most types as <code>java/lang/String</code> (type
* signatures); this is convenient since java bytecode does the same. * signatures); this is convenient since java bytecode does the same.
@ -211,17 +209,13 @@ import java.lang.reflect.Modifier;
* method to convert to it, but I need a ClassPath for this. Should * method to convert to it, but I need a ClassPath for this. Should
* the method be in ClassInfo (I don't think so), should an instance * the method be in ClassInfo (I don't think so), should an instance
* of TypeSignature have a ClassPath as member variable, or should * of TypeSignature have a ClassPath as member variable, or should
* getClassInfo() take a ClassPath parameter? What about arrays? * getClassInfo() take a ClassPath parameter as it is currently?
* What about arrays, shall we support special ClassInfo's for them,
* as java.lang.Class does? I think the current solution is okay.
* <br> * <br>
* *
* Should load(HIERARCHY) also try to load hierarchy of super class. * @author Jochen Hoenicke
* If yes, should it return an IOException if super class can't be */
* found? Or should <code>getSuperclass</code> throw the IOException
* instead (I don't like that). <br>
* <b>Current Solution:</b> <code>superClassOf</code> and
* <code>implementedBy</code> can throw an IOException, getSuperclass not.
*
* @author Jochen Hoenicke */
public final class ClassInfo extends BinaryInfo implements Comparable { public final class ClassInfo extends BinaryInfo implements Comparable {
private static ClassPath defaultClasspath; private static ClassPath defaultClasspath;
@ -245,6 +239,8 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private String sourceFile; private String sourceFile;
private boolean hasInnerClassesAttr; private boolean hasInnerClassesAttr;
private final static ClassInfo[] EMPTY_INNER = new ClassInfo[0];
/** /**
* This constant can be used as parameter to drop. It specifies * This constant can be used as parameter to drop. It specifies
* that no information at all should be kept for the current class. * that no information at all should be kept for the current class.
@ -419,10 +415,24 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (status >= OUTERCLASS) { if (status >= OUTERCLASS) {
if ((className == null if ((className == null
? this.className != null : !className.equals(this.className)) ? this.className != null : !className.equals(this.className))
|| this.outerClass != outer) || this.outerClass != outer) {
throw new ClassFormatException("Outer information mismatch " /* Ignore errors when merging, some obfuscator may have
+name+": "+className+","+outer+","+ms+"<->"+this.className +","+this.outerClass+","+this.methodScoped); * stripped InnerClasses attributes
mergeModifiers(realModifiers); */
if (this.className == null && this.outerClass == null
&& (className != null || outer != null)) {
this.outerClass = outer;
this.className = className;
this.methodScoped = ms;
} else if (className != null || outer != null) {
GlobalOptions.err.println
("WARNING: Outer information mismatch "
+name+": "+className+","+outer+","+ms+"<->"
+this.className +","+this.outerClass+","+this.methodScoped);
}
}
if (realModifiers != -1)
mergeModifiers(realModifiers);
} else { } else {
if (realModifiers != -1) if (realModifiers != -1)
mergeModifiers(realModifiers); mergeModifiers(realModifiers);
@ -438,37 +448,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throws IOException throws IOException
{ {
/* The InnerClasses attribute is transformed in a special way /* The InnerClasses attribute is transformed in a special way
* so we want to taker a closer look. According to the inner * so we want to taker a closer look. According to the 2nd
* class specification, * edition of the vm specification (InnerClasses attribute),
*
* http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814
*
* there are several InnerClass records in a class. We differ
* three different types:
*
* The InnerClass records for our own inner classes: They can
* easily be recognized, since this class must be mentioned in
* the outer_class_info_index field.
* *
* The InnerClass records for the outer class and its outer * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996
* classes, and so on: According to the spec, these must
* always be present if this is a class scoped class. And they
* must be in reversed order, i.e. the outer most class comes
* first.
* *
* Some other InnerClass records, the extra classes. This is * there is a InnerClass record for each non package scope
* optional, but we don't want to loose this information if we * class referenced in this class. We are only interested in
* just transform classes, so we memorize for which classes we * out own entry and in the entries for our inner classes.
* have to keep the information anyway. * The latter can easily be recognized, since this class must
* * be mentioned in the outer_class_info_index field.
* Currently we don't use all informations, since we don't
* update the information for inner/outer/extra classes or
* check it for consistency. This might be bad, since this
* means that
* <pre>
* load(ALL); write(new File())
* </pre>
* doesn't work.
*/ */
hasInnerClassesAttr = true; hasInnerClassesAttr = true;
@ -478,14 +467,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throw new ClassFormatException throw new ClassFormatException
("InnerClasses attribute has wrong length"); ("InnerClasses attribute has wrong length");
int innerCount = 0, outerCount = 0, extraCount = 0; int innerCount = 0;
/** /**
* The first part will contain the inner classes, the last * The first part will contain the inner classes, the last
* part the extra classes. * part the extra classes.
*/ */
ClassInfo[] innerExtra = new ClassInfo[count]; ClassInfo[] innerCIs = new ClassInfo[count];
for (int i=0; i < count; i++) { for (int i = 0; i < count; i++) {
int innerIndex = input.readUnsignedShort(); int innerIndex = input.readUnsignedShort();
int outerIndex = input.readUnsignedShort(); int outerIndex = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort(); int nameIndex = input.readUnsignedShort();
@ -497,12 +486,17 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (innername != null && innername.length() == 0) if (innername != null && innername.length() == 0)
innername = null; innername = null;
/* Some compilers give method scope classes a valid /* Some compilers give method scope and anonymous classes
* outer field, but we mustn't handle them as inner * a valid outer field, but we mustn't handle them as
* classes. The best way to distinguish this case * inner classes.
* is by the class name.
*/ */
if (outer != null && innername != null if (innername == null)
outer = null;
/* The best way to distinguish method scope classes is by thier
* class name.
*/
if (outer != null
&& inner.length() > outer.length() + 2 + innername.length() && inner.length() > outer.length() + 2 + innername.length()
&& inner.startsWith(outer+"$") && inner.startsWith(outer+"$")
&& inner.endsWith("$"+innername) && inner.endsWith("$"+innername)
@ -510,45 +504,13 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
outer = null; outer = null;
ClassInfo innerCI = classpath.getClassInfo(inner); ClassInfo innerCI = classpath.getClassInfo(inner);
ClassInfo outerCI = null; ClassInfo outerCI = outer != null
if (outer != null) { ? classpath.getClassInfo(outer) : null;
outerCI = classpath.getClassInfo(outer);
/* If we didn't find an InnerClasses info for outerCI, yet,
* this means that it doesn't have an outer class. So we
* know its (empty) outer class status now.
*/
if (outerCI.status < OUTERCLASS)
outerCI.mergeOuterInfo(null, null, -1, false);
}
if (innername == null)
/* anonymous class */
outerCI = null;
innerCI.mergeOuterInfo(innername, outerCI, innerCI.mergeOuterInfo(innername, outerCI,
access, outerCI == null); access, outerCI == null);
if (outerCI == this) if (outerCI == this)
innerExtra[innerCount++] = innerCI; innerCIs[innerCount++] = innerCI;
else {
/* Remove outerCI from the extra part of innerExtra
* since it is implicit now.
*/
for (int j = count - 1; j >= count - extraCount; j--) {
if (innerExtra[j] == outerCI) {
System.arraycopy(innerExtra, count - extraCount,
innerExtra, count - extraCount + 1,
j - (count - extraCount));
extraCount--;
break;
}
}
/* Add innerCI to the extra classes, except if it is
* this class.
*/
if (innerCI != this)
innerExtra[count - (++extraCount)] = innerCI;
}
} }
/* Now inner classes are at the front of the array in correct /* Now inner classes are at the front of the array in correct
@ -557,32 +519,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/ */
if (innerCount > 0) { if (innerCount > 0) {
innerClasses = new ClassInfo[innerCount]; innerClasses = new ClassInfo[innerCount];
for (int i=0; i < innerCount; i++) System.arraycopy(innerCIs, 0, innerClasses, 0, innerCount);
innerClasses[i] = innerExtra[i];
} else } else
innerClasses = null; innerClasses = EMPTY_INNER;
/* All remaining classes that are mentioned in the constant
* pool must have an empty outer class info. This is
* specified in the 2nd edition of the JVM specification.
*/
for (int i = 1; i < cp.size(); i++) {
if (cp.tags[i] == cp.CLASS) {
String clName = cp.getUTF8(cp.indices1[i]);
if (clName.charAt(0) != '[') {
ClassInfo ci = classpath.getClassInfo
(clName.replace('/','.'));
if (ci.status < OUTERCLASS)
ci.mergeOuterInfo(null, null, -1, false);
}
}
}
} }
void readAttribute(String name, int length, protected void readAttribute(String name, int length,
ConstantPool cp, ConstantPool cp,
DataInputStream input, DataInputStream input,
int howMuch) throws IOException { int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) { if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) {
if (length != 2) if (length != 2)
throw new ClassFormatException("SourceFile attribute" throw new ClassFormatException("SourceFile attribute"
@ -642,7 +587,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
(clazz.getSuperclass().getName()); (clazz.getSuperclass().getName());
Class[] ifaces = clazz.getInterfaces(); Class[] ifaces = clazz.getInterfaces();
interfaces = new ClassInfo[ifaces.length]; interfaces = new ClassInfo[ifaces.length];
for (int i=0; i<ifaces.length; i++) for (int i = 0; i<ifaces.length; i++)
interfaces[i] = classpath.getClassInfo(ifaces[i].getName()); interfaces[i] = classpath.getClassInfo(ifaces[i].getName());
} }
if (howMuch >= PUBLICDECLARATIONS) { if (howMuch >= PUBLICDECLARATIONS) {
@ -707,7 +652,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
innerClasses[i].loadFromReflection(is[i], OUTERCLASS); innerClasses[i].loadFromReflection(is[i], OUTERCLASS);
} }
} else } else
innerClasses = null; innerClasses = EMPTY_INNER;
} }
status = howMuch; status = howMuch;
} }
@ -761,11 +706,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
String className = cpool.getClassName(input.readUnsignedShort()); String className = cpool.getClassName(input.readUnsignedShort());
if (!name.equals(className)) if (!name.equals(className))
throw new ClassFormatException("wrong name " + className); throw new ClassFormatException("wrong name " + className);
String superName = cpool.getClassName(input.readUnsignedShort()); int superID = input.readUnsignedShort();
superclass = superName != null ? classpath.getClassInfo(superName) : null; superclass = superID == 0 ? null
: classpath.getClassInfo(cpool.getClassName(superID));
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
interfaces = new ClassInfo[count]; interfaces = new ClassInfo[count];
for (int i=0; i< count; i++) { for (int i = 0; i < count; i++) {
interfaces[i] = classpath.getClassInfo interfaces[i] = classpath.getClassInfo
(cpool.getClassName(input.readUnsignedShort())); (cpool.getClassName(input.readUnsignedShort()));
} }
@ -775,14 +721,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) { if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
fields = new FieldInfo[count]; fields = new FieldInfo[count];
for (int i=0; i< count; i++) { for (int i = 0; i < count; i++) {
fields[i] = new FieldInfo(); fields[i] = new FieldInfo();
fields[i].read(cpool, input, howMuch); fields[i].read(cpool, input, howMuch);
} }
} else { } else {
byte[] skipBuf = new byte[6]; byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
for (int i=0; i< count; i++) { for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type input.readFully(skipBuf); // modifier, name, type
skipAttributes(input); skipAttributes(input);
} }
@ -792,29 +738,45 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) { if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
methods = new MethodInfo[count]; methods = new MethodInfo[count];
for (int i=0; i< count; i++) { for (int i = 0; i < count; i++) {
methods[i] = new MethodInfo(); methods[i] = new MethodInfo();
methods[i].read(cpool, input, howMuch); methods[i].read(cpool, input, howMuch);
} }
} else { } else {
byte[] skipBuf = new byte[6]; byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
for (int i=0; i< count; i++) { for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type input.readFully(skipBuf); // modifier, name, type
skipAttributes(input); skipAttributes(input);
} }
} }
/* initialize inner classes to empty array, in case there
* is no InnerClasses attribute.
*/
innerClasses = EMPTY_INNER;
/* attributes */ /* attributes */
readAttributes(cpool, input, howMuch); readAttributes(cpool, input, howMuch);
/* All classes that are mentioned in the constant pool must
* have an empty outer class info. This is specified in the
* 2nd edition of the JVM specification.
*/
Iterator iter = cpool.iterateClassNames();
while (iter.hasNext()) {
ClassInfo ci = classpath.getClassInfo((String) iter.next());
if (ci.status < OUTERCLASS)
ci.mergeOuterInfo(null, null, -1, false);
}
status = howMuch; status = howMuch;
} }
private void reserveSmallConstants(GrowableConstantPool gcp) { private void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i=0; i < fields.length; i++) for (int i = 0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp); fields[i].reserveSmallConstants(gcp);
for (int i=0; i < methods.length; i++) for (int i = 0; i < methods.length; i++)
methods[i].reserveSmallConstants(gcp); methods[i].reserveSmallConstants(gcp);
} }
@ -823,16 +785,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private void prepareWriting(GrowableConstantPool gcp) { private void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name); gcp.putClassName(name);
gcp.putClassName(superclass.name); gcp.putClassName(superclass.name);
for (int i=0; i < interfaces.length; i++) for (int i = 0; i < interfaces.length; i++)
gcp.putClassName(interfaces[i].name); gcp.putClassName(interfaces[i].name);
for (int i=0; i < fields.length; i++) for (int i = 0; i < fields.length; i++)
fields[i].prepareWriting(gcp); fields[i].prepareWriting(gcp);
for (int i=0; i < methods.length; i++) for (int i = 0; i < methods.length; i++)
methods[i].prepareWriting(gcp); methods[i].prepareWriting(gcp);
for (int i=0; i < innerClasses.length; i++) for (int i = 0; i < innerClasses.length; i++)
gcp.putClassName(innerClasses[i].name); gcp.putClassName(innerClasses[i].name);
if (sourceFile != null) { if (sourceFile != null) {
@ -845,40 +807,29 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* edition of the JVM specification. * edition of the JVM specification.
*/ */
hasInnerClassesAttr = false; hasInnerClassesAttr = false;
for (int i = 1; i < gcp.size(); i++) { Iterator iter = gcp.iterateClassNames();
if (gcp.tags[i] == gcp.CLASS) { while (iter.hasNext()) {
String clName; ClassInfo ci = classpath.getClassInfo((String) iter.next());
try { if (ci.status < OUTERCLASS) {
clName = gcp.getUTF8(gcp.indices1[i]); GlobalOptions.err.println
} catch (ClassFormatException ex) { ("WARNING: " + ci.name + "'s outer class isn't known.");
throw new InternalError(ex.getMessage()); } else {
} if ((ci.outerClass != null || ci.methodScoped)
if (clName.charAt(0) != '[') { && ! hasInnerClassesAttr) {
ClassInfo ci = classpath.getClassInfo gcp.putUTF8("InnerClasses");
(clName.replace('/','.')); hasInnerClassesAttr = true;
if (ci.status < OUTERCLASS) {
GlobalOptions.err.println
("WARNING: "+ ci.name
+ "'s outer class isn't known.");
} else {
if ((ci.outerClass != null || ci.methodScoped)
&& ! hasInnerClassesAttr) {
gcp.putUTF8("innerClasses");
hasInnerClassesAttr = true;
}
if (ci.outerClass != null)
gcp.putClassName(ci.outerClass.name);
if (ci.className != null)
gcp.putUTF8(ci.className);
}
} }
if (ci.outerClass != null)
gcp.putClassName(ci.outerClass.name);
if (ci.className != null)
gcp.putUTF8(ci.className);
} }
} }
prepareAttributes(gcp); prepareAttributes(gcp);
} }
int getKnownAttributeCount() { protected int getAttributeCount() {
int count = 0; int count = super.getAttributeCount();
if (sourceFile != null) if (sourceFile != null)
count++; count++;
if (hasInnerClassesAttr) if (hasInnerClassesAttr)
@ -886,9 +837,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return count; return count;
} }
void writeKnownAttributes(GrowableConstantPool gcp, protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output) DataOutputStream output)
throws IOException { throws IOException {
super.writeAttributes(gcp, output);
if (sourceFile != null) { if (sourceFile != null) {
output.writeShort(gcp.putUTF8("SourceFile")); output.writeShort(gcp.putUTF8("SourceFile"));
output.writeInt(2); output.writeInt(2);
@ -896,28 +848,19 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
} }
List outers = new ArrayList(); List outers = new ArrayList();
for (int i = 0; i < gcp.size(); i++) { Iterator iter = gcp.iterateClassNames();
if (gcp.tags[i] == gcp.CLASS) { while (iter.hasNext()) {
String clName; ClassInfo ci = classpath.getClassInfo((String) iter.next());
try { while (ci != null
clName = gcp.getUTF8(gcp.indices1[i]); && ci.status >= OUTERCLASS
} catch (ClassFormatException ex) { && (ci.outerClass != null || ci.methodScoped)) {
throw new InternalError(ex.getMessage()); /* Order is important so remove ci if it
} * already exists and add it to the end. This
if (clName.charAt(0) != '[') { * way the outermost classes go to the end.
ClassInfo ci = classpath.getClassInfo */
(clName.replace('/','.')); outers.remove(ci);
while (ci.status >= OUTERCLASS outers.add(ci);
&& ci.outerClass != null || ci.methodScoped) { ci = ci.outerClass;
/* Order is important so remove ci if it
* already exists and add it to the end. This
* way the outermost classes go to the end.
*/
outers.remove(ci);
outers.add(ci);
ci = ci.outerClass;
}
}
} }
} }
if (hasInnerClassesAttr) { if (hasInnerClassesAttr) {
@ -926,9 +869,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
output.writeInt(2 + count * 8); output.writeInt(2 + count * 8);
output.writeShort(count); output.writeShort(count);
ListIterator iter = outers.listIterator(count); ListIterator listiter = outers.listIterator(count);
while (iter.hasPrevious()) { while (listiter.hasPrevious()) {
ClassInfo ci = (ClassInfo) iter.previous(); ClassInfo ci = (ClassInfo) listiter.previous();
output.writeShort(gcp.putClassName(ci.name)); output.writeShort(gcp.putClassName(ci.name));
output.writeShort(ci.outerClass == null ? 0 : output.writeShort(ci.outerClass == null ? 0 :
@ -951,7 +894,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* @exception IllegalStateException if not enough information is set. * @exception IllegalStateException if not enough information is set.
*/ */
public void write(DataOutputStream out) throws IOException { public void write(DataOutputStream out) throws IOException {
if (status <= ALL) if (status < ALL)
throw new IllegalStateException("state is "+status); throw new IllegalStateException("state is "+status);
GrowableConstantPool gcp = new GrowableConstantPool(); GrowableConstantPool gcp = new GrowableConstantPool();
@ -967,15 +910,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
out.writeShort(gcp.putClassName(name)); out.writeShort(gcp.putClassName(name));
out.writeShort(gcp.putClassName(superclass.getName())); out.writeShort(gcp.putClassName(superclass.getName()));
out.writeShort(interfaces.length); out.writeShort(interfaces.length);
for (int i=0; i < interfaces.length; i++) for (int i = 0; i < interfaces.length; i++)
out.writeShort(gcp.putClassName(interfaces[i].getName())); out.writeShort(gcp.putClassName(interfaces[i].getName()));
out.writeShort(fields.length); out.writeShort(fields.length);
for (int i=0; i < fields.length; i++) for (int i = 0; i < fields.length; i++)
fields[i].write(gcp, out); fields[i].write(gcp, out);
out.writeShort(methods.length); out.writeShort(methods.length);
for (int i=0; i < methods.length; i++) for (int i = 0; i < methods.length; i++)
methods[i].write(gcp, out); methods[i].write(gcp, out);
writeAttributes(gcp, out); writeAttributes(gcp, out);
@ -1018,11 +961,6 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* Guess the contents of a class. It * Guess the contents of a class. It
* @param howMuch The amount of information that should be read, e.g. * @param howMuch The amount of information that should be read, e.g.
* <code>HIERARCHY</code>. * <code>HIERARCHY</code>.
* @exception ClassFormatException if the file doesn't denote a
* valid class.
* @exception FileNotFoundException if class wasn't found in classpath.
* @exception IOException if an io exception occured.
* @exception IllegalStateException if this ClassInfo was modified.
* @see #OUTERCLASS * @see #OUTERCLASS
* @see #HIERARCHY * @see #HIERARCHY
* @see #PUBLICDECLARATIONS * @see #PUBLICDECLARATIONS
@ -1074,7 +1012,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) { if (howMuch >= PUBLICDECLARATIONS) {
methods = new MethodInfo[0]; methods = new MethodInfo[0];
fields = new FieldInfo[0]; fields = new FieldInfo[0];
innerClasses = new ClassInfo[0]; innerClasses = EMPTY_INNER;
} }
status = howMuch; status = howMuch;
} }
@ -1117,9 +1055,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/ */
keep = DECLARATIONS; keep = DECLARATIONS;
for (int i=0; i < fields.length; i++) for (int i = 0; i < fields.length; i++)
fields[i].drop(keep); fields[i].drop(keep);
for (int i=0; i < methods.length; i++) for (int i = 0; i < methods.length; i++)
methods[i].drop(keep); methods[i].drop(keep);
} }
@ -1201,7 +1139,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public FieldInfo findField(String name, String typeSig) { public FieldInfo findField(String name, String typeSig) {
if (status < PUBLICDECLARATIONS) if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status); throw new IllegalStateException("status is "+status);
for (int i=0; i< fields.length; i++) for (int i = 0; i < fields.length; i++)
if (fields[i].getName().equals(name) if (fields[i].getName().equals(name)
&& fields[i].getType().equals(typeSig)) && fields[i].getType().equals(typeSig))
return fields[i]; return fields[i];
@ -1211,7 +1149,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public MethodInfo findMethod(String name, String typeSig) { public MethodInfo findMethod(String name, String typeSig) {
if (status < PUBLICDECLARATIONS) if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status); throw new IllegalStateException("status is "+status);
for (int i=0; i< methods.length; i++) for (int i = 0; i < methods.length; i++)
if (methods[i].getName().equals(name) if (methods[i].getName().equals(name)
&& methods[i].getType().equals(typeSig)) && methods[i].getType().equals(typeSig))
return methods[i]; return methods[i];
@ -1260,6 +1198,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return methodScoped; return methodScoped;
} }
/**
* Returns the inner classes declared in this class.
* This needs at least PUBLICDECLARATION information loaded.
* @return an array containing the inner classes, guaranteed != null.
* @exception IllegalStateException if PUBLICDECLARATIONS information
* wasn't loaded yet. */
public ClassInfo[] getClasses() { public ClassInfo[] getClasses() {
if (status < PUBLICDECLARATIONS) if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status); throw new IllegalStateException("status is "+status);
@ -1270,53 +1214,73 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return sourceFile; return sourceFile;
} }
/**
* Sets the name of this class info. Note that by changing the
* name you may overwrite an already loaded class. This can have
* ugly effects, as that overwritten class may still exist, but
* can't be loaded via the classPath.
*/
public void setName(String newName) { public void setName(String newName) {
/* The class name is used as index in the hash table. We have
* to update the class path and tell it about the name change.
*/
classpath.renameClassInfo(this, newName);
name = newName.intern(); name = newName.intern();
status = ALL;
modified = true; modified = true;
} }
public void setSuperclass(ClassInfo newSuper) { public void setSuperclass(ClassInfo newSuper) {
superclass = newSuper; superclass = newSuper;
status = ALL;
modified = true; modified = true;
} }
public void setInterfaces(ClassInfo[] newIfaces) { public void setInterfaces(ClassInfo[] newIfaces) {
interfaces = newIfaces; interfaces = newIfaces;
status = ALL;
modified = true; modified = true;
} }
public void setModifiers(int newModifiers) { public void setModifiers(int newModifiers) {
modifiers = newModifiers; modifiers = newModifiers;
status = ALL;
modified = true; modified = true;
} }
public void setMethods(MethodInfo[] mi) { public void setMethods(MethodInfo[] mi) {
methods = mi; methods = mi;
status = ALL;
modified = true; modified = true;
} }
public void setFields(FieldInfo[] fi) { public void setFields(FieldInfo[] fi) {
fields = fi; fields = fi;
status = ALL;
modified = true; modified = true;
} }
public void setOuterClass(ClassInfo oc) { public void setOuterClass(ClassInfo oc) {
outerClass = oc; outerClass = oc;
status = ALL;
modified = true; modified = true;
} }
public void setMethodScoped(boolean ms) { public void setMethodScoped(boolean ms) {
methodScoped = ms; methodScoped = ms;
status = ALL;
modified = true; modified = true;
} }
public void setClasses(ClassInfo[] ic) { public void setClasses(ClassInfo[] ic) {
innerClasses = ic; innerClasses = ic.length == 0 ? EMPTY_INNER : ic;
status = ALL;
modified = true; modified = true;
} }
public void setSourceFile(String newSource) { public void setSourceFile(String newSource) {
sourceFile = newSource; sourceFile = newSource;
status = ALL;
modified = true; modified = true;
} }
@ -1363,12 +1327,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone(); ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone();
Arrays.sort(interfaces); Arrays.sort(interfaces);
for (int i=0; i < interfaces.length; i++) for (int i = 0; i < interfaces.length; i++)
out.writeUTF(interfaces[i].name); out.writeUTF(interfaces[i].name);
FieldInfo[] fields = (FieldInfo[]) this.fields.clone(); FieldInfo[] fields = (FieldInfo[]) this.fields.clone();
Arrays.sort(fields); Arrays.sort(fields);
for (int i=0; i < fields.length; i++) { for (int i = 0; i < fields.length; i++) {
modifs = fields[i].getModifiers(); modifs = fields[i].getModifiers();
if ((modifs & Modifier.PRIVATE) != 0 if ((modifs & Modifier.PRIVATE) != 0
&& (modifs & (Modifier.STATIC && (modifs & (Modifier.STATIC
@ -1383,7 +1347,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
MethodInfo[] methods = (MethodInfo[]) this.methods.clone(); MethodInfo[] methods = (MethodInfo[]) this.methods.clone();
Arrays.sort(methods); Arrays.sort(methods);
for (int i=0; i < methods.length; i++) { for (int i = 0; i < methods.length; i++) {
modifs = methods[i].getModifiers(); modifs = methods[i].getModifiers();
/* The modifiers of <clinit> should be just static, /* The modifiers of <clinit> should be just static,
* but jikes also marks it final. * but jikes also marks it final.
@ -1405,7 +1369,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
byte[] sha = md.digest(); byte[] sha = md.digest();
long result = 0; long result = 0;
for (int i=0; i < 8; i++) { for (int i = 0; i < 8; i++) {
result += (long)(sha[i] & 0xFF) << (8 * i); result += (long)(sha[i] & 0xFF) << (8 * i);
} }
return result; return result;
@ -1461,7 +1425,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (clazz.status < HIERARCHY) if (clazz.status < HIERARCHY)
clazz.load(HIERARCHY); clazz.load(HIERARCHY);
ClassInfo[] ifaces = clazz.getInterfaces(); ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i< ifaces.length; i++) { for (int i = 0; i < ifaces.length; i++) {
if (implementedBy(ifaces[i])) if (implementedBy(ifaces[i]))
return true; return true;
} }

@ -684,6 +684,26 @@ public class ClassPath {
return clazz; return clazz;
} }
/**
* Updates the classes unify hash for a class renaming. This
* should be only called by {@link ClassInfo#setName}.
*/
void renameClassInfo(ClassInfo classInfo, String classname) {
classes.remove(classInfo.getName().hashCode(), classInfo);
/* Now remove any class already loaded with that name, just
* in case we're overwriting one.
*/
Iterator iter = classes.iterateHashCode(classname.hashCode());
while (iter.hasNext()) {
ClassInfo clazz = (ClassInfo) iter.next();
if (clazz.getName().equals(classname)) {
iter.remove();
break;
}
}
classes.put(classname.hashCode(), classInfo);
}
/** /**
* Checks, if a class with the given name exists somewhere in this * Checks, if a class with the given name exists somewhere in this
* path. * path.
@ -752,7 +772,7 @@ public class ClassPath {
* @param fqn the full qualified name. The components should be dot * @param fqn the full qualified name. The components should be dot
* separated. * separated.
* @return true, if filename exists and is a package, false otherwise. * @return true, if filename exists and is a package, false otherwise.
* @see isDirectory * @see #isDirectory
*/ */
public boolean isPackage(String fqn) { public boolean isPackage(String fqn) {
return isDirectory(fqn.replace('.', '/')); return isDirectory(fqn.replace('.', '/'));

@ -21,12 +21,22 @@ package net.sf.jode.bytecode;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.NoSuchElementException;
///#def COLLECTIONS java.util
import java.util.Iterator;
///#enddef
///#def COLLECTIONEXTRA java.lang
import java.lang.UnsupportedOperationException;
///#enddef
/** /**
* This class represent the constant pool. * This class represent the constant pool. You normally don't need to
* access this class, except if you want to add your own custom
* attributes that use the constant pool.
* *
* @author Jochen Hoenicke * @author Jochen Hoenicke
*/ */
class ConstantPool { public class ConstantPool {
public final static int CLASS = 7; public final static int CLASS = 7;
public final static int FIELDREF = 9; public final static int FIELDREF = 9;
public final static int METHODREF = 10; public final static int METHODREF = 10;
@ -106,16 +116,12 @@ class ConstantPool {
} }
public String getUTF8(int i) throws ClassFormatException { public String getUTF8(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != UTF8) if (tags[i] != UTF8)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
return (String)constants[i]; return (String)constants[i];
} }
public Reference getRef(int i) throws ClassFormatException { public Reference getRef(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != FIELDREF if (tags[i] != FIELDREF
&& tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF) && tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
@ -156,8 +162,6 @@ class ConstantPool {
} }
public String getClassType(int i) throws ClassFormatException { public String getClassType(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS) if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]); String clName = getUTF8(indices1[i]);
@ -169,13 +173,10 @@ class ConstantPool {
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
throw new ClassFormatException(ex.getMessage()); throw new ClassFormatException(ex.getMessage());
} }
return clName; return clName;
} }
public String getClassName(int i) throws ClassFormatException { public String getClassName(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS) if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch"); throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]); String clName = getUTF8(indices1[i]);
@ -187,6 +188,43 @@ class ConstantPool {
return clName.replace('/','.').intern(); return clName.replace('/','.').intern();
} }
/**
* Iterates through all class entries in the class pool and returns
* their (dot seperated) class name.
*/
public Iterator iterateClassNames() {
return new Iterator()
{
int entry = 1;
public boolean hasNext() {
try {
while (entry < count
&& (tags[entry] != CLASS
|| getUTF8(indices1[entry])
.charAt(0) == '['))
entry++;
} catch (ClassFormatException ex) {
throw new InternalError(ex.getMessage());
}
return entry < count;
}
public Object next() {
if (!hasNext())
throw new NoSuchElementException();
try {
return getClassName(entry++);
} catch (ClassFormatException ex) {
throw new InternalError(ex.getMessage());
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public String toString(int i) { public String toString(int i) {
switch (tags[i]) { switch (tags[i]) {
case CLASS: case CLASS:
@ -225,7 +263,7 @@ class ConstantPool {
} }
public String toString() { public String toString() {
StringBuffer result = new StringBuffer("[ null"); StringBuffer result = new StringBuffer("ConstantPool[ null");
for (int i=1; i< count; i++) { for (int i=1; i< count; i++) {
result.append(", ").append(i).append(" = ").append(toString(i)); result.append(", ").append(i).append(" = ").append(toString(i));
} }

@ -44,10 +44,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
this.modifier = modifier; this.modifier = modifier;
} }
void readAttribute(String name, int length, protected void readAttribute(String name, int length,
ConstantPool cp, ConstantPool cp,
DataInputStream input, DataInputStream input,
int howMuch) throws IOException { int howMuch) throws IOException {
if (howMuch >= ClassInfo.DECLARATIONS if (howMuch >= ClassInfo.DECLARATIONS
&& name.equals("ConstantValue")) { && name.equals("ConstantValue")) {
if (length != 2) if (length != 2)
@ -97,8 +97,8 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp); prepareAttributes(gcp);
} }
protected int getKnownAttributeCount() { protected int getAttributeCount() {
int count = 0; int count = super.getAttributeCount();
if (constant != null) if (constant != null)
count++; count++;
if (syntheticFlag) if (syntheticFlag)
@ -108,9 +108,10 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
return count; return count;
} }
void writeKnownAttributes(GrowableConstantPool gcp, protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output) DataOutputStream output)
throws IOException { throws IOException {
super.writeAttributes(gcp, output);
if (constant != null) { if (constant != null) {
output.writeShort(gcp.putUTF8("ConstantValue")); output.writeShort(gcp.putUTF8("ConstantValue"));
output.writeInt(2); output.writeInt(2);
@ -140,7 +141,7 @@ public final class FieldInfo extends BinaryInfo implements Comparable {
writeAttributes(constantPool, output); writeAttributes(constantPool, output);
} }
void drop(int keep) { protected void drop(int keep) {
if (keep < ClassInfo.DECLARATIONS) if (keep < ClassInfo.DECLARATIONS)
constant = null; constant = null;
super.drop(keep); super.drop(keep);

@ -66,7 +66,7 @@ public class Handler {
} }
/** /**
* Gets the type signature of the exception. * Gets the class name of the exception type.
*/ */
public String getType() { public String getType() {
return type; return type;
@ -85,7 +85,7 @@ public class Handler {
} }
/** /**
* Sets the type signature of the exception. * Sets the class name of the exception type.
*/ */
public void setType(String type) { public void setType(String type) {
this.type = type; this.type = type;

@ -26,6 +26,38 @@ import java.lang.reflect.Modifier;
import java.lang.Comparable; import java.lang.Comparable;
///#enddef ///#enddef
/**
* <p>Represents a java bytecode method. A method consists of the following
* parts:</p>
*
* <dl>
*
* <dt>name</dt><dd>The method's name</dd>
*
* <dt>type</dt><dd>The method's {@link TypeSignature type signature}
* in bytecode format.</dd>
*
* <dt>modifiers</dt><dd>The modifiers of the method like private, public etc.
* These are created by or-ing the constants defined in
* {@link java.lang.reflect.Modifier}. </dt>
*
* <dt>basicblocks</dt><dd>the bytecode of the method in form of
* {@link BasicBlocks basic blocks}, null if it is native or
* abstract.</dd>
*
* <dt>synthetic</dt><dd>true if this method is synthetic</dd>
*
* <dt>deprecated</dt><dd>true if this method is deprecated</dd>
*
* <dt>exceptions</dt> <dd>the exceptions that the method declared in
* its throws clause</dd>
*
* </dl>
*
* @author Jochen Hoenicke
* @see net.sf.jode.bytecode.TypeSignature
* @see net.sf.jode.bytecode.BasicBlocks
*/
public final class MethodInfo extends BinaryInfo implements Comparable { public final class MethodInfo extends BinaryInfo implements Comparable {
int modifier; int modifier;
String name; String name;
@ -45,8 +77,9 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
this.modifier = modifier; this.modifier = modifier;
} }
void readAttribute(String name, int length, ConstantPool cp, protected void readAttribute
DataInputStream input, int howMuch) throws IOException { (String name, int length, ConstantPool cp,
DataInputStream input, int howMuch) throws IOException {
if (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) { if (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) {
basicblocks = new BasicBlocks(this); basicblocks = new BasicBlocks(this);
basicblocks.read(cp, input, howMuch); basicblocks.read(cp, input, howMuch);
@ -105,8 +138,8 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp); prepareAttributes(gcp);
} }
int getKnownAttributeCount() { protected int getAttributeCount() {
int count = 0; int count = super.getAttributeCount();
if (basicblocks != null) if (basicblocks != null)
count++; count++;
if (exceptions != null) if (exceptions != null)
@ -118,9 +151,10 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
return count; return count;
} }
void writeKnownAttributes(GrowableConstantPool gcp, protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output) DataOutputStream output)
throws IOException { throws IOException {
super.writeAttributes(gcp, output);
if (basicblocks != null) { if (basicblocks != null) {
output.writeShort(gcp.putUTF8("Code")); output.writeShort(gcp.putUTF8("Code"));
basicblocks.write(gcp, output); basicblocks.write(gcp, output);
@ -144,14 +178,14 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
} }
void write(GrowableConstantPool constantPool, void write(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException { DataOutputStream output) throws IOException {
output.writeShort(modifier); output.writeShort(modifier);
output.writeShort(constantPool.putUTF8(name)); output.writeShort(constantPool.putUTF8(name));
output.writeShort(constantPool.putUTF8(typeSig)); output.writeShort(constantPool.putUTF8(typeSig));
writeAttributes(constantPool, output); writeAttributes(constantPool, output);
} }
void drop(int keep) { protected void drop(int keep) {
if (keep < ClassInfo.DECLARATIONS) if (keep < ClassInfo.DECLARATIONS)
exceptions = null; exceptions = null;
if (keep < ClassInfo.NODEBUG) if (keep < ClassInfo.NODEBUG)
@ -260,8 +294,12 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
return result; return result;
} }
/**
* Returns a string representation of this method. It consists
* of the method's name and type signature.
* @return a string representation of this method.
*/
public String toString() { public String toString() {
return "Method "+Modifier.toString(modifier)+" "+ return "MethodInfo[name="+name+",type="+typeSig+"]";
typeSig + " " + name;
} }
} }

@ -1,73 +0,0 @@
/* Subroutine Copyright (C) 2000 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.bytecode;
import java.io.PrintWriter;
///#def COLLECTIONS java.util
import java.util.Collection;
import java.util.Arrays;
import java.util.List;
import java.util.Iterator;
///#enddef
/**
* <p>Represents a <code>jsr</code>-Subroutine.</p>
*
* <p>In my representation a subroutine consists of all blocks from
* which the ret instruction is reachable. Every subroutine must have
* a reachable ret instruction, or the jsr is replaced by a simple goto.
* </p>
*
* @author Jochen Hoenicke
* @see net.sf.jode.bytecode.BasicBlocks
* @see net.sf.jode.bytecode.Block
*/
public final class Subroutine {
/**
* Subroutines may be nested. This points to the outer subroutine
* or to null if this doesn't have an outer.
*/
private Subroutine outer;
/**
* Each subroutine has exactly one ret instruction, which is the
* last instruction in the retBlock. The local of the ret
* instruction must equal the local where the first instruction of
* the subroutine stores to.
*/
private Block retBlock;
/**
* The set of locals that are accessed inside this subroutine.
*/
private BitSet accessedLocals;
public Block getRetBlock() {
return retBlock;
}
public Subroutine getOuter() {
return outer;
}
public boolean isAccessed(int slot) {
return accessedLocals.get(slot);
}
}

@ -1,228 +0,0 @@
/* TransformSubroutine Copyright (C) 1999-2000 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.bytecode;
import java.util.BitSet;
/**
* <p>This class contains some code to transform the subroutines of
* a method into a normal form. The normal form is as following.</p>
*
* <p>Each subroutine block, meaning a block where some jsr
* instructions may jump to, must store the return address in a local
* variable immediately. There must be exactly one block with the
* corresponding <code>opc_ret</code> instruction and this block must
* be reachable from all blocks that belong to this subroutine. </b>
*
* <p>The JVM spec allows a subroutine, to leave the return address on
* stack for a while, even longer than the subroutine actually exists.
* One can also pop that value instead of storing it in a local
* variable. And finally its possible to store it in a variable, but
* there exists no reachable <code>opc_ret</code> instruction that returns
* to that address.</p>
*
* <p>If the return address is not used by the subroutine, we simply
* replace the jsr by a jump and remove the pop/store instruction that
* popped the return address.</p>
*
* <p>If the return address is used, but not immediately stored, we simply
* move the corresponding astore to the start of the subroutine.</p>
*
* @see net.sf.jode.bytecode.Block
* @see net.sf.jode.bytecode.Instruction
*/
public class TransformSubroutine implements Opcodes {
private final static int SUBSTATUS_SUBROUTINE = 1;
private final static int SUBSTATUS_REMOVEDSUB = 2;
private final static int SUBSTATUS_NOTSUB = 3;
private final static int SUBSTATUS_INPROGRESS = 4;
BasicBlocks bb;
Blocks[] blocks;
byte[] substatus;
Subroutine[] subInfo;
// {
// for (int i=0; i < blocks.length; i++) {
// Instructions[] instrs = blocks[i].getInstructions();
// if (instrs[instrs.length-1].getOpcode() == opc_jsr) {
// int srBlock = instrs.getSuccs()[0].getBlockNr();
// if (substatus[srBlock] == 0)
// analyzeSubroutine(srBlock);
// if (substatus[srBlock] == SUBSTATUS_REMOVED) {
// Instructions[] newInstrs
// = new Instruction[instrs.length-1];
// System.arraycopy(instrs, 0, newInstrs, 0,
// newInstrs.length);
// Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; };
// blocks[i].setCode(newInstrs, newSuccs);
// }
// }
// }
// }
class SubroutineInfo {
int retPosition;
BitSet accessedLocals;
SubroutineInfo outer;
SubroutineInfo(int retPos, Bitset accLocals, SubroutineInfo outer) {
this.outer = outer;
this.retPosition = retPos;
this.accessedLocals = accLocals;
}
boolean merge(int retPos, BitSet accLocals, SubroutineInfo outer) {
if ((retPos < 0 || this.retPosition < 0)
&& retPos != this.retPosition)
throw new
}
}
public TransformSubroutine(BasicBlocks bb)
throws ClassFormatException
{
if (bb.getStartBlock() == null)
return;
blocks = bb.getBlocks();
substatus = new byte[blocks.length];
analyzeBlock(blockNr, SUBSTATUS_NOTSUB, null);
}
public void analyzeBlock(int blockNr, int status, SubroutineInfo outer,
BitSet retsOnStack) {
Block block = blocks[blockNr];
if (status == SUBSTATUS_INPROGRESS) {
}
}
public void analyzeBlock(int blockNr, int status, SubroutineInfo outer) {
substatus[blockNr] = status;
accessedLocals[blockNr] = accessed;
Stack todo = new Stack();
todo.add(new BlockInfo(startBlockNr, 0, null));
while (!todo.isEmpty()) {
BlockInfo info = todo.pop();
Block block = blocks[info.blockNr];
Instruction[] instrs = block.getInstructions();
Instruction[] newInstrs = null;
Block[] succs = block.getSuccessors();
if (substatus[info.blockNr]
== SUBSTATUS_INPROGRESS) {
int retPosition = info.retPosition;
BitSet =
retPosition < 0 ? info.accessedLocals.clone() : null;
for (int i = 0; i < instrs.length; i++) {
Instruction instr = instrs[i];
if (instr instanceof SlotInstruction) {
if (instr.getOpcode() == opc_astore
&& retPosition == -1) {
/* We found the store operation. At least
* a candidate, since there may not be a
* corresponding ret.
*/
retPosition = instr.getLocalSlot();
accessedLocals = null;
/* remove store Instruction.
*/
newInstrs = new Instruction[instrs.length - 1];
System.arraycopy(instrs, 0, newInstrs, 0, i);
System.arraycopy(instrs, i+1, newInstrs, i,
newInstrs.length - i);
} else {
if (retPosition < 0) {
accessedLocals.add(instr.getLocalSlot());
switch (instr.getOpcode()) {
case opc_lload:
case opc_dload:
case opc_lstore:
case opc_dstore:
accessedLocals.add(instr.getLocalSlot()+1);
}
}
}
} else if (instr.getOpcode() == opc_pop
&& retPosition == -1) {
/* We spontanously left the subroutine by popping.
* Remove the pop Instruction.
*/
newInstrs = new Instruction[instrs.length - 1];
System.arraycopy(instrs, 0, newInstrs, 0, i);
System.arraycopy(instrs, i+1, newInstrs, i,
newInstrs.length - i);
substatus[info.blockNr] = SUBSTATUS_NOTSUB;
break;
} else if ((instr.getOpcode() == opc_pop2 &&
(retPosition == -1 || retPosition == -2))) {
/* We spontanously left the subroutine by popping.
* Replace the pop2 with a pop.
*/
newInstrs = new Instruction[instrs.length];
System.arraycopy(instrs, 0, newInstrs, 0,
instrs.length);
newInstrs[i] = Instruction.forOpcode(opc_pop);
substatus[info.blockNr] = SUBSTATUS_NOTSUB;
break;
} else if (instr.getOpcode() == opc_jsr) {
/* A recursive subroutine (or have we already
* spontanously left this subroutine?)
*/
int srBlock = instrs.getSuccs()[0].getBlockNr();
if (substatus[srBlock] == 0)
analyzeSubroutine(srBlock);
if (substatus[srBlock] == SUBSTATUS_INPROGRESS) {
/* We spontanously left this subroutine! */
if (retPosition < 0)
/* This can't happen in valid code.
*/
throw new CodeFormatException
("Can't merge return instr on Stack.");
substatus[info.blockNr] = SUBSTATUS_NOTSUB;
}
leftSub = true;
break;
} else if (substatus[srBlock] == SUBSTATUS_REMOVED) {
Instructions[] newInstrs
= new Instruction[instrs.length-1];
System.arraycopy(instrs, 0, newInstrs, 0,
newInstrs.length);
Block[] newSuccs = new Block[1] { instrs.getSuccs()[1]; };
blocks[i].setCode(newInstrs, newSuccs);
}
}
}
if (!leftSub) {
}
}
}
}

@ -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>
@ -29,13 +29,9 @@ class and get a ClassInfo object. As third step you can actually load
the class.<br><br> the class.<br><br>
Please notify me if you want to use this package. I will inform you Please notify me if you want to use this package. I will inform you
about updates, help you with problems, etc. <b> WARNING: </b> This about updates, help you with problems, etc. <b> WARNING: </b> Some
package may change in the future in incompatible ways. Ask me for parts of this package may change in the future in incompatible ways.
more information. <br><br> Ask me for more information. <br><br>
<b>BIG FAT WARNING:</b> Some of the things documented here aren't even
implemented. Some of this is only vapor ware. But feel free to
comment on this and point me to design errors!<br><br>
Here is a short example, how you can use this package, see the Here is a short example, how you can use this package, see the
documentation of the classes for more details. documentation of the classes for more details.
@ -55,7 +51,7 @@ documentation of the classes for more details.
} }
MethodInfo[] methods = clazz.getMethods(); MethodInfo[] methods = clazz.getMethods();
for (int i=0; i< methods.length; i++) { for (int i = 0; i &lt; methods.length; i++) {
String type = methods[i].getType(); String type = methods[i].getType();
if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE) if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE)
System.out.println("Found integer method: "+method.getName()); System.out.println("Found integer method: "+method.getName());
@ -79,7 +75,7 @@ You can also use this package to create and write new classes:
<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 16:56:39 MET DST 2000 -->
<!-- hhmts start --> <!-- hhmts start -->
Last modified: Mon Jun 26 09:46:24 MET DST 2000 Last modified: Sun Aug 5 17:53:03 MEST 2001
<!-- hhmts end --> <!-- hhmts end -->
</body> </body>
</html> </html>

@ -203,9 +203,6 @@ public class Decompiler {
* @param progress A progress listener (see below). Null if you * @param progress A progress listener (see below). Null if you
* don't need information about progress. * don't need information about progress.
* @exception IllegalArgumentException if className isn't correct. * @exception IllegalArgumentException if className isn't correct.
* @exception net.sf.jode.jvm.VerifyException The code
* isn't valid or a dependent class, needed for type
* guessing, couldn't be found.
* @exception IOException if writer throws an exception. * @exception IOException if writer throws an exception.
* @exception RuntimeException If jode has a bug ;-) * @exception RuntimeException If jode has a bug ;-)
*/ */

@ -20,6 +20,7 @@
package net.sf.jode.decompiler; package net.sf.jode.decompiler;
import net.sf.jode.bytecode.ClassInfo; import net.sf.jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.ClassPath; import net.sf.jode.bytecode.ClassPath;
import net.sf.jode.bytecode.ClassFormatException;
import net.sf.jode.GlobalOptions; import net.sf.jode.GlobalOptions;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -172,6 +173,10 @@ public class Main extends Options {
GlobalOptions.err.println GlobalOptions.err.println
("Check the class path ("+classPathStr+ ("Check the class path ("+classPathStr+
") and check that you use the java class name."); ") and check that you use the java class name.");
} catch (ClassFormatException ex) {
GlobalOptions.err.println
("Error while reading "+className+".");
ex.printStackTrace(GlobalOptions.err);
} catch (IOException ex) { } catch (IOException ex) {
GlobalOptions.err.println GlobalOptions.err.println
("Can't write source of "+className+"."); ("Can't write source of "+className+".");

@ -22,7 +22,7 @@ package net.sf.jode.decompiler;
/** /**
* This interface is used by jode to tell about its progress. You * This interface is used by jode to tell about its progress. You
* supply an instance of this interface to the * supply an instance of this interface to the
* {@link Decompiler.decompile} method.<br> * {@link Decompiler#decompile} method.<br>
* *
* @author <a href="mailto:jochen@gnu.org">Jochen Hoenicke</a> * @author <a href="mailto:jochen@gnu.org">Jochen Hoenicke</a>
* @version 1.0 */ * @version 1.0 */

@ -150,7 +150,7 @@ public class FlowBlock {
/** /**
* The stack map. This tells how many objects are on stack at * The stack map. This tells how many objects are on stack at
* begin of the flow block, and to what locals they are maped. * begin of the flow block, and to what locals they are maped.
* @see mapStackToLocal * @see #mapStackToLocal
*/ */
VariableStack stackMap; VariableStack stackMap;

@ -42,7 +42,7 @@ public class Jump {
/** /**
* The stack map. This tells how many objects are on stack at * The stack map. This tells how many objects are on stack at
* begin of the flow block, and to what locals they are maped. * begin of the flow block, and to what locals they are maped.
* @see FlowBlock.mapStackToLocal * @see FlowBlock#mapStackToLocal
*/ */
VariableStack stackMap; VariableStack stackMap;

@ -29,8 +29,8 @@ import net.sf.jode.expr.Operator;
* of the PUSH / stack_i statements. <p> * of the PUSH / stack_i statements. <p>
* *
* This class is immutable, but note, that the local infos can get merged. * This class is immutable, but note, that the local infos can get merged.
* @see FlowBlock.mapStackToLocal * @see FlowBlock#mapStackToLocal
* @see FlowBlock.removePush * @see FlowBlock#removePush
*/ */
public class VariableStack { public class VariableStack {
public final static VariableStack EMPTY = new VariableStack(); public final static VariableStack EMPTY = new VariableStack();

@ -1286,19 +1286,19 @@ public class CodeVerifier implements Opcodes {
Block block = blocks[blockNr]; Block block = blocks[blockNr];
VerifyInfo info = (VerifyInfo) verifyInfos[blockNr].clone(); VerifyInfo info = (VerifyInfo) verifyInfos[blockNr].clone();
Handler[] catchers = block.getCatchers(); Handler[] handlers = block.getHandlers();
if (catchers.length > 0) { if (handlers.length > 0) {
VerifyInfo excInfo = (VerifyInfo) info.clone(); VerifyInfo excInfo = (VerifyInfo) info.clone();
excInfo.stackHeight = 1; excInfo.stackHeight = 1;
for (int i=0; i < catchers.length; i++) { for (int i=0; i < handlers.length; i++) {
String type = catchers[i].getType(); String type = handlers[i].getType();
if (type != null) if (type != null)
excInfo.stack[0] = excInfo.stack[0] =
tType("L" + type.replace('.', '/') + ";"); tType("L" + type.replace('.', '/') + ";");
else else
excInfo.stack[0] excInfo.stack[0]
= tType("Ljava/lang/Throwable;"); = tType("Ljava/lang/Throwable;");
Block catcher = catchers[i].getCatcher(); Block catcher = handlers[i].getCatcher();
if (mergeInfo(catcher, excInfo)) if (mergeInfo(catcher, excInfo))
todoSet.set(catcher.getBlockNr()); todoSet.set(catcher.getBlockNr());
} }
@ -1311,10 +1311,10 @@ public class CodeVerifier implements Opcodes {
instr = (Instruction) iter.next(); instr = (Instruction) iter.next();
modelEffect(instr, info); modelEffect(instr, info);
if (catchers.length > 0 && instr.isStore()) { if (handlers.length > 0 && instr.isStore()) {
for (int i=0; i < catchers.length; i++) { for (int i=0; i < handlers.length; i++) {
int slot = instr.getLocalSlot(); int slot = instr.getLocalSlot();
Block catcher = catchers[i].getCatcher(); Block catcher = handlers[i].getCatcher();
int catcherNr = catcher.getBlockNr(); int catcherNr = catcher.getBlockNr();
VerifyInfo oldInfo = verifyInfos[catcherNr]; VerifyInfo oldInfo = verifyInfos[catcherNr];
Type newType = oldInfo.locals[slot] Type newType = oldInfo.locals[slot]

@ -108,7 +108,7 @@ public class Interpreter implements Opcodes {
return Void.TYPE; return Void.TYPE;
iter = Arrays.asList(nextBlock.getInstructions()).iterator(); iter = Arrays.asList(nextBlock.getInstructions()).iterator();
succs = nextBlock.getSuccs(); succs = nextBlock.getSuccs();
handlers = nextBlock.getCatchers(); handlers = nextBlock.getHandlers();
nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; nextBlock = succs.length > 0 ? succs[succs.length - 1] : null;
} }
try { try {

@ -357,6 +357,15 @@ public class ClassIdentifier extends Identifier {
} }
} else { } else {
// all methods and fields in superclass are preserved! // all methods and fields in superclass are preserved!
try {
superclass.load(ClassInfo.DECLARATIONS);
} catch (IOException ex) {
throw new RuntimeException
("Can't read declarations of class "
+ superclass.getName()
+ ": " + ex.getMessage());
}
MethodInfo[] topmethods = superclass.getMethods(); MethodInfo[] topmethods = superclass.getMethods();
for (int i=0; i< topmethods.length; i++) { for (int i=0; i< topmethods.length; i++) {
// all virtual methods in superclass may be // all virtual methods in superclass may be
@ -433,14 +442,12 @@ public class ClassIdentifier extends Identifier {
info.setSourceFile(null); info.setSourceFile(null);
} }
if ((Main.stripping & Main.STRIP_INNERINFO) != 0) { if ((Main.stripping & Main.STRIP_INNERINFO) != 0) {
info.setClasses(null); info.setClasses(new ClassInfo[0]);
info.setOuterClass(null); info.setOuterClass(null);
info.setExtraClasses(null);
} }
// load inner classes // load inner classes
ClassInfo outerClass = info.getOuterClass(); ClassInfo outerClass = info.getOuterClass();
ClassInfo[] innerClasses = info.getClasses(); ClassInfo[] innerClasses = info.getClasses();
ClassInfo[] extraClasses = info.getExtraClasses();
if (outerClass != null) if (outerClass != null)
Main.getClassBundle().getClassIdentifier(outerClass.getName()); Main.getClassBundle().getClassIdentifier(outerClass.getName());
@ -450,13 +457,6 @@ public class ClassIdentifier extends Identifier {
.getClassIdentifier(innerClasses[i].getName()); .getClassIdentifier(innerClasses[i].getName());
} }
} }
if (extraClasses != null) {
for (int i=0; i < extraClasses.length; i++) {
System.err.println("ec["+i+"]:"+extraClasses[i].getName());
Main.getClassBundle()
.getClassIdentifier(extraClasses[i].getName());
}
}
} }
/** /**
@ -515,6 +515,7 @@ public class ClassIdentifier extends Identifier {
.getClassIdentifier(outerClass.getName()); .getClassIdentifier(outerClass.getName());
if (outerIdent != null && outerIdent.isReachable()) if (outerIdent != null && outerIdent.isReachable())
break; break;
outerClass = outerClass.getOuterClass();
} }
} }
info.setOuterClass(outerClass); info.setOuterClass(outerClass);
@ -533,44 +534,13 @@ public class ClassIdentifier extends Identifier {
} }
} }
if (newInnerCount == 0) { ClassInfo[] newInners = new ClassInfo[newInnerCount];
info.setClasses(null); int pos = 0;
} else { for (int i=0; i<innerClasses.length; i++) {
ClassInfo[] newInners = new ClassInfo[newInnerCount]; if (innerClasses[i] != null)
int pos = 0; newInners[pos++] = innerClasses[i];
for (int i=0; i<innerClasses.length; i++) {
if (innerClasses[i] != null)
newInners[pos++] = innerClasses[i];
}
info.setClasses(newInners);
}
}
ClassInfo[] extraClasses = info.getExtraClasses();
if (extraClasses != null) {
int newExtraCount = extraClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < extraClasses.length; i++) {
ClassIdentifier extraIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].getName());
if (extraIdent != null && !extraIdent.isReachable()) {
extraClasses[i] = null;
newExtraCount--;
}
}
}
if (newExtraCount == 0) {
info.setExtraClasses(null);
} else {
ClassInfo[] newExtras = new ClassInfo[newExtraCount];
int pos = 0;
for (int i=0; i<extraClasses.length; i++) {
if (extraClasses[i] != null)
newExtras[pos++] = extraClasses[i];
}
info.setExtraClasses(newExtras);
} }
info.setClasses(newInners);
} }
} }

@ -81,14 +81,14 @@ public class PackageIdentifier extends Identifier {
// Load all classes and packages now, so they don't get stripped // Load all classes and packages now, so they don't get stripped
Enumeration enum = Enumeration enum =
ClassInfo.getClassesAndPackages(getFullName()); bundle.getClassPath().listClassesAndPackages(getFullName());
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
String subclazz = ((String)enum.nextElement()).intern(); String subclazz = ((String)enum.nextElement()).intern();
if (loadedClasses.containsKey(subclazz)) if (loadedClasses.containsKey(subclazz))
continue; continue;
String subFull = (fullNamePrefix + subclazz).intern(); String subFull = (fullNamePrefix + subclazz).intern();
if (ClassInfo.isPackage(subFull)) { if (bundle.getClassPath().isPackage(subFull)) {
PackageIdentifier ident = new PackageIdentifier PackageIdentifier ident = new PackageIdentifier
(bundle, this, subFull, subclazz); (bundle, this, subFull, subclazz);
loadedClasses.put(subclazz, ident); loadedClasses.put(subclazz, ident);
@ -121,14 +121,16 @@ public class PackageIdentifier extends Identifier {
String subFull = (fullName.length() > 0) String subFull = (fullName.length() > 0)
? fullName + "."+ component : component; ? fullName + "."+ component : component;
subFull = subFull.intern(); subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) { if (bundle.getClassPath().isPackage(subFull)) {
ident = new PackageIdentifier(bundle, this, ident = new PackageIdentifier(bundle, this,
subFull, component); subFull, component);
loadedClasses.put(component, ident); loadedClasses.put(component, ident);
swappedClasses = null; swappedClasses = null;
if (loadOnDemand) if (loadOnDemand)
((PackageIdentifier) ident).setLoadOnDemand(); ((PackageIdentifier) ident).setLoadOnDemand();
} else if (ClassInfo.exists(subFull)) { if (initialized)
((PackageIdentifier) ident).initialize();
} else if (bundle.getClassPath().existsClass(subFull)) {
if (GlobalOptions.verboseLevel > 1) if (GlobalOptions.verboseLevel > 1)
GlobalOptions.err.println("loading Class " +subFull); GlobalOptions.err.println("loading Class " +subFull);
ident = new ClassIdentifier(this, subFull, component, ident = new ClassIdentifier(this, subFull, component,
@ -162,7 +164,7 @@ public class PackageIdentifier extends Identifier {
(fullName.length() > 0) ? fullName + "." : ""; (fullName.length() > 0) ? fullName + "." : "";
/* Load all matching classes and packages */ /* Load all matching classes and packages */
Enumeration enum = Enumeration enum =
ClassInfo.getClassesAndPackages(getFullName()); bundle.getClassPath().listClassesAndPackages(getFullName());
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
String subclazz = ((String)enum.nextElement()).intern(); String subclazz = ((String)enum.nextElement()).intern();
if (loadedClasses.containsKey(subclazz)) if (loadedClasses.containsKey(subclazz))
@ -170,7 +172,7 @@ public class PackageIdentifier extends Identifier {
String subFull = (fullNamePrefix + subclazz).intern(); String subFull = (fullNamePrefix + subclazz).intern();
if (matcher.matchesSub(this, subclazz)) { if (matcher.matchesSub(this, subclazz)) {
if (ClassInfo.isPackage(subFull)) { if (bundle.getClassPath().isPackage(subFull)) {
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("loading Package " GlobalOptions.err.println("loading Package "
+ subFull); + subFull);
@ -180,6 +182,8 @@ public class PackageIdentifier extends Identifier {
swappedClasses = null; swappedClasses = null;
if (loadOnDemand || matcher.matches(ident)) if (loadOnDemand || matcher.matches(ident))
ident.setLoadOnDemand(); ident.setLoadOnDemand();
if (initialized)
((PackageIdentifier) ident).initialize();
} else { } else {
ClassIdentifier ident = new ClassIdentifier ClassIdentifier ident = new ClassIdentifier
(this, subFull, subclazz, (this, subFull, subclazz,
@ -250,14 +254,14 @@ public class PackageIdentifier extends Identifier {
String subFull = String subFull =
(fullName.length() > 0) ? fullName + "."+ name : name; (fullName.length() > 0) ? fullName + "."+ name : name;
subFull = subFull.intern(); subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) { if (bundle.getClassPath().isPackage(subFull)) {
PackageIdentifier pack PackageIdentifier pack
= new PackageIdentifier(bundle, this, subFull, name); = new PackageIdentifier(bundle, this, subFull, name);
loadedClasses.put(name, pack); loadedClasses.put(name, pack);
swappedClasses = null; swappedClasses = null;
pack.setLoadOnDemand(); pack.setLoadOnDemand();
ident = pack; ident = pack;
} else if (!ClassInfo.exists(subFull)) { } else if (!bundle.getClassPath().existsClass(subFull)) {
GlobalOptions.err.println("Warning: Can't find class " GlobalOptions.err.println("Warning: Can't find class "
+ subFull); + subFull);
Thread.dumpStack(); Thread.dumpStack();
@ -280,7 +284,7 @@ public class PackageIdentifier extends Identifier {
String subFull = (fullName.length() > 0) String subFull = (fullName.length() > 0)
? fullName + "."+ subpack : subpack; ? fullName + "."+ subpack : subpack;
subFull = subFull.intern(); subFull = subFull.intern();
if (ClassInfo.isPackage(subFull)) { if (bundle.getClassPath().isPackage(subFull)) {
pack = new PackageIdentifier(bundle, this, pack = new PackageIdentifier(bundle, this,
subFull, subpack); subFull, subpack);
loadedClasses.put(subpack, pack); loadedClasses.put(subpack, pack);

@ -19,6 +19,7 @@
package net.sf.jode.obfuscator.modules; package net.sf.jode.obfuscator.modules;
import net.sf.jode.bytecode.Opcodes; import net.sf.jode.bytecode.Opcodes;
import net.sf.jode.bytecode.ClassPath;
import net.sf.jode.bytecode.ClassInfo; import net.sf.jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.BasicBlocks; import net.sf.jode.bytecode.BasicBlocks;
import net.sf.jode.bytecode.Block; import net.sf.jode.bytecode.Block;
@ -30,6 +31,7 @@ import net.sf.jode.obfuscator.Identifier;
import net.sf.jode.obfuscator.*; import net.sf.jode.obfuscator.*;
import net.sf.jode.GlobalOptions; import net.sf.jode.GlobalOptions;
import java.io.IOException;
///#def COLLECTIONS java.util ///#def COLLECTIONS java.util
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@ -38,9 +40,25 @@ import java.util.ListIterator;
public class SimpleAnalyzer implements CodeAnalyzer, Opcodes { public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
public Identifier canonizeReference(Instruction instr) { private ClassInfo canonizeIfaceRef(ClassInfo clazz, Reference ref) {
while (clazz != null) {
if (clazz.findMethod(ref.getName(), ref.getType()) != null)
return clazz;
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i = 0; i < ifaces.length; i++) {
ClassInfo realClass = canonizeIfaceRef(ifaces[i], ref);
if (realClass != null)
return realClass;
}
clazz = clazz.getSuperclass();
}
return null;
}
protected Identifier canonizeReference(Instruction instr) {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
Identifier ident = Main.getClassBundle().getIdentifier(ref); Identifier ident = Main.getClassBundle().getIdentifier(ref);
ClassPath classPath = Main.getClassBundle().getClassPath();
String clName = ref.getClazz(); String clName = ref.getClazz();
String realClazzName; String realClazzName;
if (ident != null) { if (ident != null) {
@ -56,13 +74,21 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
/* Arrays don't define new methods (well clone(), /* Arrays don't define new methods (well clone(),
* but that can be ignored). * but that can be ignored).
*/ */
clazz = ClassInfo.forName("java.lang.Object"); clazz = classPath.getClassInfo("java.lang.Object");
} else { } else {
clazz = ClassInfo.forName clazz = classPath.getClassInfo
(clName.substring(1, clName.length()-1) (clName.substring(1, clName.length()-1)
.replace('/','.')); .replace('/','.'));
} }
if (instr.getOpcode() >= opc_invokevirtual) { try {
clazz.load(clazz.DECLARATIONS);
} catch (IOException ex) {
throw new RuntimeException("Can't get declarations of "
+ clazz);
}
if (instr.getOpcode() == opc_invokeinterface) {
clazz = canonizeIfaceRef(clazz, ref);
} else if (instr.getOpcode() >= opc_invokevirtual) {
while (clazz != null while (clazz != null
&& clazz.findMethod(ref.getName(), && clazz.findMethod(ref.getName(),
ref.getType()) == null) ref.getType()) == null)

@ -191,6 +191,9 @@ public class UnifyHash extends AbstractCollection {
///#endif ///#endif
return new Iterator() { return new Iterator() {
private int known = modCount; private int known = modCount;
private boolean removeOk = false;
private Bucket removeBucket = null;
private Bucket prevBucket = null;
private Bucket nextBucket private Bucket nextBucket
= buckets[Math.abs(hash % buckets.length)]; = buckets[Math.abs(hash % buckets.length)];
private Object nextVal; private Object nextVal;
@ -206,7 +209,7 @@ public class UnifyHash extends AbstractCollection {
if (nextVal != null) if (nextVal != null)
return; return;
} }
prevBucket = nextBucket;
nextBucket = nextBucket.next; nextBucket = nextBucket.next;
} }
} }
@ -221,13 +224,26 @@ public class UnifyHash extends AbstractCollection {
if (nextBucket == null) if (nextBucket == null)
throw new NoSuchElementException(); throw new NoSuchElementException();
Object result = nextVal; Object result = nextVal;
removeBucket = prevBucket;
removeOk = true;
prevBucket = nextBucket;
nextBucket = nextBucket.next; nextBucket = nextBucket.next;
internalNext(); internalNext();
return result; return result;
} }
public void remove() { public void remove() {
throw new UnsupportedOperationException(); if (known != modCount)
throw new ConcurrentModificationException();
if (!removeOk)
throw new IllegalStateException();
if (removeBucket == null)
buckets[Math.abs(hash % buckets.length)]
= buckets[Math.abs(hash % buckets.length)].next;
else
removeBucket.next = removeBucket.next.next;
known = ++modCount;
size--;
} }
}; };
} }
@ -248,6 +264,17 @@ public class UnifyHash extends AbstractCollection {
buckets[slot] = b; buckets[slot] = b;
} }
public boolean remove(int hash, Object o) {
Iterator i = iterateHashCode(hash);
while (i.hasNext()) {
if (i.next() == o) {
i.remove();
return true;
}
}
return false;
}
public Object unify(Object o, int hash, Comparator comparator) { public Object unify(Object o, int hash, Comparator comparator) {
///#ifdef JDK12 ///#ifdef JDK12
cleanUp(); cleanUp();

Loading…
Cancel
Save