Documentation updates (INSTALL, javadoc).

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


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

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

@ -1,45 +1,23 @@
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
need the java runtime environment. Version 1.1 is quite old, I
recommend using Java 2 (jdk1.2 or above).
This package was designed to use the GNU standard for configuration
and makefiles. To build and install do the following:
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
"aclocal; automake -a; autoconf".
1). You need a java development kit (at least version 1.1), some unix
tools and some java packages. Make sure that you have all java
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
1.1. These packages are accessible from the following urls:
CYGWIN (unix tools for win95/NT):
http://sourceware.cygnus.com/cygwin/
JDK 1.1:
http://java.sun.com/products/jdk/1.1/index.htm
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.
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 ANT from the jakarta.apache.org
tools. I assume you have installed it correctly.
Take some time to edit config.props. There are a few options you need
to take care of. (Unfortunately ant can't test for executables).
Now you are ready to invoke ant. There are many possible targets, here
are the most useful ones:
all builds class files and documentation.
build builds class files only (autodetects java version).
build-1.1 builds JDK1.1 class files.
doc builds documentation.
dist creates all release files.
test does some self tests. You need to have junit installed for this.
clean cleans everything that doesn't belong to the source distribution.
cvsclean cleans everything that doesn't belong into the cvs repository.

@ -19,52 +19,75 @@
-->
<!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 -->
<property name="version" value="1.90"/>
<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="version" value="1.90-CVS"/>
<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="jcpp" value="${scripts}/jcpp.pl"/>
<property name="php2html" value="${scripts}/php2html.pl"/>
<property name="versionfile" value="${src}/jode/GlobalOptions.java"/>
<property file="config.props"/>
<path id="project.classpath">
<pathelement path="${classpath}"/>
<fileset dir="lib" includes="*.jar"/>
</path>
<!-- -->
<!-- ********* General targets ******* -->
<!-- compiles jode and creates its javadoc-files -->
<target name="all" depends="dist,javadoc"/>
<target name="all" depends="build,doc"/>
<!-- build the Jode files -->
<target name="build" depends="check-jdk,preconfig">
<mkdir dir="${build}"/>
<javac srcdir="${src}"
destdir="${build}"
classpathref="project.classpath">
<exclude name="net/sf/jode/obfuscator/" />
<exclude name="net/sf/jode/bytecode/*Subroutine*" />
</javac>
<copy todir="${build}">
<fileset dir="src"/>
</copy>
<!-- clean all -->
<target name="clean" depends="clean-jcpp,clean-build,clean-doc,clean-test,clean-dist"/>
<target name="cvsclean" depends="clean,clean-html"/>
<!-- ********* jcpp targets ******* -->
<target name="check-jcpp" unless="perl.present">
<fail message="need perl to configure for JDK 1.1"/>
</target>
<target name="build-1.1">
<property name="collections.package"
value="gnu.java.util.collections"/>
<property name="swing.package"
value="javax.swing"/>
<property name="jdk1.2+" value="false"/>
<antcall name="build"/>
<target name="run-jcpp" depends="check-packages,check-jcpp">
<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 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">
<available property="jdk1.2+" classname="java.lang.ThreadLocal" />
<available property="jdk1.3+" classname="java.lang.StrictMath" />
@ -93,91 +116,191 @@
value="com.sun.java.util.collections"
classname="com.sun.java.util.collections.Set"
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"
classname="com.sun.java.swing.JFrame"
classpathref="project.classpath" />
<available property="swing.package" value="javax.swing"
classname="javax.swing.JFrame"
classpathref="project.classpath" />
</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+">
<property name="src" value="src-1.1"/>
<antcall target="runjcpp"/>
<antcall target="run-jcpp"/>
</target>
<target name="preconfig.12" if="jdk1.2+">
<property name="src" value="src"/>
<antcall target="clean-jcpp"/>
</target>
<target name="runjcpp" depends="check-packages">
<copy todir="${src}">
<fileset dir="src"/>
<target name="build-1.1">
<property name="jdk1.1.forced" value="on"/>
<antcall target="build"/>
</target>
<target name="build" depends="check-jdk,preconfig">
<mkdir dir="${build}"/>
<javac srcdir="${src}"
destdir="${build}"
classpathref="project.classpath"
deprecation="on">
<!--
<exclude name="net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalOptimizer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java"/>
<exclude name="net/sf/jode/bytecode/*Subroutine*" />
-->
</javac>
<copy todir="${build}">
<fileset dir="${props}"/>
</copy>
<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>
<!-- create the jar-files -->
<target name="dist" depends="distclass,distdoc">
<jar jarfile="${dist}/jode-${version}.jar"
basedir="${dist}"
includes="jode.jar,doc.jar"/>
<!-- clean the class files -->
<target name="clean-build">
<delete dir="${build}"/>
</target>
<!-- ********* Create Jar files ******* -->
<target name="distclass" depends="build">
<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 name="dist-class" depends="build">
<mkdir dir="${dist}"/>
<jar jarfile="${dist}/jode.jar"
basedir="${build}"/>
</target>
<target name="distdoc" depends="javadoc">
<jar jarfile="${dist}/doc.jar"
basedir="${docs}"/>
<target name="dist-src">
<mkdir dir="${dist}"/>
<jar jarfile="${dist}/src.jar"
basedir="${src}"/>
</target>
<target name="clean-dist">
<delete dir="${dist}"/>
</target>
<!-- ********* Javadoc targets ********** -->
<target name="doc" depends="doc-javadoc,doc-html"/>
<target name="check-html">
<uptodate property="html.uptodate">
<srcfiles dir="${basedir}" includes="${doc}/*.php"/>
<mapper type="glob" from="*.php" to="*.html"/>
</uptodate>
</target>
<!-- javadoc -->
<target name="javadoc">
<mkdir dir="${docs}/api"/>
<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.*"
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>
<!-- ********* clean targets ************* -->
<target name="clean-doc">
<delete dir="${api.doc}"/>
</target>
<!-- clean all -->
<target name="clean" depends="cleanbuild,cleanjavadoc"/>
<target name="clean-html">
<delete>
<fileset dir="${doc}" includes="*.html"/>
</delete>
</target>
<!-- clean the class files -->
<target name="cleanbuild">
<delete dir="${build}"/>
<!-- ********* test targets ************* -->
<target name="build-test" depends="build">
<mkdir dir="${test.build}"/>
<javac srcdir="${test.src}"
destdir="${test.build}"
classpathref="project.classpath"
classpath="${build}"
deprecation="on">
</javac>
</target>
<!-- clean javadocs -->
<target name="cleanjavadoc">
<delete dir="${docs}/api"/>
<target name="test" 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="/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>
<!-- 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">
<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} ..."/>
<exec executable="perl">
<arg value="-i"/>
@ -185,13 +308,30 @@
<arg value='s/(String\s*version\s*=\s*")[^"]*/$1${version}/' />
<arg value="${versionfile}"/>
</exec>
</target>
<!--
commit the new $(VERSIONFILE) to the CVS
<target name="commit" depends="setversion,test-cvs" if="version">
<antcall target="cvsclean"/>
<echo message="---------------------------------------------------"/>
<echo message=' Commiting 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.
-->
<!-- commit the new $(VERSIONFILE) to the CVS
<echo message="commiting updated file to CVS..."/>
<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="---------------------------------------------------"/>
</target>

@ -1,6 +1,6 @@
#!/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
# 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
# branch was commented out it will get commented in.
#
# jcpp also definitions, useful for package renaming. The java file
# must look like this:
# jcpp can also change definitions, useful for package renaming. The
# java file should look like this:
#
# ///#def COLLECTIONS java.util
# import java.util.Vector

@ -75,6 +75,9 @@ class BasicBlockReader implements Opcodes {
BasicBlocks bb;
Block[] blocks;
int maxStack;
int maxLocals;
public BasicBlockReader(BasicBlocks bb) {
this.bb = bb;
}
@ -298,13 +301,20 @@ class BasicBlockReader implements Opcodes {
if (start != -1)
convertBlock(start, count);
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,
DataInputStream input) throws IOException {
bb.maxStack = input.readUnsignedShort();
int maxLocals = input.readUnsignedShort();
bb.maxLocals = maxLocals;
maxStack = input.readUnsignedShort();
maxLocals = input.readUnsignedShort();
int codeLength = input.readInt();
infos = new InstrInfo[codeLength];

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

@ -26,6 +26,8 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.BitSet;
import java.util.Stack;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Arrays;
@ -68,8 +70,9 @@ import java.lang.UnsupportedOperationException;
* necessary, you don't have to care about that.</p>
*
* @see net.sf.jode.bytecode.Block
* @see net.sf.jode.bytecode.Instruction */
public class BasicBlocks extends BinaryInfo {
* @see net.sf.jode.bytecode.Instruction
*/
public class BasicBlocks extends BinaryInfo implements Opcodes {
/**
* The method info which contains the basic blocks.
@ -153,12 +156,84 @@ public class BasicBlocks extends BinaryInfo {
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) {
maxLocals = ml;
if (startBlock == null)
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,
@ -173,15 +248,18 @@ public class BasicBlocks extends BinaryInfo {
for (int j = 0; j < handlers.length; j++) {
if (handlers[j].getStart() == blocks[i])
activeHandlers.add(handlers[j]);
if (handlers[j].getEnd() == blocks[i])
activeHandlers.remove(handlers[j]);
}
if (activeHandlers.size() == 0)
blocks[i].catchers = Handler.EMPTY;
else
blocks[i].catchers =
(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);
}
@ -212,7 +290,7 @@ public class BasicBlocks extends BinaryInfo {
dumpCode(GlobalOptions.err);
}
void readAttribute(String name, int length, ConstantPool cp,
protected void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL
@ -256,15 +334,17 @@ public class BasicBlocks extends BinaryInfo {
BasicBlockWriter bbw;
void prepareWriting(GrowableConstantPool gcp) {
bbw = new BasicBlockWriter(this, gcp);
prepareAttributes(gcp);
}
int getKnownAttributeCount() {
return bbw.getAttributeCount();
protected int getAttributeCount() {
return super.getAttributeCount() + bbw.getAttributeCount();
}
void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
super.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
* standard attributes.</p>
*
* <p>One application of this attributes are installation classes.
* These classes have a special attribute containing a zip of the
* <p>You can provide new attributes by overriding the protected
* 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,
* e.g. putting native machine code for some architectures into the
* class.</p>
@ -70,14 +77,22 @@ public class BinaryInfo {
}
}
int getKnownAttributeCount() {
return 0;
}
void readAttribute(String name, int length,
ConstantPool constantPool,
DataInputStream input,
int howMuch) throws IOException {
/**
* 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.
* @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,
DataInputStream input,
int howMuch) throws IOException {
byte[] data = new byte[length];
input.readFully(data);
if (howMuch >= ClassInfo.ALL) {
@ -156,12 +171,37 @@ public class BinaryInfo {
}
}
void drop(int keep) {
/**
* Drops information from this info. Override this to drop your
* own info and don't forget to call the method of the super class.
* @param howMuch the constant that was given to the {@link
* ClassInfo#drop} function when loading this class.
*/
protected void drop(int keep) {
if (keep < ClassInfo.ALL)
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)
return;
Iterator i = unknownAttributes.keySet().iterator();
@ -169,19 +209,27 @@ public class BinaryInfo {
gcp.putUTF8((String) i.next());
}
void writeKnownAttributes
(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
}
void writeAttributes
/**
* <p>Writes the attributes to the output stream.
* 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}
* 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,
DataOutputStream output) throws IOException {
int count = getKnownAttributeCount();
if (unknownAttributes != null)
count += unknownAttributes.size();
int count = getAttributeCount();
output.writeShort(count);
writeKnownAttributes(constantPool, output);
if (unknownAttributes != null) {
Iterator i = unknownAttributes.entrySet().iterator();
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 */
if (unknownAttributes != null) {
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.
* @return the contents of the attribute, null if not found.
* @see #readAttribute
*/
public byte[] findAttribute(String name) {
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() {
if (unknownAttributes != null)
@ -226,19 +292,24 @@ public class BinaryInfo {
}
/**
* Adds a new non standard attribute or replaces an old one with the
* same name.
* Adds a new non standard attribute or replaces an old one with
* 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 contents the new contens.
*/
public void setAttribute(String name, byte[] contents) {
public void addAttribute(String name, byte[] contents) {
if (unknownAttributes == null)
unknownAttributes = new SimpleMap();
unknownAttributes.put(name, contents);
}
/**
* Removes a new non standard attribute.
* Removes a non standard attributes.
* @param name the name 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() {
unknownAttributes = null;
}
}

@ -92,28 +92,34 @@ public final class Block {
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() {
instrs = new Instruction[0];
succs = null;
}
/**
* Gets the list of instructions. The returned list should not be
* modified, except that the instructions (but not their opcodes)
@ -134,12 +140,13 @@ public final class Block {
}
/**
* Gets the exception handlers which try part contains this block.
* You can't set them since they are calculated automatically.
* Gets the exception handlers whose try region contains this
* block. You can't set them since they are calculated
* automatically.
* @return the exception handlers.
* @see BasicBlocks#setExceptionHandlers
* @see BasicBlocks#setBlocks
*/
public Handler[] getCatchers() {
public Handler[] getHandlers() {
return catchers;
}
@ -158,15 +165,18 @@ public final class Block {
private void initCode() {
int size = instrs.length;
maxpop = maxpush = 0;
int depth = 0;
int poppush[] = new int[2];
boolean needGoto = true;
for (int i = 0; i < size; i++) {
instrs[i].getStackPopPush(poppush);
depth += poppush[0];
if (pop < depth)
pop = depth;
depth -= poppush[1];
depth -= poppush[0];
if (maxpop < -depth)
maxpop = -depth;
depth += poppush[1];
if (maxpush < depth)
maxpush = depth;
int opcode = instrs[i].getOpcode();
switch (opcode) {
@ -176,7 +186,7 @@ public final class Block {
case Opcodes.opc_lookupswitch:
if (succs.length != instrs[i].getValues().length + 1)
throw new IllegalArgumentException
("no successors for switch");
("number of successors for switch doesn't match");
if (i != size - 1)
throw new IllegalArgumentException
("switch in the middle!");
@ -217,14 +227,14 @@ public final class Block {
needGoto = false;
}
}
push = pop - depth;
delta = depth;
if (needGoto && succs.length != 1)
throw new IllegalArgumentException("no single successor block");
}
public void getStackPopPush (int[] poppush) {
poppush[0] = pop;
poppush[1] = push;
poppush[0] = maxpop;
poppush[1] = delta + maxpop;
return;
}
@ -235,12 +245,7 @@ public final class Block {
public void setCode(Instruction[] instrs, Block[] succs) {
this.instrs = instrs;
this.succs = succs;
try {
initCode();
} catch (IllegalArgumentException ex) {
dumpCode(net.sf.jode.GlobalOptions.err);
throw ex;
}
initCode();
}
public void dumpCode(PrintWriter output) {

@ -59,13 +59,13 @@ import java.lang.reflect.Modifier;
* now have two different possibilities to fill it with informations:
* You load the class from its classpath (from which it was created)
* 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>
* Even if the class is already filled with information you can change
* it. You can, for example, set another array of methods, change the
* modifiers, or rename the class. Use the various
* <code>setXXX</code> methods.
* <code>setSomething</code> methods.
*
* <h3>The components of a class</h3>
* A class consists of several components:
@ -79,7 +79,7 @@ import java.lang.reflect.Modifier;
* <dt>class name</dt><dd>
* The short java name of this class, i.e. the name that appears
* 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
* change the bytecode name.<br>
*
@ -121,22 +121,17 @@ import java.lang.reflect.Modifier;
* Fields are represented as {@link MethodInfo} objects.
* </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>
* <dt>outer class</dt><dd>
* the class of which this class is the inner. It returns null for
* top level classes and for method scoped classes. <br>
* the class of which this class is the inner class. It returns
* null for package scoped and method scoped classes. <br>
* </dd>
* <dt>classes</dt><dd>
* the inner classes which is an array of ClassInfo. This doesn't
* include method scoped classes.<br>
* </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>
* The name of source file. The JVM uses this field when a stack
* trace is produced.
@ -155,16 +150,16 @@ import java.lang.reflect.Modifier;
* <code>getOuterClass()</code> returns <code>null</code> and
* <code>isMethodScoped()</code> returns <code>false</code>.
* </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
* <code>getOuterClass()</code> returns not <code>null</code>.
*
* 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
* InnerClass attribute is present. The method
* <code>getClassName()</code> returns the name of the inner class
* (<code>Inner</code> in the above example).
* (<code>Inner</code> in the above example).
*
* You can get all inner classes of a class with the
* <code>getClasses</code> method.
@ -177,12 +172,13 @@ import java.lang.reflect.Modifier;
* too.<br><br>
*
* 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
* InnerClass attribute is present. <br><br>
*
* 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>
* <dt>anonymous classes</dt><dd>
* 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>,
* too.<br><br>
*
* The bytecode name (<code>getName()</code>) of an method scoped class is
* most times of the form <code>Package.Outer$Number</code>. But
* ClassInfo also supports differently named classes, as long as the
* InnerClass attribute is present. <br><br>
* The bytecode name (<code>getName()</code>) of an method scoped
* class is in most cases of the form
* <code>Package.Outer$Number</code>. But ClassInfo also supports
* 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
* 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>
* </dl>
*
* <hr>
* <h3>open questions...</h3>
* <h3>Open Questions</h3>
*
* I represent most types as <code>java/lang/String</code> (type
* 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
* the method be in ClassInfo (I don't think so), should an instance
* 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>
*
* Should load(HIERARCHY) also try to load hierarchy of super class.
* 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 */
* @author Jochen Hoenicke
*/
public final class ClassInfo extends BinaryInfo implements Comparable {
private static ClassPath defaultClasspath;
@ -245,6 +239,8 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private String sourceFile;
private boolean hasInnerClassesAttr;
private final static ClassInfo[] EMPTY_INNER = new ClassInfo[0];
/**
* This constant can be used as parameter to drop. It specifies
* that no information at all should be kept for the current class.
@ -419,10 +415,24 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (status >= OUTERCLASS) {
if ((className == null
? this.className != null : !className.equals(this.className))
|| this.outerClass != outer)
throw new ClassFormatException("Outer information mismatch "
+name+": "+className+","+outer+","+ms+"<->"+this.className +","+this.outerClass+","+this.methodScoped);
mergeModifiers(realModifiers);
|| this.outerClass != outer) {
/* Ignore errors when merging, some obfuscator may have
* 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);
} else {
if (realModifiers != -1)
mergeModifiers(realModifiers);
@ -438,37 +448,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throws IOException
{
/* The InnerClasses attribute is transformed in a special way
* so we want to taker a closer look. According to the inner
* class specification,
*
* http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814
*
* there are several InnerClass records in a class. We differ
* three different types:
*
* The InnerClass records for our own inner classes: They can
* easily be recognized, since this class must be mentioned in
* the outer_class_info_index field.
*
* The InnerClass records for the outer class and its outer
* classes, and so on: According to the spec, these must
* always be present if this is a class scoped class. And they
* must be in reversed order, i.e. the outer most class comes
* first.
* so we want to taker a closer look. According to the 2nd
* edition of the vm specification (InnerClasses attribute),
*
* 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.
* http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#79996
*
* 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.
* there is a InnerClass record for each non package scope
* class referenced in this class. We are only interested in
* out own entry and in the entries for our inner classes.
* The latter can easily be recognized, since this class must
* be mentioned in the outer_class_info_index field.
*/
hasInnerClassesAttr = true;
@ -478,14 +467,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
throw new ClassFormatException
("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
* 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 outerIndex = input.readUnsignedShort();
int nameIndex = input.readUnsignedShort();
@ -497,12 +486,17 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (innername != null && innername.length() == 0)
innername = null;
/* Some compilers give method scope classes a valid
* outer field, but we mustn't handle them as inner
* classes. The best way to distinguish this case
* is by the class name.
/* Some compilers give method scope and anonymous classes
* a valid outer field, but we mustn't handle them as
* inner classes.
*/
if (outer != null && innername != null
if (innername == null)
outer = null;
/* The best way to distinguish method scope classes is by thier
* class name.
*/
if (outer != null
&& inner.length() > outer.length() + 2 + innername.length()
&& inner.startsWith(outer+"$")
&& inner.endsWith("$"+innername)
@ -510,45 +504,13 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
outer = null;
ClassInfo innerCI = classpath.getClassInfo(inner);
ClassInfo outerCI = null;
if (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;
ClassInfo outerCI = outer != null
? classpath.getClassInfo(outer) : null;
innerCI.mergeOuterInfo(innername, outerCI,
access, outerCI == null);
if (outerCI == this)
innerExtra[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;
}
innerCIs[innerCount++] = innerCI;
}
/* Now inner classes are at the front of the array in correct
@ -557,32 +519,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/
if (innerCount > 0) {
innerClasses = new ClassInfo[innerCount];
for (int i=0; i < innerCount; i++)
innerClasses[i] = innerExtra[i];
System.arraycopy(innerCIs, 0, innerClasses, 0, innerCount);
} else
innerClasses = null;
/* 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);
}
}
}
innerClasses = EMPTY_INNER;
}
void readAttribute(String name, int length,
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
protected void readAttribute(String name, int length,
ConstantPool cp,
DataInputStream input,
int howMuch) throws IOException {
if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) {
if (length != 2)
throw new ClassFormatException("SourceFile attribute"
@ -642,7 +587,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
(clazz.getSuperclass().getName());
Class[] ifaces = clazz.getInterfaces();
interfaces = new ClassInfo[ifaces.length];
for (int i=0; i<ifaces.length; i++)
for (int i = 0; i<ifaces.length; i++)
interfaces[i] = classpath.getClassInfo(ifaces[i].getName());
}
if (howMuch >= PUBLICDECLARATIONS) {
@ -707,7 +652,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
innerClasses[i].loadFromReflection(is[i], OUTERCLASS);
}
} else
innerClasses = null;
innerClasses = EMPTY_INNER;
}
status = howMuch;
}
@ -761,11 +706,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
String className = cpool.getClassName(input.readUnsignedShort());
if (!name.equals(className))
throw new ClassFormatException("wrong name " + className);
String superName = cpool.getClassName(input.readUnsignedShort());
superclass = superName != null ? classpath.getClassInfo(superName) : null;
int superID = input.readUnsignedShort();
superclass = superID == 0 ? null
: classpath.getClassInfo(cpool.getClassName(superID));
int count = input.readUnsignedShort();
interfaces = new ClassInfo[count];
for (int i=0; i< count; i++) {
for (int i = 0; i < count; i++) {
interfaces[i] = classpath.getClassInfo
(cpool.getClassName(input.readUnsignedShort()));
}
@ -775,14 +721,14 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort();
fields = new FieldInfo[count];
for (int i=0; i< count; i++) {
for (int i = 0; i < count; i++) {
fields[i] = new FieldInfo();
fields[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
@ -792,29 +738,45 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
int count = input.readUnsignedShort();
methods = new MethodInfo[count];
for (int i=0; i< count; i++) {
for (int i = 0; i < count; i++) {
methods[i] = new MethodInfo();
methods[i].read(cpool, input, howMuch);
}
} else {
byte[] skipBuf = new byte[6];
int count = input.readUnsignedShort();
for (int i=0; i< count; i++) {
for (int i = 0; i < count; i++) {
input.readFully(skipBuf); // modifier, name, type
skipAttributes(input);
}
}
/* initialize inner classes to empty array, in case there
* is no InnerClasses attribute.
*/
innerClasses = EMPTY_INNER;
/* attributes */
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;
}
private void reserveSmallConstants(GrowableConstantPool gcp) {
for (int i=0; i < fields.length; i++)
for (int i = 0; i < fields.length; i++)
fields[i].reserveSmallConstants(gcp);
for (int i=0; i < methods.length; i++)
for (int i = 0; i < methods.length; i++)
methods[i].reserveSmallConstants(gcp);
}
@ -823,16 +785,16 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
private void prepareWriting(GrowableConstantPool gcp) {
gcp.putClassName(name);
gcp.putClassName(superclass.name);
for (int i=0; i < interfaces.length; i++)
for (int i = 0; i < interfaces.length; i++)
gcp.putClassName(interfaces[i].name);
for (int i=0; i < fields.length; i++)
for (int i = 0; i < fields.length; i++)
fields[i].prepareWriting(gcp);
for (int i=0; i < methods.length; i++)
for (int i = 0; i < methods.length; i++)
methods[i].prepareWriting(gcp);
for (int i=0; i < innerClasses.length; i++)
for (int i = 0; i < innerClasses.length; i++)
gcp.putClassName(innerClasses[i].name);
if (sourceFile != null) {
@ -845,40 +807,29 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* edition of the JVM specification.
*/
hasInnerClassesAttr = false;
for (int i = 1; i < gcp.size(); i++) {
if (gcp.tags[i] == gcp.CLASS) {
String clName;
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) {
GlobalOptions.err.println
("WARNING: "+ ci.name
+ "'s outer class isn't known.");
} else {
if ((ci.outerClass != null || ci.methodScoped)
&& ! hasInnerClassesAttr) {
gcp.putUTF8("innerClasses");
hasInnerClassesAttr = true;
}
if (ci.outerClass != null)
gcp.putClassName(ci.outerClass.name);
if (ci.className != null)
gcp.putUTF8(ci.className);
}
Iterator iter = gcp.iterateClassNames();
while (iter.hasNext()) {
ClassInfo ci = classpath.getClassInfo((String) iter.next());
if (ci.status < OUTERCLASS) {
GlobalOptions.err.println
("WARNING: " + ci.name + "'s outer class isn't known.");
} else {
if ((ci.outerClass != null || ci.methodScoped)
&& ! hasInnerClassesAttr) {
gcp.putUTF8("InnerClasses");
hasInnerClassesAttr = true;
}
if (ci.outerClass != null)
gcp.putClassName(ci.outerClass.name);
if (ci.className != null)
gcp.putUTF8(ci.className);
}
}
prepareAttributes(gcp);
}
int getKnownAttributeCount() {
int count = 0;
protected int getAttributeCount() {
int count = super.getAttributeCount();
if (sourceFile != null)
count++;
if (hasInnerClassesAttr)
@ -886,9 +837,10 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
return count;
}
void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
super.writeAttributes(gcp, output);
if (sourceFile != null) {
output.writeShort(gcp.putUTF8("SourceFile"));
output.writeInt(2);
@ -896,28 +848,19 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
}
List outers = new ArrayList();
for (int i = 0; i < gcp.size(); i++) {
if (gcp.tags[i] == gcp.CLASS) {
String clName;
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('/','.'));
while (ci.status >= OUTERCLASS
&& ci.outerClass != null || ci.methodScoped) {
/* Order is important so remove ci if it
* already exists and add it to the end. This
* way the outermost classes go to the end.
*/
outers.remove(ci);
outers.add(ci);
ci = ci.outerClass;
}
}
Iterator iter = gcp.iterateClassNames();
while (iter.hasNext()) {
ClassInfo ci = classpath.getClassInfo((String) iter.next());
while (ci != null
&& ci.status >= OUTERCLASS
&& (ci.outerClass != null || ci.methodScoped)) {
/* Order is important so remove ci if it
* already exists and add it to the end. This
* way the outermost classes go to the end.
*/
outers.remove(ci);
outers.add(ci);
ci = ci.outerClass;
}
}
if (hasInnerClassesAttr) {
@ -926,9 +869,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
output.writeInt(2 + count * 8);
output.writeShort(count);
ListIterator iter = outers.listIterator(count);
while (iter.hasPrevious()) {
ClassInfo ci = (ClassInfo) iter.previous();
ListIterator listiter = outers.listIterator(count);
while (listiter.hasPrevious()) {
ClassInfo ci = (ClassInfo) listiter.previous();
output.writeShort(gcp.putClassName(ci.name));
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.
*/
public void write(DataOutputStream out) throws IOException {
if (status <= ALL)
if (status < ALL)
throw new IllegalStateException("state is "+status);
GrowableConstantPool gcp = new GrowableConstantPool();
@ -967,15 +910,15 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
out.writeShort(gcp.putClassName(name));
out.writeShort(gcp.putClassName(superclass.getName()));
out.writeShort(interfaces.length);
for (int i=0; i < interfaces.length; i++)
for (int i = 0; i < interfaces.length; i++)
out.writeShort(gcp.putClassName(interfaces[i].getName()));
out.writeShort(fields.length);
for (int i=0; i < fields.length; i++)
for (int i = 0; i < fields.length; i++)
fields[i].write(gcp, out);
out.writeShort(methods.length);
for (int i=0; i < methods.length; i++)
for (int i = 0; i < methods.length; i++)
methods[i].write(gcp, out);
writeAttributes(gcp, out);
@ -1018,11 +961,6 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
* Guess the contents of a class. It
* @param howMuch The amount of information that should be read, e.g.
* <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 #HIERARCHY
* @see #PUBLICDECLARATIONS
@ -1074,7 +1012,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (howMuch >= PUBLICDECLARATIONS) {
methods = new MethodInfo[0];
fields = new FieldInfo[0];
innerClasses = new ClassInfo[0];
innerClasses = EMPTY_INNER;
}
status = howMuch;
}
@ -1117,9 +1055,9 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
*/
keep = DECLARATIONS;
for (int i=0; i < fields.length; i++)
for (int i = 0; i < fields.length; i++)
fields[i].drop(keep);
for (int i=0; i < methods.length; i++)
for (int i = 0; i < methods.length; i++)
methods[i].drop(keep);
}
@ -1201,7 +1139,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public FieldInfo findField(String name, String typeSig) {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
for (int i=0; i< fields.length; i++)
for (int i = 0; i < fields.length; i++)
if (fields[i].getName().equals(name)
&& fields[i].getType().equals(typeSig))
return fields[i];
@ -1211,7 +1149,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
public MethodInfo findMethod(String name, String typeSig) {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
for (int i=0; i< methods.length; i++)
for (int i = 0; i < methods.length; i++)
if (methods[i].getName().equals(name)
&& methods[i].getType().equals(typeSig))
return methods[i];
@ -1260,6 +1198,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
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() {
if (status < PUBLICDECLARATIONS)
throw new IllegalStateException("status is "+status);
@ -1270,53 +1214,73 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
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) {
/* 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();
status = ALL;
modified = true;
}
public void setSuperclass(ClassInfo newSuper) {
superclass = newSuper;
status = ALL;
modified = true;
}
public void setInterfaces(ClassInfo[] newIfaces) {
interfaces = newIfaces;
status = ALL;
modified = true;
}
public void setModifiers(int newModifiers) {
modifiers = newModifiers;
status = ALL;
modified = true;
}
public void setMethods(MethodInfo[] mi) {
methods = mi;
status = ALL;
modified = true;
}
public void setFields(FieldInfo[] fi) {
fields = fi;
status = ALL;
modified = true;
}
public void setOuterClass(ClassInfo oc) {
outerClass = oc;
status = ALL;
modified = true;
}
public void setMethodScoped(boolean ms) {
methodScoped = ms;
status = ALL;
modified = true;
}
public void setClasses(ClassInfo[] ic) {
innerClasses = ic;
innerClasses = ic.length == 0 ? EMPTY_INNER : ic;
status = ALL;
modified = true;
}
public void setSourceFile(String newSource) {
sourceFile = newSource;
status = ALL;
modified = true;
}
@ -1363,12 +1327,12 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone();
Arrays.sort(interfaces);
for (int i=0; i < interfaces.length; i++)
for (int i = 0; i < interfaces.length; i++)
out.writeUTF(interfaces[i].name);
FieldInfo[] fields = (FieldInfo[]) this.fields.clone();
Arrays.sort(fields);
for (int i=0; i < fields.length; i++) {
for (int i = 0; i < fields.length; i++) {
modifs = fields[i].getModifiers();
if ((modifs & Modifier.PRIVATE) != 0
&& (modifs & (Modifier.STATIC
@ -1383,7 +1347,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
MethodInfo[] methods = (MethodInfo[]) this.methods.clone();
Arrays.sort(methods);
for (int i=0; i < methods.length; i++) {
for (int i = 0; i < methods.length; i++) {
modifs = methods[i].getModifiers();
/* The modifiers of <clinit> should be just static,
* but jikes also marks it final.
@ -1405,7 +1369,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
byte[] sha = md.digest();
long result = 0;
for (int i=0; i < 8; i++) {
for (int i = 0; i < 8; i++) {
result += (long)(sha[i] & 0xFF) << (8 * i);
}
return result;
@ -1461,7 +1425,7 @@ public final class ClassInfo extends BinaryInfo implements Comparable {
if (clazz.status < HIERARCHY)
clazz.load(HIERARCHY);
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i< ifaces.length; i++) {
for (int i = 0; i < ifaces.length; i++) {
if (implementedBy(ifaces[i]))
return true;
}

@ -684,6 +684,26 @@ public class ClassPath {
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
* path.
@ -752,7 +772,7 @@ public class ClassPath {
* @param fqn the full qualified name. The components should be dot
* separated.
* @return true, if filename exists and is a package, false otherwise.
* @see isDirectory
* @see #isDirectory
*/
public boolean isPackage(String fqn) {
return isDirectory(fqn.replace('.', '/'));

@ -21,12 +21,22 @@ package net.sf.jode.bytecode;
import java.io.DataInputStream;
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
*/
class ConstantPool {
public class ConstantPool {
public final static int CLASS = 7;
public final static int FIELDREF = 9;
public final static int METHODREF = 10;
@ -106,16 +116,12 @@ class ConstantPool {
}
public String getUTF8(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != UTF8)
throw new ClassFormatException("Tag mismatch");
return (String)constants[i];
}
public Reference getRef(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != FIELDREF
&& tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF)
throw new ClassFormatException("Tag mismatch");
@ -156,8 +162,6 @@ class ConstantPool {
}
public String getClassType(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]);
@ -169,13 +173,10 @@ class ConstantPool {
} catch (IllegalArgumentException ex) {
throw new ClassFormatException(ex.getMessage());
}
return clName;
}
public String getClassName(int i) throws ClassFormatException {
if (i == 0)
return null;
if (tags[i] != CLASS)
throw new ClassFormatException("Tag mismatch");
String clName = getUTF8(indices1[i]);
@ -187,6 +188,43 @@ class ConstantPool {
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) {
switch (tags[i]) {
case CLASS:
@ -225,7 +263,7 @@ class ConstantPool {
}
public String toString() {
StringBuffer result = new StringBuffer("[ null");
StringBuffer result = new StringBuffer("ConstantPool[ null");
for (int i=1; i< count; i++) {
result.append(", ").append(i).append(" = ").append(toString(i));
}

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

@ -26,6 +26,38 @@ import java.lang.reflect.Modifier;
import java.lang.Comparable;
///#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 {
int modifier;
String name;
@ -45,8 +77,9 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
this.modifier = modifier;
}
void readAttribute(String name, int length, ConstantPool cp,
DataInputStream input, int howMuch) throws IOException {
protected void readAttribute
(String name, int length, ConstantPool cp,
DataInputStream input, int howMuch) throws IOException {
if (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) {
basicblocks = new BasicBlocks(this);
basicblocks.read(cp, input, howMuch);
@ -105,8 +138,8 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
prepareAttributes(gcp);
}
int getKnownAttributeCount() {
int count = 0;
protected int getAttributeCount() {
int count = super.getAttributeCount();
if (basicblocks != null)
count++;
if (exceptions != null)
@ -118,9 +151,10 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
return count;
}
void writeKnownAttributes(GrowableConstantPool gcp,
DataOutputStream output)
protected void writeAttributes(GrowableConstantPool gcp,
DataOutputStream output)
throws IOException {
super.writeAttributes(gcp, output);
if (basicblocks != null) {
output.writeShort(gcp.putUTF8("Code"));
basicblocks.write(gcp, output);
@ -144,14 +178,14 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
}
void write(GrowableConstantPool constantPool,
DataOutputStream output) throws IOException {
DataOutputStream output) throws IOException {
output.writeShort(modifier);
output.writeShort(constantPool.putUTF8(name));
output.writeShort(constantPool.putUTF8(typeSig));
writeAttributes(constantPool, output);
}
void drop(int keep) {
protected void drop(int keep) {
if (keep < ClassInfo.DECLARATIONS)
exceptions = null;
if (keep < ClassInfo.NODEBUG)
@ -260,8 +294,12 @@ public final class MethodInfo extends BinaryInfo implements Comparable {
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() {
return "Method "+Modifier.toString(modifier)+" "+
typeSig + " " + name;
return "MethodInfo[name="+name+",type="+typeSig+"]";
}
}

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

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

@ -18,7 +18,7 @@
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-->
<title>JODE Bytecode Package</title>
<title>JODE Bytecode Package</title>
</head>
<body>
@ -29,13 +29,9 @@ class and get a ClassInfo object. As third step you can actually load
the class.<br><br>
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
package may change in the future in incompatible ways. 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>
about updates, help you with problems, etc. <b> WARNING: </b> Some
parts of this package may change in the future in incompatible ways.
Ask me for more information. <br><br>
Here is a short example, how you can use this package, see the
documentation of the classes for more details.
@ -55,7 +51,7 @@ documentation of the classes for more details.
}
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();
if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE)
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>
<!-- Created: Thu Jun 22 16:56:39 MET DST 2000 -->
<!-- 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 -->
</body>
</html>

@ -203,9 +203,6 @@ public class Decompiler {
* @param progress A progress listener (see below). Null if you
* don't need information about progress.
* @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 RuntimeException If jode has a bug ;-)
*/

@ -20,6 +20,7 @@
package net.sf.jode.decompiler;
import net.sf.jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.ClassPath;
import net.sf.jode.bytecode.ClassFormatException;
import net.sf.jode.GlobalOptions;
import java.io.BufferedOutputStream;
@ -172,6 +173,10 @@ public class Main extends Options {
GlobalOptions.err.println
("Check the class path ("+classPathStr+
") 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) {
GlobalOptions.err.println
("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
* 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>
* @version 1.0 */

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

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

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

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

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

@ -357,6 +357,15 @@ public class ClassIdentifier extends Identifier {
}
} else {
// 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();
for (int i=0; i< topmethods.length; i++) {
// all virtual methods in superclass may be
@ -433,14 +442,12 @@ public class ClassIdentifier extends Identifier {
info.setSourceFile(null);
}
if ((Main.stripping & Main.STRIP_INNERINFO) != 0) {
info.setClasses(null);
info.setClasses(new ClassInfo[0]);
info.setOuterClass(null);
info.setExtraClasses(null);
}
// load inner classes
ClassInfo outerClass = info.getOuterClass();
ClassInfo[] innerClasses = info.getClasses();
ClassInfo[] extraClasses = info.getExtraClasses();
if (outerClass != null)
Main.getClassBundle().getClassIdentifier(outerClass.getName());
@ -450,13 +457,6 @@ public class ClassIdentifier extends Identifier {
.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());
if (outerIdent != null && outerIdent.isReachable())
break;
outerClass = outerClass.getOuterClass();
}
}
info.setOuterClass(outerClass);
@ -533,44 +534,13 @@ public class ClassIdentifier extends Identifier {
}
}
if (newInnerCount == 0) {
info.setClasses(null);
} else {
ClassInfo[] newInners = new ClassInfo[newInnerCount];
int pos = 0;
for (int i=0; i<innerClasses.length; i++) {
if (innerClasses[i] != null)
newInners[pos++] = innerClasses[i];
}
info.setClasses(newInners);
}
}
ClassInfo[] extraClasses = info.getExtraClasses();
if (extraClasses != null) {
int newExtraCount = extraClasses.length;
if ((Main.stripping & Main.STRIP_UNREACH) != 0) {
for (int i=0; i < extraClasses.length; i++) {
ClassIdentifier extraIdent = Main.getClassBundle()
.getClassIdentifier(extraClasses[i].getName());
if (extraIdent != null && !extraIdent.isReachable()) {
extraClasses[i] = null;
newExtraCount--;
}
}
}
if (newExtraCount == 0) {
info.setExtraClasses(null);
} else {
ClassInfo[] newExtras = new ClassInfo[newExtraCount];
int pos = 0;
for (int i=0; i<extraClasses.length; i++) {
if (extraClasses[i] != null)
newExtras[pos++] = extraClasses[i];
}
info.setExtraClasses(newExtras);
ClassInfo[] newInners = new ClassInfo[newInnerCount];
int pos = 0;
for (int i=0; i<innerClasses.length; i++) {
if (innerClasses[i] != null)
newInners[pos++] = innerClasses[i];
}
info.setClasses(newInners);
}
}

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

@ -19,6 +19,7 @@
package net.sf.jode.obfuscator.modules;
import net.sf.jode.bytecode.Opcodes;
import net.sf.jode.bytecode.ClassPath;
import net.sf.jode.bytecode.ClassInfo;
import net.sf.jode.bytecode.BasicBlocks;
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.GlobalOptions;
import java.io.IOException;
///#def COLLECTIONS java.util
import java.util.ArrayList;
import java.util.Iterator;
@ -38,9 +40,25 @@ import java.util.ListIterator;
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();
Identifier ident = Main.getClassBundle().getIdentifier(ref);
ClassPath classPath = Main.getClassBundle().getClassPath();
String clName = ref.getClazz();
String realClazzName;
if (ident != null) {
@ -56,13 +74,21 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
/* Arrays don't define new methods (well clone(),
* but that can be ignored).
*/
clazz = ClassInfo.forName("java.lang.Object");
clazz = classPath.getClassInfo("java.lang.Object");
} else {
clazz = ClassInfo.forName
clazz = classPath.getClassInfo
(clName.substring(1, clName.length()-1)
.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
&& clazz.findMethod(ref.getName(),
ref.getType()) == null)

@ -191,6 +191,9 @@ public class UnifyHash extends AbstractCollection {
///#endif
return new Iterator() {
private int known = modCount;
private boolean removeOk = false;
private Bucket removeBucket = null;
private Bucket prevBucket = null;
private Bucket nextBucket
= buckets[Math.abs(hash % buckets.length)];
private Object nextVal;
@ -206,7 +209,7 @@ public class UnifyHash extends AbstractCollection {
if (nextVal != null)
return;
}
prevBucket = nextBucket;
nextBucket = nextBucket.next;
}
}
@ -221,13 +224,26 @@ public class UnifyHash extends AbstractCollection {
if (nextBucket == null)
throw new NoSuchElementException();
Object result = nextVal;
removeBucket = prevBucket;
removeOk = true;
prevBucket = nextBucket;
nextBucket = nextBucket.next;
internalNext();
return result;
}
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;
}
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) {
///#ifdef JDK12
cleanUp();

Loading…
Cancel
Save