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. 70
      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. 118
      jode/src/net/sf/jode/bytecode/BasicBlockWriter.java
  7. 106
      jode/src/net/sf/jode/bytecode/BasicBlocks.java
  8. 127
      jode/src/net/sf/jode/bytecode/BinaryInfo.java
  9. 61
      jode/src/net/sf/jode/bytecode/Block.java
  10. 310
      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. 11
      jode/src/net/sf/jode/bytecode/FieldInfo.java
  14. 4
      jode/src/net/sf/jode/bytecode/Handler.java
  15. 52
      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. 14
      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. 52
      jode/src/net/sf/jode/obfuscator/ClassIdentifier.java
  28. 22
      jode/src/net/sf/jode/obfuscator/PackageIdentifier.java
  29. 687
      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.

@ -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>
<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>
<!-- ********* 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 name="build-1.1">
<property name="jdk1.1.forced" value="on"/>
<antcall target="build"/>
</target> </target>
<target name="runjcpp" depends="check-packages"> <target name="build" depends="check-jdk,preconfig">
<copy todir="${src}"> <mkdir dir="${build}"/>
<fileset dir="src"/> <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> </target>
<!-- javadoc -->
<target name="javadoc"> <!-- ********* Javadoc targets ********** -->
<mkdir dir="${docs}/api"/>
<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 name="doc-html" depends="check-html"
if="php.present" unless="html.uptodate">
<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,17 +96,10 @@ 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;
@ -125,6 +120,24 @@ class BasicBlockWriter implements Opcodes {
todo.push(succs[j]); todo.push(succs[j]);
} }
} }
Handler[] handlers = block.getHandlers();
for (int j = 0; j < handlers.length; j++) {
int succNr = handlers[j].getCatcher().getBlockNr();
if (atStart[succNr] == null) {
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(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];
@ -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 // Use a lookup switch
length += 9 + 8 * npairs; length += 9 + 8 * npairs;
// The goto is inclusive through the default part. }
gotos[i+1] = -2; hasDefaultSucc = false;
continue next_block; break;
} }
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,12 +448,14 @@ class BasicBlockWriter implements Opcodes {
instrLength[i][j] = length; instrLength[i][j] = length;
addr += length; addr += length;
} }
if (hasDefaultSucc) {
Block defaultSucc = succs[succs.length-1]; Block defaultSucc = succs[succs.length-1];
if (defaultSucc == null) { if (defaultSucc == null) {
// This is a return // This is a return
gotos[i+1] = -1; gotos[i+1] = -1;
isRet.set(i+1); isRet.set(i+1);
lastRetAddr = addr; lastRetAddr = addr;
hasRet = true;
addr++; addr++;
} else if (defaultSucc.getBlockNr() == i + 1) { } else if (defaultSucc.getBlockNr() == i + 1) {
// no need for any jump // no need for any jump
@ -449,6 +465,10 @@ class BasicBlockWriter implements Opcodes {
gotos[i+1] = succs[succs.length-1].getBlockNr(); gotos[i+1] = succs[succs.length-1].getBlockNr();
addr += 3; addr += 3;
} }
} else {
// No goto needed for this block
gotos[i+1] = -2;
}
} }
if (needRet && !hasRet) { if (needRet && !hasRet) {
retAtEnd = true; retAtEnd = true;
@ -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,6 +503,7 @@ class BasicBlockWriter implements Opcodes {
break; break;
} }
} }
if (dist == Integer.MAX_VALUE)
throw new InternalError(); throw new InternalError();
} }
} else { } else {
@ -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,19 +804,20 @@ 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,6 +971,7 @@ class BasicBlockWriter implements Opcodes {
} }
addr += instrLength[i][j]; addr += instrLength[i][j];
} }
if (hasDefaultSucc) {
// Check which type of goto we should use at end of this block. // Check which type of goto we should use at end of this block.
Block defaultSucc = succs[succs.length - 1]; Block defaultSucc = succs[succs.length - 1];
if (isRet.get(i+1)) { if (isRet.get(i+1)) {
@ -951,14 +979,17 @@ class BasicBlockWriter implements Opcodes {
addr++; addr++;
} else if (isWide.get(i+1)) { } else if (isWide.get(i+1)) {
output.writeByte(opc_goto_w); output.writeByte(opc_goto_w);
output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr); output.writeInt(blockAddr[defaultSucc.getBlockNr()]
- addr);
addr+=5; addr+=5;
} else if (defaultSucc.getBlockNr() != i+1) { } else if (defaultSucc.getBlockNr() != i+1) {
output.writeByte(opc_goto); output.writeByte(opc_goto);
output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr); output.writeShort(blockAddr[defaultSucc.getBlockNr()]
- addr);
addr+=3; addr+=3;
} }
} }
}
if (retAtEnd) { if (retAtEnd) {
output.writeByte(opc_return); output.writeByte(opc_return);
addr++; addr++;
@ -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,11 +77,19 @@ 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.
* @param length the length of the attribute.
* @param constantPool the constant pool of the class.
* @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, ConstantPool constantPool,
DataInputStream input, DataInputStream input,
int howMuch) throws IOException { int howMuch) throws IOException {
@ -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,9 +415,23 @@ 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
*/
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); mergeModifiers(realModifiers);
} else { } else {
if (realModifiers != -1) if (realModifiers != -1)
@ -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 * http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996
* 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 * there is a InnerClass record for each non package scope
* classes, and so on: According to the spec, these must * class referenced in this class. We are only interested in
* always be present if this is a class scoped class. And they * out own entry and in the entries for our inner classes.
* must be in reversed order, i.e. the outer most class comes * The latter can easily be recognized, since this class must
* first. * be mentioned in the outer_class_info_index field.
*
* Some other InnerClass records, the extra classes. This is
* optional, but we don't want to loose this information if we
* just transform classes, so we memorize for which classes we
* have to keep the information anyway.
*
* 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,12 +467,12 @@ 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();
@ -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 (innername == null)
outer = null;
/* The best way to distinguish method scope classes is by thier
* class name.
*/ */
if (outer != null && innername != null 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,29 +519,12 @@ 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 {
@ -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,8 +706,9 @@ 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++) {
@ -805,8 +751,24 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
} }
} }
/* 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;
} }
@ -845,25 +807,16 @@ 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 {
clName = gcp.getUTF8(gcp.indices1[i]);
} catch (ClassFormatException ex) {
throw new InternalError(ex.getMessage());
}
if (clName.charAt(0) != '[') {
ClassInfo ci = classpath.getClassInfo
(clName.replace('/','.'));
if (ci.status < OUTERCLASS) { if (ci.status < OUTERCLASS) {
GlobalOptions.err.println GlobalOptions.err.println
("WARNING: "+ ci.name ("WARNING: " + ci.name + "'s outer class isn't known.");
+ "'s outer class isn't known.");
} else { } else {
if ((ci.outerClass != null || ci.methodScoped) if ((ci.outerClass != null || ci.methodScoped)
&& ! hasInnerClassesAttr) { && ! hasInnerClassesAttr) {
gcp.putUTF8("innerClasses"); gcp.putUTF8("InnerClasses");
hasInnerClassesAttr = true; hasInnerClassesAttr = true;
} }
if (ci.outerClass != null) if (ci.outerClass != null)
@ -872,13 +825,11 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
gcp.putUTF8(ci.className); 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,19 +848,12 @@ 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());
}
if (clName.charAt(0) != '[') {
ClassInfo ci = classpath.getClassInfo
(clName.replace('/','.'));
while (ci.status >= OUTERCLASS
&& ci.outerClass != null || ci.methodScoped) {
/* Order is important so remove ci if it /* Order is important so remove ci if it
* already exists and add it to the end. This * already exists and add it to the end. This
* way the outermost classes go to the end. * way the outermost classes go to the end.
@ -918,17 +863,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
ci = ci.outerClass; ci = ci.outerClass;
} }
} }
}
}
if (hasInnerClassesAttr) { if (hasInnerClassesAttr) {
int count = outers.size(); int count = outers.size();
output.writeShort(gcp.putUTF8("InnerClasses")); output.writeShort(gcp.putUTF8("InnerClasses"));
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();
@ -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;
} }
@ -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;
} }

@ -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,7 +44,7 @@ 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 {
@ -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,7 +77,8 @@ 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
(String name, int length, ConstantPool cp,
DataInputStream input, int howMuch) throws IOException { 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);
@ -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);
@ -151,7 +185,7 @@ public final class MethodInfo 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)
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) {
}
}
}
}

@ -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,9 +534,6 @@ public class ClassIdentifier extends Identifier {
} }
} }
if (newInnerCount == 0) {
info.setClasses(null);
} else {
ClassInfo[] newInners = new ClassInfo[newInnerCount]; ClassInfo[] newInners = new ClassInfo[newInnerCount];
int pos = 0; int pos = 0;
for (int i=0; i<innerClasses.length; i++) { for (int i=0; i<innerClasses.length; i++) {
@ -546,34 +544,6 @@ public class ClassIdentifier extends Identifier {
} }
} }
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);
}
}
}
public void doTransformations() { public void doTransformations() {
if (GlobalOptions.verboseLevel > 0) if (GlobalOptions.verboseLevel > 0)
GlobalOptions.err.println("Transforming "+this); GlobalOptions.err.println("Transforming "+this);

@ -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);

@ -66,7 +66,7 @@ import java.util.ListIterator;
* *
* *
* @author Jochen Hoenicke */ * @author Jochen Hoenicke */
public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { public class ConstantAnalyzer extends SimpleAnalyzer {
private static ConstantRuntimeEnvironment runtime private static ConstantRuntimeEnvironment runtime
= new ConstantRuntimeEnvironment(); = new ConstantRuntimeEnvironment();
@ -188,7 +188,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
value = VOLATILE; value = VOLATILE;
for (Iterator i = listeners.iterator(); i.hasNext(); ) { for (Iterator i = listeners.iterator(); i.hasNext(); ) {
ConstantListener l = (ConstantListener) i.next(); ConstantListener l = (ConstantListener) i.next();
System.err.println(" notifying: "+ l); // System.err.println(" notifying: "+ l);
l.constantChanged(); l.constantChanged();
} }
listeners = null; listeners = null;
@ -217,130 +217,106 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
} }
/**
* This class handles information necessary for jsr analysis.
* Jsr is probably the most difficult opcode to handle, we have
* to keep track of changed locals, of nested jsrs, if there is
* a corresponding ret instruction and much more.
*/
private final static class JsrInfo implements Cloneable { private final static class JsrInfo implements Cloneable {
BitSet usedLocals;
BlockInfo jsrTarget; BlockInfo jsrTarget;
Collection callers; Collection callers;
/** /**
* The dependent entries, that want to know if the bit set changed. * The number of outer subroutines
* This is either a StackLocalInfo (the ret info) or a single
* JsrInfo or a Collection of JsrInfos.
*/ */
Object dependent; int jsrDepth;
public JsrInfo(BlockInfo target) { /**
jsrTarget = target; * The outer jsr info, or null if this is a top level jsr.
callers = new ArrayList(); */
usedLocals = new BitSet(); JsrInfo outerJsr;
}
public JsrInfo copy() { /**
try { * The locals used in the outer subroutine, or null if this is
JsrInfo result = (JsrInfo) clone(); * a top level jsr.
result.usedLocals = (BitSet) usedLocals.clone(); * The locals used inside this jsr are in the BlockInfo itself.
addDependent(result); */
return result; BitSet usedInOuter;
} catch (CloneNotSupportedException ex) {
throw new IncompatibleClassChangeError(ex.getMessage());
}
}
private void addDependent(JsrInfo result) { /**
if (dependent == null || dependent == result) * The info for the ret block of this subroutine.
dependent = result; */
else if (dependent instanceof JsrInfo) { BlockInfo retInfo;
Set newDeps = new HashSet();
newDeps.add(dependent);
newDeps.add(result);
} else if (dependent instanceof Collection) {
((Collection) dependent).add(result);
}
}
public boolean uses(int localSlot) { public JsrInfo(BlockInfo target, JsrInfo outer, BitSet used) {
return usedLocals.get(localSlot); jsrTarget = target;
if (outer != null) {
outerJsr = outer;
usedInOuter = (BitSet) used.clone();
}
jsrDepth = (outer != null ? outer.jsrDepth + 1 : 0);
callers = new ArrayList();
} }
public void setRetInfo(BlockInfo retInfo) { public void setRetInfo(BlockInfo retInfo) {
dependent = retInfo; this.retInfo = retInfo;
for (Iterator i = callers.iterator(); i.hasNext(); ) for (Iterator i = callers.iterator(); i.hasNext(); )
retInfo.mergeRetLocals(this, (BlockInfo)i.next()); ((BlockInfo)i.next()).mergeRetLocals(this, retInfo);
} }
public void addCaller(BlockInfo caller) { public void addCaller(BlockInfo caller) {
if (callers.contains(caller)) if (callers.contains(caller))
return; return;
callers.add(caller); callers.add(caller);
if (retInfo != null)
if (dependent instanceof BlockInfo) { caller.mergeRetLocals(this, retInfo);
BlockInfo retInfo = ((BlockInfo) dependent);
for (Iterator i = callers.iterator(); i.hasNext(); )
retInfo.mergeRetLocals(this, caller);
} else if (dependent instanceof JsrInfo) {
((JsrInfo) dependent).addCaller(caller);
} else if (dependent instanceof Collection) {
Iterator iter = ((Collection) dependent).iterator();
while (iter.hasNext()) {
JsrInfo dep = (JsrInfo) iter.next();
dep.addCaller(caller);
}
}
}
public boolean hasRetInfo() {
if (dependent instanceof BlockInfo)
return true;
else if (dependent instanceof JsrInfo)
return ((JsrInfo) dependent).hasRetInfo();
else if (dependent instanceof Collection) {
Iterator iter = ((Collection) dependent).iterator();
while (iter.hasNext()) {
JsrInfo dep = (JsrInfo) iter.next();
if (dep.hasRetInfo())
return true;
}
}
return false;
} }
public void addUsed(int localSlot) { public JsrInfo intersect(JsrInfo other, BitSet used) {
if (usedLocals.get(localSlot)) int otherDepth = other != null ? other.jsrDepth : -1;
return; JsrInfo isect = this;
usedLocals.set(localSlot); int myDepth = jsrDepth;
if (dependent instanceof BlockInfo) { while (otherDepth > myDepth) {
BlockInfo retInfo = ((BlockInfo) dependent); if (other.usedInOuter != null)
for (Iterator i = callers.iterator(); i.hasNext(); ) used.or(other.usedInOuter);
retInfo.mergeRetLocals(this, (BlockInfo)i.next()); other = other.outerJsr;
} else if (dependent instanceof JsrInfo) { otherDepth--;
((JsrInfo) dependent).addUsed(localSlot);
} else if (dependent instanceof Collection) {
Iterator iter = ((Collection) dependent).iterator();
while (iter.hasNext()) {
JsrInfo dep = (JsrInfo) iter.next();
dep.addUsed(localSlot);
} }
while (myDepth > otherDepth) {
if (isect.usedInOuter != null)
used.or(isect.usedInOuter);
isect = isect.outerJsr;
myDepth--;
} }
while (isect != other) {
if (other.usedInOuter != null) {
used.or(other.usedInOuter);
used.or(isect.usedInOuter);
} }
other = other.outerJsr;
public void merge(JsrInfo o) { isect = isect.outerJsr;
o.addDependent(this);
for (int slot = 0; slot < o.usedLocals.size(); slot++) {
if (o.usedLocals.get(slot))
addUsed(slot);
} }
for (Iterator i = o.callers.iterator(); i.hasNext();) { return isect;
addCaller((BlockInfo) i.next());
} }
public void merge(JsrInfo outer, BitSet used) {
if (outerJsr != null)
outerJsr = outerJsr.intersect(outer, used);
if (outerJsr == null)
usedInOuter = null;
else
usedInOuter.or(used);
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget)); StringBuffer sb = new StringBuffer(String.valueOf(jsrTarget.nr));
if (dependent instanceof BlockInfo) if (retInfo != null)
sb.append("->").append(((BlockInfo) dependent).nr); sb.append("->").append(retInfo.nr);
return sb.append(usedLocals) if (outerJsr != null)
.append('_').append(hashCode()).toString(); sb.append("used="+usedInOuter+",outer=["+outerJsr+"]");
return sb.toString();
} }
} }
@ -381,6 +357,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
locals[i] = original.locals[i].copy(); locals[i] = original.locals[i].copy();
} }
stack = new ConstValue[original.stack.length]; stack = new ConstValue[original.stack.length];
stackDepth = original.stackDepth;
for (int i=0; i< stackDepth; i++) { for (int i=0; i< stackDepth; i++) {
if (original.stack[i] != null) if (original.stack[i] != null)
stack[i] = original.stack[i].copy(); stack[i] = original.stack[i].copy();
@ -388,15 +365,17 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
public StackLocalInfo poppush(int pops, ConstValue push) { public StackLocalInfo poppush(int pops, ConstValue push) {
for (int i = pops; i>= 0; i--) for (int i = pops; i > 0; i--)
stack[--stackDepth] = null; stack[--stackDepth] = null;
if (push == null)
throw new NullPointerException();
stack[stackDepth] = push; stack[stackDepth] = push;
stackDepth += push.stackSize; stackDepth += push.stackSize;
return this; return this;
} }
public StackLocalInfo pop(int pops) { public StackLocalInfo pop(int pops) {
for (int i = pops; i>= 0; i--) for (int i = pops; i > 0; i--)
stack[--stackDepth] = null; stack[--stackDepth] = null;
return this; return this;
} }
@ -433,7 +412,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
public ConstValue getStack(int depth) { public ConstValue getStack(int depth) {
return stack[stack.length - depth]; return stack[stackDepth - depth];
} }
public StackLocalInfo setLocal(int slot, ConstValue value) { public StackLocalInfo setLocal(int slot, ConstValue value) {
@ -468,8 +447,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
public String toString() { public String toString() {
return "Locals: "+Arrays.asList(locals) return "StackLocalInfo[locals="+Arrays.asList(locals)
+"Stack: "+Arrays.asList(stack); +",stack="+Arrays.asList(stack)+",stackDepth="+stackDepth+"]";
} }
} }
@ -530,10 +509,18 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
StackLocalInfo after; StackLocalInfo after;
/** /**
* The JsrInfo of jsrs that must be visited to reach * The JsrInfo of the innermost surrounding subroutine. If
* this block. * before is null, this value is null and means unknown. If
* before is not null, a value of null means no surrounding
* subroutine.
*/
JsrInfo jsrInfo;
/**
* The locals used between the jsr instruction for jsrInfo and
* the end of the current block; null if jsrInfo is null.
*/ */
JsrInfo[] jsrInfos; BitSet usedLocals;
public BlockInfo(int nr, Block block) { public BlockInfo(int nr, Block block) {
this.nr = nr; this.nr = nr;
@ -549,36 +536,24 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
public void mergeBefore(StackLocalInfo info, public void mergeBefore(StackLocalInfo info,
JsrInfo[] newJsrInfos) { JsrInfo newJsrInfo,
BitSet usedLocals) {
if (before == null) { if (before == null) {
System.err.println("mergeBefore:::"+info); // System.err.println("mergeBefore:::"+info);
before = new StackLocalInfo(info); before = new StackLocalInfo(info);
this.jsrInfos = new JsrInfo[newJsrInfos.length]; this.jsrInfo = newJsrInfo;
for (int i = 0; i < newJsrInfos.length; i++) if (usedLocals != null)
jsrInfos[i] = newJsrInfos[i].copy(); this.usedLocals = (BitSet) usedLocals.clone();
modifiedQueue.enqueue(this); modifiedQueue.enqueue(this);
} else { } else {
System.err.println("merging:::: "+before+":::AND:::"+info); // System.err.println("merging:::: "+before+":::AND:::"+info);
before.merge(info); before.merge(info);
int newJsrCount = 0; if (jsrInfo != null)
int ptr = 0; jsrInfo = jsrInfo.intersect(newJsrInfo, usedLocals);
for (int i = 0; i < jsrInfos.length; i++) { if (jsrInfo == null)
for (int j = ptr; j < newJsrInfos.length; j++) { this.usedLocals = null;
if (newJsrInfos[j].jsrTarget else
== jsrInfos[i].jsrTarget) { this.usedLocals.or(usedLocals);
jsrInfos[newJsrCount] = jsrInfos[i];
jsrInfos[newJsrCount].merge(newJsrInfos[j]);
newJsrCount++;
ptr = j+1;
break;
}
}
}
if (newJsrCount < jsrInfos.length) {
JsrInfo[] newJsr = new JsrInfo[newJsrCount];
System.arraycopy(jsrInfos, 0, newJsr, 0, newJsrCount);
jsrInfos = newJsr;
}
if (constantFlow >= 0) if (constantFlow >= 0)
propagateAfter(); propagateAfter();
} }
@ -590,17 +565,19 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
public void useLocal(int slot) { public void useLocal(int slot) {
for (int i=0; i< jsrInfos.length; i++) if (usedLocals != null)
jsrInfos[i].addUsed(slot); usedLocals.set(slot);
} }
public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo caller) { public void mergeRetLocals(JsrInfo myJsrInfo, BlockInfo retBlock) {
if (constantFlow == 0) { if (constantFlow == 0) {
Instruction[] instrs = block.getInstructions(); Instruction[] instrs = block.getInstructions();
// remove the constantFlow info // remove the constantFlow info
constantInfos.remove(instrs[instrs.length-1]); constantInfos.remove(instrs[instrs.length-1]);
constantFlow = -1;
} }
Block nextBlock = caller.block.getSuccs()[1];
Block nextBlock = block.getSuccs()[1];
if (nextBlock == null) { if (nextBlock == null) {
/* The calling jsr is just before a return. We don't /* The calling jsr is just before a return. We don't
* have to fuzz around, since nobody is interested in * have to fuzz around, since nobody is interested in
@ -610,21 +587,29 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
ConstValue[] newLocals = (ConstValue[]) after.locals.clone(); ConstValue[] newLocals = (ConstValue[]) after.locals.clone();
BitSet nextUsed = new BitSet();
for (int slot = 0; slot < newLocals.length; slot++) { for (int slot = 0; slot < newLocals.length; slot++) {
if (!myJsrInfo.uses(slot)) if (retBlock.usedLocals.get(slot)) {
newLocals[slot] = caller.after.locals[slot]; newLocals[slot] = retBlock.after.locals[slot];
else if (nextUsed != null)
caller.useLocal(slot); nextUsed.set(slot);
}
} }
StackLocalInfo nextInfo StackLocalInfo nextInfo
= new StackLocalInfo(after.stack, newLocals, after.stackDepth); = new StackLocalInfo(after.stack, newLocals, after.stackDepth);
int newCount = jsrInfos.length - 1; JsrInfo nextJsrInfo = null;
while (jsrInfos[newCount].jsrTarget != myJsrInfo.jsrTarget) if (usedLocals != null)
newCount--; nextUsed.or(usedLocals);
JsrInfo[] retJsrInfos = new JsrInfo[newCount]; if (myJsrInfo.usedInOuter != null)
System.arraycopy(jsrInfos, 0, nextUsed.or(myJsrInfo.usedInOuter);
retJsrInfos, 0, newCount); if (jsrInfo != null)
infos[nextBlock.getBlockNr()].mergeBefore(nextInfo, retJsrInfos); nextJsrInfo = jsrInfo.intersect(myJsrInfo.outerJsr,
nextUsed);
if (nextJsrInfo == null)
nextUsed = null;
infos[nextBlock.getBlockNr()].mergeBefore(nextInfo,
nextJsrInfo, nextUsed);
} }
public void propagateAfter() { public void propagateAfter() {
@ -692,7 +677,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
Block constantSucc = block.getSuccs()[constantFlow]; Block constantSucc = block.getSuccs()[constantFlow];
if (constantSucc != null) if (constantSucc != null)
infos[constantSucc.getBlockNr()] infos[constantSucc.getBlockNr()]
.mergeBefore(nextInfo, jsrInfos); .mergeBefore(nextInfo, jsrInfo, usedLocals);
} else { } else {
constantInfos.remove(instr); constantInfos.remove(instr);
for (int i=0; i < 2; i++) { for (int i=0; i < 2; i++) {
@ -700,7 +685,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
Block succ = block.getSuccs()[i]; Block succ = block.getSuccs()[i];
if (succ != null) if (succ != null)
infos[succ.getBlockNr()] infos[succ.getBlockNr()]
.mergeBefore(nextInfo, jsrInfos); .mergeBefore(nextInfo,
jsrInfo, usedLocals);
} }
} }
constantFlow = -1; constantFlow = -1;
@ -727,7 +713,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
Block constantSucc = block.getSuccs()[constantFlow]; Block constantSucc = block.getSuccs()[constantFlow];
if (constantSucc != null) if (constantSucc != null)
infos[constantSucc.getBlockNr()] infos[constantSucc.getBlockNr()]
.mergeBefore(nextInfo, jsrInfos); .mergeBefore(nextInfo, jsrInfo, usedLocals);
} else { } else {
constantInfos.remove(instr); constantInfos.remove(instr);
Block[] succs = block.getSuccs(); Block[] succs = block.getSuccs();
@ -735,7 +721,8 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
if (i != constantFlow) { if (i != constantFlow) {
if (succs[i] != null) if (succs[i] != null)
infos[succs[i].getBlockNr()] infos[succs[i].getBlockNr()]
.mergeBefore(nextInfo, jsrInfos); .mergeBefore(nextInfo, jsrInfo,
usedLocals);
} }
} }
constantFlow = -1; constantFlow = -1;
@ -743,40 +730,51 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
break; break;
} }
case opc_jsr: { case opc_jsr: {
// XXXXXXXXXXXX System.err /* Assume there is no ret for this jsr. If the ret
dumpBlockInfo(GlobalOptions.err); * was already found this info will be corrected
GlobalOptions.err.println(instr); * immediately by the jsrInfo.addCaller.
*/
BlockInfo target = infos[block.getSuccs()[0].getBlockNr()];
ConstValue result = new ConstValue(target);
int jsrInfosCount = jsrInfos.length;
for (int i=0; i< jsrInfosCount; i++) {
if (jsrInfos[i].jsrTarget == target) {
// recursive jsrs are forbidden. But maybe the jsr
// just terminated without a ret.
jsrInfosCount = i;
}
}
JsrInfo[] newJsrInfos = new JsrInfo[jsrInfosCount + 1];
System.arraycopy(jsrInfos, 0, newJsrInfos, 0, jsrInfosCount);
newJsrInfos[jsrInfosCount] = new JsrInfo(target);
newJsrInfos[jsrInfosCount].addCaller(this);
constantFlow = 0; constantFlow = 0;
ConstantInfo constInfo = new ConstantInfo ConstantInfo constInfo = new ConstantInfo
(CONSTANTFLOW, new Integer(0)); (CONSTANTFLOW, new Integer(0));
constantInfos.put(instr, constInfo); constantInfos.put(instr, constInfo);
BlockInfo target = infos[block.getSuccs()[0].getBlockNr()];
ConstValue result = new ConstValue(target);
JsrInfo newJsrInfo;
BitSet newUsed;
if (target.before == null) {
/* The info for this jsr target wasn't created before. */
newJsrInfo = new JsrInfo(target, jsrInfo, usedLocals);
newJsrInfo.addCaller(this);
newUsed = new BitSet();
} else if (target.jsrInfo == null
|| target.jsrInfo.jsrTarget != target) {
/* The jsr exits instantenously. */
newJsrInfo = jsrInfo;
newUsed = usedLocals;
} else {
/* Normal case, merge jsr infos. */
newJsrInfo = target.jsrInfo;
newJsrInfo.merge(jsrInfo, usedLocals);
newJsrInfo.addCaller(this);
newUsed = new BitSet();
}
target.mergeBefore(after.copy().poppush(0, result), target.mergeBefore(after.copy().poppush(0, result),
newJsrInfos); newJsrInfo, newUsed);
break; break;
} }
case opc_ret: { case opc_ret: {
ConstValue result = after.getLocal(instr.getLocalSlot()); ConstValue result = after.getLocal(instr.getLocalSlot());
BlockInfo jsrTarget = (BlockInfo) result.value; BlockInfo jsrTarget = (BlockInfo) result.value;
while (jsrInfo.jsrTarget != jsrTarget) {
int i = jsrInfos.length-1; usedLocals.or(jsrInfo.usedInOuter);
while (jsrInfos[i].jsrTarget != jsrTarget) jsrInfo = jsrInfo.outerJsr;
i--; }
jsrInfos[i].setRetInfo(this); jsrInfo.setRetInfo(this);
break; break;
} }
case opc_ireturn: case opc_lreturn: case opc_ireturn: case opc_lreturn:
@ -787,184 +785,13 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
default: { default: {
Block succ = block.getSuccs()[0]; Block succ = block.getSuccs()[0];
if (succ != null) if (succ != null)
infos[succ.getBlockNr()].mergeBefore(after, jsrInfos); infos[succ.getBlockNr()].mergeBefore
} (after, jsrInfo, usedLocals);
}
}
public void analyze() {
System.err.println("analyze:");
StackLocalInfo info = before.copy();
Handler[] handlers = block.getCatchers();
if (handlers.length > 0) {
ConstValue[] newStack = new ConstValue[info.stack.length];
newStack[0] = unknownValue[0];
StackLocalInfo catchInfo =
new StackLocalInfo(newStack, info.locals, 1);
for (int i=0; i< handlers.length; i++) {
if (handlers[i].getType() != null)
Main.getClassBundle().reachableClass
(handlers[i].getType());
infos[handlers[i].getCatcher().getBlockNr()]
.mergeBefore(catchInfo, jsrInfos);
}
}
Instruction[] instrs = block.getInstructions();
for (int idx = 0 ; idx < instrs.length; idx++) {
Instruction instr = instrs[idx];
info = handleOpcode(instr, info, this);
if (instr.isStore() && handlers.length > 0) {
int slot = instr.getLocalSlot();
ConstValue newValue = info.locals[slot];
for (int i=0; i< handlers.length; i++) {
infos[handlers[i].getCatcher().getBlockNr()]
.mergeOneLocal(slot, info.locals[slot]);
if (newValue.stackSize > 1)
infos[handlers[i].getCatcher().getBlockNr()]
.mergeOneLocal(slot+1, info.locals[slot+1]);
}
}
}
after = info;
dumpInfo(GlobalOptions.err);
propagateAfter();
}
public void dumpInfo(PrintWriter output) {
output.println("/-["+nr+"]-"+before);
block.dumpCode(output);
output.println("\\-["+nr+"]-"+after);
}
public String toString() {
return "BlockAnalyzer["+nr+"]";
}
}
/**
* The TodoQueue is a linked list of BlockInfo
*
* There is only one TodoQueue, the modifiedQueue in analyzeCode
*
* The queue operations are in StackLocalInfo.
*/
static class TodoQueue {
BlockInfo first;
public void enqueue(BlockInfo info) {
if (info.nextTodo == null) {
info.nextTodo = first;
first = info;
}
}
public BlockInfo dequeue() {
BlockInfo result = first;
if (result != null) {
first = result.nextTodo;
result.nextTodo = null;
}
return result;
}
}
public void fieldNotConstant(FieldIdentifier fi) {
ConstValue value = (ConstValue) fieldDependencies.remove(fi);
if (value != null)
value.constantChanged();
fi.removeFieldListener(methodIdent);
fi.setNotConstant();
}
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;
} }
private Identifier canonizeReference(Instruction instr) { StackLocalInfo handleOpcode(Instruction instr, StackLocalInfo info) {
Reference ref = instr.getReference();
Identifier ident = Main.getClassBundle().getIdentifier(ref);
String clName = ref.getClazz();
String realClazzName;
if (ident != null) {
ClassIdentifier clazz = (ClassIdentifier)ident.getParent();
realClazzName = "L" + (clazz.getFullName()
.replace('.', '/')) + ";";
} else {
/* We have to look at the ClassInfo's instead, to
* point to the right method.
*/
ClassInfo clazz;
if (clName.charAt(0) == '[') {
/* Arrays don't define new methods (well clone(),
* but that can be ignored).
*/
clazz = ClassInfo.forName("java.lang.Object");
} else {
clazz = ClassInfo.forName
(clName.substring(1, clName.length()-1)
.replace('/','.'));
}
if (instr.getOpcode() == opc_invokeinterface) {
clazz = canonizeIfaceRef(clazz, ref);
} else if (instr.getOpcode() >= opc_invokevirtual) {
while (clazz != null
&& clazz.findMethod(ref.getName(),
ref.getType()) == null)
clazz = clazz.getSuperclass();
} else {
while (clazz != null
&& clazz.findField(ref.getName(),
ref.getType()) == null)
clazz = clazz.getSuperclass();
}
if (clazz == null) {
GlobalOptions.err.println("WARNING: Can't find reference: "
+ref);
realClazzName = clName;
} else
realClazzName = "L" + clazz.getName().replace('.', '/') + ";";
}
if (!realClazzName.equals(ref.getClazz())) {
ref = Reference.getReference(realClazzName,
ref.getName(), ref.getType());
instr.setReference(ref);
}
return ident;
}
void handleReference(Reference ref, boolean isVirtual) {
Main.getClassBundle().reachableReference(ref, isVirtual);
}
void handleClass(String clName) {
int i = 0;
while (i < clName.length() && clName.charAt(i) == '[')
i++;
if (i < clName.length() && clName.charAt(i) == 'L') {
clName = clName.substring(i+1, clName.length()-1);
Main.getClassBundle().reachableClass(clName);
}
}
StackLocalInfo handleOpcode(Instruction instr,
StackLocalInfo info, BlockInfo block) {
constantInfos.remove(instr); constantInfos.remove(instr);
int opcode = instr.getOpcode(); int opcode = instr.getOpcode();
ConstValue result; ConstValue result;
@ -997,14 +824,16 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
case opc_istore: case opc_fstore: case opc_astore: { case opc_istore: case opc_fstore: case opc_astore: {
int slot = instr.getLocalSlot(); int slot = instr.getLocalSlot();
block.useLocal(slot); useLocal(slot);
return info.pop(1).setLocal(slot, info.getStack(1)); result = info.getStack(1);
return info.pop(1).setLocal(slot, result);
} }
case opc_lstore: case opc_dstore: { case opc_lstore: case opc_dstore: {
int slot = instr.getLocalSlot(); int slot = instr.getLocalSlot();
block.useLocal(slot); useLocal(slot);
block.useLocal(slot + 1); useLocal(slot + 1);
return info.pop(2).setLocal(slot, info.getStack(2)); result = info.getStack(2);
return info.pop(2).setLocal(slot, result);
} }
case opc_iastore: case opc_lastore: case opc_iastore: case opc_lastore:
case opc_fastore: case opc_dastore: case opc_aastore: case opc_fastore: case opc_dastore: case opc_aastore:
@ -1283,7 +1112,9 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
return info.poppush(size+1, result); return info.poppush(size+1, result);
} }
case opc_iinc: { case opc_iinc: {
ConstValue local = info.getLocal(instr.getLocalSlot()); int slot = instr.getLocalSlot();
useLocal(slot);
ConstValue local = info.getLocal(slot);
if (local.value != ConstValue.VOLATILE) { if (local.value != ConstValue.VOLATILE) {
result = new ConstValue result = new ConstValue
(new Integer(((Integer)local.value).intValue() (new Integer(((Integer)local.value).intValue()
@ -1600,6 +1431,110 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
} }
} }
public void analyze() {
StackLocalInfo info = before.copy();
Handler[] handlers = block.getHandlers();
if (handlers.length > 0) {
ConstValue[] newStack = new ConstValue[info.stack.length];
newStack[0] = unknownValue[0];
StackLocalInfo catchInfo =
new StackLocalInfo(newStack, info.locals, 1);
for (int i=0; i< handlers.length; i++) {
if (handlers[i].getType() != null)
Main.getClassBundle().reachableClass
(handlers[i].getType());
infos[handlers[i].getCatcher().getBlockNr()]
.mergeBefore(catchInfo, jsrInfo, usedLocals);
}
}
Instruction[] instrs = block.getInstructions();
for (int idx = 0 ; idx < instrs.length; idx++) {
Instruction instr = instrs[idx];
info = handleOpcode(instr, info);
if (instr.isStore() && handlers.length > 0) {
int slot = instr.getLocalSlot();
ConstValue newValue = info.locals[slot];
for (int i=0; i< handlers.length; i++) {
infos[handlers[i].getCatcher().getBlockNr()]
.mergeOneLocal(slot, info.locals[slot]);
if (newValue.stackSize > 1)
infos[handlers[i].getCatcher().getBlockNr()]
.mergeOneLocal(slot+1, info.locals[slot+1]);
}
}
}
after = info;
propagateAfter();
}
public void dumpInfo(PrintWriter output) {
output.println("/-["+nr+"]-"+before);
if (constantFlow >= 0)
output.println("| constantFlow: "+constantFlow);
if (jsrInfo != null)
output.println("| used: "+usedLocals+" JSR: "+jsrInfo);
block.dumpCode(output);
output.println("\\-["+nr+"]-"+after);
}
public String toString() {
return "BlockAnalyzer["+nr+"]";
}
}
/**
* The TodoQueue is a linked list of BlockInfo
*
* There is only one TodoQueue, the modifiedQueue in analyzeCode
*
* The queue operations are in StackLocalInfo.
*/
static class TodoQueue {
BlockInfo first;
public void enqueue(BlockInfo info) {
if (info.nextTodo == null) {
info.nextTodo = first;
first = info;
}
}
public BlockInfo dequeue() {
BlockInfo result = first;
if (result != null) {
first = result.nextTodo;
result.nextTodo = null;
}
return result;
}
}
public void fieldNotConstant(FieldIdentifier fi) {
ConstValue value = (ConstValue) fieldDependencies.remove(fi);
if (value != null)
value.constantChanged();
fi.removeFieldListener(methodIdent);
fi.setNotConstant();
}
void handleReference(Reference ref, boolean isVirtual) {
Main.getClassBundle().reachableReference(ref, isVirtual);
}
void handleClass(String clName) {
int i = 0;
while (i < clName.length() && clName.charAt(i) == '[')
i++;
if (i < clName.length() && clName.charAt(i) == 'L') {
clName = clName.substring(i+1, clName.length()-1);
Main.getClassBundle().reachableClass(clName);
}
}
public ConstantAnalyzer() { public ConstantAnalyzer() {
} }
@ -1610,8 +1545,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
infos[i].dumpInfo(output); infos[i].dumpInfo(output);
} }
public void analyzeCode(MethodIdentifier methodIdent, public void analyzeCode(MethodIdentifier methodIdent, BasicBlocks bb) {
BasicBlocks bb) {
Block[] blocks = bb.getBlocks(); Block[] blocks = bb.getBlocks();
this.methodIdent = methodIdent; this.methodIdent = methodIdent;
this.bb = bb; this.bb = bb;
@ -1627,15 +1561,19 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
infos[startBlock.getBlockNr()].mergeBefore infos[startBlock.getBlockNr()].mergeBefore
(new StackLocalInfo(bb.getMaxStack(), bb.getMaxLocals(), (new StackLocalInfo(bb.getMaxStack(), bb.getMaxLocals(),
minfo.isStatic(), minfo.getType()), minfo.isStatic(), minfo.getType()),
new JsrInfo[0]); null, null);
BlockInfo info; BlockInfo info;
while ((info = modifiedQueue.dequeue()) != null) { while ((info = modifiedQueue.dequeue()) != null) {
System.err.println(info); // dumpBlockInfo(GlobalOptions.err);
// GlobalOptions.err.println("Analyzing: "+info);
info.analyze(); info.analyze();
} }
} }
// GlobalOptions.err.println("After Analyze");
// dumpBlockInfo(GlobalOptions.err);
BitSet reachableBlocks = new BitSet(); BitSet reachableBlocks = new BitSet();
for (int i=0; i< infos.length; i++) { for (int i=0; i< infos.length; i++) {
if (infos[i].isReachable()) if (infos[i].isReachable())
@ -1730,22 +1668,16 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
String[] pt = TypeSignature.getParameterTypes(ref.getType()); String[] pt = TypeSignature.getParameterTypes(ref.getType());
int arg = 0; int arg = 0;
if (instr.getOpcode() != opc_invokestatic)
newCode.add(Instruction.forOpcode(opc_pop));
else if (pt.length > 0) {
newCode.add(Instruction.forOpcode(TypeSignature.getTypeSize(pt[0])
+ opc_pop - 1));
arg++;
} else {
if (replacement != null)
newCode.add(replacement);
return;
}
for (int i=arg; i < pt.length; i++) int i = pt.length;
newCode.add(Instruction.forOpcode while (i > 0)
(TypeSignature.getTypeSize(pt[i]) newCode.add(Instruction.forOpcode(TypeSignature
.getTypeSize(pt[--i])
+ opc_pop - 1)); + opc_pop - 1));
if (instr.getOpcode() != opc_invokestatic)
newCode.add(Instruction.forOpcode(opc_pop));
break;
} }
default: default:
throw new InternalError("Unexpected opcode"); throw new InternalError("Unexpected opcode");
@ -1790,7 +1722,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
for (int idx = 0; idx < oldCode.length; idx++) { for (int idx = 0; idx < oldCode.length; idx++) {
Instruction instr = oldCode[idx]; Instruction instr = oldCode[idx];
ConstantInfo info = (ConstantInfo) constantInfos.remove(instr); ConstantInfo info = (ConstantInfo) constantInfos.remove(instr);
if ((info.flags & CONSTANT) != 0) { if (info != null && (info.flags & CONSTANT) != 0) {
Instruction ldcInstr = Instruction.forOpcode Instruction ldcInstr = Instruction.forOpcode
(info.constant instanceof Long (info.constant instanceof Long
|| info.constant instanceof Double || info.constant instanceof Double
@ -1800,7 +1732,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
(bb + ": Replacing " + instr (bb + ": Replacing " + instr
+ " with constant " + info.constant); + " with constant " + info.constant);
replaceWith(newCode, instr, ldcInstr); replaceWith(newCode, instr, ldcInstr);
} else if ((info.flags & CONSTANTFLOW) != 0) { } else if (info != null && (info.flags & CONSTANTFLOW) != 0) {
int succnr = ((Integer)info.constant).intValue(); int succnr = ((Integer)info.constant).intValue();
replaceWith(newCode, instr, null); replaceWith(newCode, instr, null);
if (GlobalOptions.verboseLevel > 2) if (GlobalOptions.verboseLevel > 2)
@ -1822,23 +1754,28 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
&& (Main.stripping & Main.STRIP_UNREACH) != 0 && (Main.stripping & Main.STRIP_UNREACH) != 0
&& !fi.isReachable()) { && !fi.isReachable()) {
replaceWith(newCode, instr, null); replaceWith(newCode, instr, null);
}
break; break;
} }
/* fall through */
}
default: default:
newCode.add(instr); newCode.add(instr);
} }
} }
} }
blocks[i].setCode((Instruction[]) newCode.toArray(oldCode), succs); blocks[i].setCode((Instruction[]) newCode.toArray(new Instruction[newCode.size()]), succs);
newBlockCtr++; newBlockCtr++;
} }
if (newBlockCtr < blocks.length) { if (newBlockCtr < blocks.length) {
Block[] newBlocks = new Block[newBlockCtr]; Block[] newBlocks = new Block[newBlockCtr];
Handler[] newHandlers = new Handler[newHandlerCtr];
System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr); System.arraycopy(blocks, 0, newBlocks, 0, newBlockCtr);
blocks = newBlocks;
}
if (newHandlerCtr < handlers.length) {
Handler[] newHandlers = new Handler[newHandlerCtr];
System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr); System.arraycopy(handlers, 0, newHandlers, 0, newHandlerCtr);
bb.setBlocks(newBlocks, newStartBlock, newHandlers); handlers = newHandlers;
} }
bb.setBlocks(blocks, newStartBlock, handlers);
} }
} }

@ -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