-
+
+
+ | Powered by Best viewed with
|
+
- |
-
+
diff --git a/jode/doc/history.html b/jode/doc/history.html
deleted file mode 100644
index d093ec8..0000000
--- a/jode/doc/history.html
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-Java Optimize and Decompile Environment (JODE)
-
-
-
-
-
-
-
-
-
- |
- |
-
-
-
-
- |
-
-History
-
-Someday I found guavad , a disassembler for java byte
-code (it does similar things like javap -c ). I used
-it on a class file, and found that it was possible to reconstruct the
-original java code. First I did it by hand on some small routines,
-but I soon realized that it was a rather stupid task. So I wrote a
-small perl script that
-did this process automatically. At the end of the next day I had my
-first working decompiler.
-
-Now while the perl script is working, it is not easy
-to use. You have to decompile the code first with a disassembler, cut
-out the code of a single method, and run the perl script on it. I
-decided to get the bytecode directly out of the class files and do
-this all automatically. I decided to write it in java
-now, because it suited best.
-
-Just for the records: the java code is now more than 50 times
-bigger than the original perl script and is still growing.
- |
-
-
-
-
- All trademarks and copyrights on this page are properties of their respective owners.
- Last updated on 8-May-2000,
- Copyright © 1998-2000 by Jochen Hoenicke.
- |
-
-
-
-
-
-
diff --git a/jode/doc/history.php b/jode/doc/history.php
index de602ca..9ed1962 100644
--- a/jode/doc/history.php
+++ b/jode/doc/history.php
@@ -1,5 +1,5 @@
-History
+History
Someday I found guavad , a disassembler for java byte
code (it does similar things like javap -c ). I used
diff --git a/jode/doc/index.html b/jode/doc/index.html
deleted file mode 100644
index 51dff88..0000000
--- a/jode/doc/index.html
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-Java Optimize and Decompile Environment (JODE)
-
-
-
-
-
-
-
-
-
- |
- |
-
-
-
-
- |
-
-
-
- JODE is a java package containing a decompiler and an
-optimizer for java. This package is freely available under the GPL
-(see license).
-
- The decompiler takes class files as input and produces
-something similar to the original java file. Of course this
-can't be perfect: There is no way to produce the comments or the names
-of local variables (except when compiled with debuging) and there are
-often more ways to write the same thing. But JODE does its job
-quite well, so you should give it a try: start
-the applet.
-
-The optimizer transforms class files in various ways with
-can be controlled by a script file. It supports the following
-operations:
-
-- Renaming class, method, field and local names to shorter,
-obfuscated, or unique names or according to a given translation
-table
-- Removing debugging information
-- Removing dead code (classes, fields, methods) and constant
-fields
-- Optimizing local variable allocation
-
-
-News
-
-
-- JODE is now hosted by SourceForge.
-- The latest CVS version breaks long lines
-- I can now decompile inner and anonymous classes.
-- The optimizer (aka obfuscator) can be customized via a small
-config file
-- Jode is autoconfigured.
-
-
-Limitations
-
-If not all dependent classes can be found, the verifier (which is
- run before decompilation starts) may exit with a type error. You
- can decompile it with --verify=off, but take the warning
- serious, that types may be incorrect. There's sometimes no way to
- guess the right type, if you don't have access the full class
- hierarchie.
-
- This is not a bug in the verifier: java will complain the same way,
- if it is run with bytecode verification turned on. And if you don't
- have the dependent classes, you can't compile the code again.
-
-There may be situations, where the code doesn't understand complex
-expressions. In this case many ugly temporary variables are used, but
-the code should still be compileable. This does especially happen
-when you compile with `-O' flag and javac has inlined some
-methods.
-
-Sometimes this program may exit with an Exception or
-produce incorrect code. Most time the code can't be compiled, so that
-it can be easily spotted. If you have one of these problems (except
-those that occur on some of the jode.test files, I would
-be very interested in a bug report (including the class
-file, if possible).
-
-Sometimes JODE generates some GOTO expression and labels.
-This shouldn't happen any more with code produced by javac or jikes.
-But some flow obfuscator may provoke this. In that case you can run
-the Obfuscator first (to optimize away the flow obfuscation ;-).
- |
-
-
-
-
- All trademarks and copyrights on this page are properties of their respective owners.
- Last updated on 8-May-2000,
- Copyright © 1998-2000 by Jochen Hoenicke.
- |
-
-
-
-
-
diff --git a/jode/doc/index.php b/jode/doc/index.php
index 98addb1..42f8471 100644
--- a/jode/doc/index.php
+++ b/jode/doc/index.php
@@ -1,16 +1,16 @@
JODE is a java package containing a decompiler and an
-optimizer for java. This package is freely available under the GPL
-(see license).
+optimizer for java. This package is freely available under the GNU GPL.
- The decompiler takes class files as input and produces
-something similar to the original java file. Of course this
-can't be perfect: There is no way to produce the comments or the names
-of local variables (except when compiled with debuging) and there are
-often more ways to write the same thing. But JODE does its job
-quite well, so you should give it a try: selflink("applet") ?>start
-the applet.
+The decompiler reads in class files and produces something
+similar to the original java file. Of course this can't be
+perfect: There is no way to produce the comments or the names of local
+variables (except when compiled with debuging) and there are often
+more ways to write the same thing. However, JODE does its job quite
+well, so you should give it a try and selflink("applet") ?>start the
+applet.
The optimizer transforms class files in various ways with
can be controlled by a script file. It supports the following
@@ -28,14 +28,23 @@ fields
News
+- JODE 1.1 is out. With support for javac v8 (jdk 1.3).
- JODE is now hosted by SourceForge.
-- The latest CVS version breaks long lines
-- I can now decompile inner and anonymous classes.
+- Now long lines are automatically broken.
+- Inner and anonymous classes are automatically decompiled.
- The optimizer (aka obfuscator) can be customized via a small
config file
-- Jode is autoconfigured.
+Known bugs of the decompiler
+
+Some jdk1.3 synthetic access functions aren't understood. The
+ produced source contains access$xxx functions, but it still compiles.
+
+There may be other bugs, that cause Exceptions or invalid code.
+ If you have such a problems don't hesitate to issue a bug report.
+ Please include the class file if possible.
+
Limitations
If not all dependent classes can be found, the verifier (which is
@@ -55,15 +64,4 @@ the code should still be compileable. This does especially happen
when you compile with `-O' flag and javac has inlined some
methods.
-Sometimes this program may exit with an Exception or
-produce incorrect code. Most time the code can't be compiled, so that
-it can be easily spotted. If you have one of these problems (except
-those that occur on some of the jode.test files, I would
-be very interested in a bug report (including the class
-file, if possible).
-
-Sometimes JODE generates some GOTO expression and labels.
-This shouldn't happen any more with code produced by javac or jikes.
-But some flow obfuscator may provoke this. In that case you can run
-the Obfuscator first (to optimize away the flow obfuscation ;-).
diff --git a/jode/doc/license.html b/jode/doc/license.html
deleted file mode 100644
index c4a5636..0000000
--- a/jode/doc/license.html
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-Java Optimize and Decompile Environment (JODE)
-
-
-
-
-
-
-
-
-
- |
- |
-
-
-
-
- |
-
- JODE is Copyright © 1998-2000 by 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 of the License, 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.
-
- |
-
-
-
-
- All trademarks and copyrights on this page are properties of their respective owners.
- Last updated on 8-May-2000,
- Copyright © 1998-2000 by Jochen Hoenicke.
- |
-
-
-
-
-
diff --git a/jode/doc/license.php b/jode/doc/license.php
index 76ae787..ad283f7 100644
--- a/jode/doc/license.php
+++ b/jode/doc/license.php
@@ -1,15 +1,16 @@
+License
JODE is Copyright © 1998-2000 by Jochen Hoenicke.
-This program is free software; you can redistribute it and/or modify
+ This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
+version 2 of the License, or (at your option) any later version.
-This program is distributed in the hope that it will be useful,
+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.
+GNU General Public License for more details.
diff --git a/jode/doc/links.html b/jode/doc/links.html
deleted file mode 100644
index 9b4e626..0000000
--- a/jode/doc/links.html
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-Java Optimize and Decompile Environment (JODE)
-
-
-
-
-
-
-
-
-
-
-
- All trademarks and copyrights on this page are properties of their respective owners.
- Last updated on 8-May-2000,
- Copyright © 1998-2000 by Jochen Hoenicke.
- |
-
-
-
-
-
-
diff --git a/jode/doc/links.php b/jode/doc/links.php
index 14ee011..0bce94d 100644
--- a/jode/doc/links.php
+++ b/jode/doc/links.php
@@ -28,6 +28,19 @@ strings. But JODE's deobfuscator can undo both.
href="http://jedit.standmed.com/plugins/JavaInsight">JavaInsight plugin for
jEdit.
+Software Directories
+
+- Get everything and anything for Linux at the
+ .
+
+- A great place for developing free software is
+
+
+
Miscellanous packages needed to run JODE
- CYGWIN (unix tools for win95/NT)
@@ -52,10 +65,12 @@ href="http://jedit.standmed.com/plugins/JavaInsight">JavaInsight plugin for
- Collection Classes:
- I have written a small script that puts the collection classes
-from the GNU Classpath Project into
-its own package (
org.gnu.java.util.collections ). You can
-download the source code (including
-the script), or a precompiled jar file.
+from the GNU Classpath Project
+into its own package (gnu.java.util.collections ). This
+script is now part of GNU classpath. For your convenience I have put a
+precompiled jar
+file on this server.
diff --git a/jode/doc/menu.inc b/jode/doc/menu.inc
index fff205f..0e6bfa5 100644
--- a/jode/doc/menu.inc
+++ b/jode/doc/menu.inc
@@ -1,115 +1,47 @@
Home" , "selflink", "jode",
+$menu =
+ array("Home" , "selflink", "index",
"Project page" , "sflink", "project/",
- "-", "-", "-",
"Applet" , "selflink", "applet",
"Download" , "selflink", "download",
+ "FAQ" , "selflink", "faq",
+ "Feedback" , "selflink", "feedback",
"Documentation", "selflink", "usage",
"License" , "selflink", "license",
"History" , "selflink", "history",
"Links" , "selflink", "links",
- "Blue Sky" , "selflink", "bluesky"),
- "Feedback",
- array("Bug Tracking" , "sflink", "bugs/",
- "Public Forums" , "sflink", "forum/",
- "Mailing List" , "link",
- "http://lists.sourceforge.net/mailman/listinfo/jode-users",
- "Private Mail" , "link",
- "http://sourceforge.net/sendmessage.php?touser=18252"),
- "Download",
- array("FTP server", "link", "ftp://jode.sourceforge.net/pub/jode/",
- "Source releases", "sflink", "project/filelist.php"));
-
-$images = array(
- "Powered by ", "http://sourceforge.net/sflogo.php?group_id=3790&type=1",
- "SourceForge", "http://sourceforge.net",
- "Best viewed with ", "a-logo.gif",
- "Any Browser", "http://www.anybrowser.org/campaign/");
-
-if (eregi("^Lynx", $HTTP_USER_AGENT)) {
-
- reset($menu);
- while (list($dummy, $header) = each($menu)) {
- list($dummy, $subitems) = each($menu);
-
- echo "$header:\n";
- reset($subitems);
- while (list($dummy, $name) = each($subitems)) {
- list($dummy, $type) = each($subitems);
- list($dummy, $link) = each($subitems);
- if ($type == "selflink") {
- selflink($link);
- } else if ($type == "sflink") {
- sflink($link);
- } else if ($type == "-") {
- echo " \n";
- continue;
- } else if ($type == "link") {
- echo "";
- }
- $name = ereg_replace(" ", " ", $name);
- echo "$name";
- if (current($subitems)) {
- echo " |\n";
- }
- }
- echo " \n\n";
- }
- echo "\n";
- reset($images);
- while (list($dummy, $label) = each($images)) {
- list($dummy, $src) = each($images);
- list($dummy, $alt) = each($images);
- list($dummy, $link) = each($images);
- echo "$label\n";
- }
- echo "\n";
-} else {
- echo "\n";
+ "Blue Sky" , "selflink", "bluesky");
+?>
- reset($menu);
- while (list($dummy, $header) = each($menu)) {
- list($dummy, $subitems) = each($menu);
- echo "\n";
- echo "$header | \n";
- echo "\n";
- reset($subitems);
- while (list($dummy, $name) = each($subitems)) {
- list($dummy, $type) = each($subitems);
- list($dummy, $link) = each($subitems);
- if ($type == "selflink") {
- selflink($link);
- } else if ($type == "sflink") {
- sflink($link);
- } else if ($type == "-") {
- echo " \n";
- continue;
- } else if ($type == "link") {
- echo "";
- }
- $name = ereg_replace(" ", " ", $name);
+
+
+";
- if (current($subitems)) {
- echo " \n";
- }
}
+ } else if ($type == "sflink") {
+ sflink($link);
+ echo "$name";
+ } else if ($type == "-") {
+ echo " \n";
+ continue;
+ } else if ($type == "link") {
+ echo "$name";
}
- echo " | \n";
- echo "";
-
- reset($images);
- while (list($dummy, $label) = each($images)) {
- list($dummy, $src) = each($images);
- list($dummy, $alt) = each($images);
- list($dummy, $link) = each($images);
- echo " $label \n";
- echo " \n";
+ if (current($menu)) {
+ echo " |\n";
}
- echo " | \n";
}
?>
+ |
+
diff --git a/jode/doc/usage.html b/jode/doc/usage.html
deleted file mode 100644
index e04302e..0000000
--- a/jode/doc/usage.html
+++ /dev/null
@@ -1,281 +0,0 @@
-
-
-
-Java Optimize and Decompile Environment (JODE)
-
-
-
-
-
-
-
-
-
- |
- |
-
-
-
-
- |
-
-
-Using the Decompiler
-After you have downloaded the necessary
-packages, put them into your CLASSPATH:
-
-- Under Windows you have to start a MSDOS session and type
-something like:
-
-set CLASSPATH=C:\download\jode-xxx.jar;C:\swing\swingall.jar
-
- - Under Unix you start a shell and type (for bourne shell):
-
export CLASSPATH=/tmp/jode-xxx.jar:/usr/local/swing/swingall.jar
-or for csh:
-setenv CLASSPATH /tmp/jode-xxx.jar:/usr/local/swing/swingall.jar
-
-
-There is also a batch file for windows and a script file for unix,
-that you can use. Adapt the CLASSPATH in the file and put it to a
-convenient location.
-
- jar -xvf jode-xxx.jar bin/jode.bat resp. bin/jode
-
-
-Command Line Interface
-
-The most powerful way to start JODE's decompiler is the command
-line interface. Some people don't like long command lines; they
-should go to the next section.
-
-Start the class jode.decompiler.Main with the options. The
-following command will give a complete list of the available commands:
-
-java jode.decompiler.Main --help
-
-AWT Interface
-
-The AWT Interface looks exactly like the applet. In fact the applet uses the AWT Interface. You start it
-after setting the CLASSPATH (see above), with
-
-java jode.decompiler.Window
-
-In the classpath line you can enter a number of jar files, zip files
-and directories separated by comma(,). Then enter the
-dot(.) separated name of the class you want to decompile.
-Press the start button and the decompiled class should
-appear. You can save it via the save button.
-
-Swing Interface
-
-For the swing interface you need java version 1.2 or the separately
-available swing package (see link
-page. You can invoke it like this:
-
-java jode.swingui.Main --classpath classes.jar
-
-
-The swing interface will show the package hierarchie of all classes
-in the classpath on the left side. You can now select a class and the
-decompiled code will appear on the right side. Via the menu, you may
-change the classpath or switch between package hierarchie tree and
-class inheritence tree.
-
-The swing interface is very useful to browse through class files if
-you don't have the source code. You can also use it to trace bugs in
-library code. It is not meant to generate java files and so
-you won't find a save option there.
-
-Java Interface
-
-If you want to integrate JODE into your own java program, you
-can use the jode.decompiler.Decompiler
-class. Note that the GPL only allows you to integrate JODE
-into GPL programs. Please contact me if you use JODE in this
-way.
-
-You may use this stripped
-down jar archive containing all necessary classes.
-
-Using the Obfuscator
-
-To use the obfuscator you should first create a script file, say myproject.jos. Then you can invoke the
-obfuscator with:
-
-java jode.obfuscator.Main myproject.jos
-
-
-The script file should contain the following options:
-
-First select the classpath. You should include everything in the
-classpath that you need to run your application. This also includes
-the system class files (Sun puts them into classes.zip or
-rt.jar ))
-
-classpath = "c:\\jdk1.2\\jre\\lib\\rt.jar","d:\\project\\java",
- "ftp://www.myorg.org/pub/classlib.jar"
-
-
-Specify where you want the obfuscated classes to go. I recommend
-to write them directly into a zip file, but you can also give a
-directory.
-
-dest = "obfuscated.zip"
-
-
-You can make JODE write its translation table. This table
-can be used later to undo the name obfuscation, or you can look there
-to decrypt exceptions you may get.
-
-revtable = "translat.tbl"
-
-
-Select what you want to strip. There are several
-possibilities, which can be separated by comma(,):
-
-- unreach
-- strip unreachable methods and classes.
-- source
-- remove the name of the java file (exceptions will get unreadable).
-- lnt
-- remove the line number table (exceptions will get unreadable).
-- lvt
-- remove the local variable table (debugging doesn't work).
-- inner
-- strip inner class info (reflection doesn't work correctly).
-
-
-strip = "unreach","lvt","inner"
-
-
-Select the packages and classes you want to obfuscate. You should
-only include libraries, that you don't ship separately. If you give a
-package, all classes and subpackages are loaded. You can also use
-* as wild card, that matches everything (including dots).
-
-
-load = new WildCard { value = "org.myorg.myproject" },
- new WildCard { value = "org.myorg.mylib*" },
- new WildCard { value = "org.otherorg.shortlib" }
-
-
-Select the methods and classes you want to preserve. This is
-the main method for applications and the default constructor
-<init>.()V for applets, resource bundles and other classes
-that you load manually at runtime. You have to give the method
-name and the type signature to identify your method. javap
--s will show you the type signatures for your classes, but you
-may also use *, to select all methods with that name.
-
-preserve = new WildCard { value = "org.myorg.ApplicationClass.main.*" },
- new WildCard { value = "org.myorg.AppletClass.<init>.()V" },
- new WildCard { value = "org.resources.Bundle*.<init>.()V" },
-
-
-If you want to obfuscate (or just shorten) the identifier you can
-specify a renamer. There are currently following renamer
-available
-- StrongRenamer
-- Renames to the shortest possible name. You can give a charset
-that should be used. It uses the same name as much as possible.
-- UniqueRenamer
-- Renames to unique identifier of the form xxx123. Useful
-to reduce name conflicts, before you decompile an obfuscated package.
-- NameSwapper
-- This renamer just swaps the names. This is a funny obfuscation
-option that is not very strong, but very confusing.
-- KeywordRenamer
-- Renames identifiers to keyword. You can give your own list of
-keywords as parameters. Resulting code is not decompilable directly,
-but it is not legal bytecode either. Some paranoid
-web browsers refuse to run applets containing keywords as identifiers
-(and they are completely within the Java VM spec).
-
-
-renamer = new StrongRenamer
-
-
-You can also create a renaming table with the same format as the
-table written by revtable. The entries in the table get precedence
-over renamer. Entries not in the table will get renamed by the
-renamer.
-
-table = "translat.tbl"
-
-
-Now you can select the analyzer. The purpose of the
-analyzer is to mark all reachable methods, find out which methods
-needs to get the same name (overloading), and which method names
-mustn't change (overload of library methods, e.g. nextElement
-for Enumerations). There are currently two analyzers.
- - SimpleAnalyzer
-- Straight forward analyzer. It is fast and will remove dead code
-on method basis.
-
-- ConstantAnalyzer
-- Strong analyzer that will determine, which fields and instructions
-have constant values. It will remove dead code on instruction basis
-and replace constant instruction with a load of the constant, or
-remove them completely.
This analyzer is especially useful to
-revert the flow obfuscation of some other obfuscators.
-
-
-
-analyzer = new ConstantAnalyzer
-
-
-Pre- and Post transformers transform the bytecode before
-resp. after the Analyzer runs. Using this default should be okay.
-You may remove the LocalOptimizer, though, if you have problems.
-In the future I may add some new post transformers, that do string
-encryption, flow obfuscation and similar things. If you want to write
-your own Transformers please contact me, since the next version will
-change the bytecode interface.
-
-post = new LocalOptimizer, new RemovePopAnalyzer
-
- |
-
-
-
-
- All trademarks and copyrights on this page are properties of their respective owners.
- Last updated on 8-May-2000,
- Copyright © 1998-2000 by Jochen Hoenicke.
- |
-
-
-
-
-
diff --git a/jode/doc/usage.php b/jode/doc/usage.php
index 5ea9192..f7a55d7 100644
--- a/jode/doc/usage.php
+++ b/jode/doc/usage.php
@@ -9,26 +9,28 @@
*/ ?>
Using the Decompiler
-After you have downloaded the necessary
-packages, put them into your CLASSPATH:
+After you have downloaded the jar archive
+put it into your CLASSPATH. The package
+swingall.jar is also needed if you are using JDK 1.1.
- Under Windows you have to start a MSDOS session and type
something like:
-set CLASSPATH=C:\download\jode-xxx.jar;C:\swing\swingall.jar
+set CLASSPATH=C:\download\jode-.jar;C:\swing\swingall.jar
- - Under Unix you start a shell and type (for bourne shell):
-
export CLASSPATH=/tmp/jode-xxx.jar:/usr/local/swing/swingall.jar
+
+ - Under Unix you start a shell and type (for bourne shell):
+
export CLASSPATH=/tmp/jode-.jar:/usr/local/swing/swingall.jar
or for csh:
-setenv CLASSPATH /tmp/jode-xxx.jar:/usr/local/swing/swingall.jar
+setenv CLASSPATH /tmp/jode-.jar:/usr/local/swing/swingall.jar
There is also a batch file for windows and a script file for unix,
-that you can use. Adapt the CLASSPATH in the file and put it to a
-convenient location.
+that you can use. You can extract it with the following command:
- jar -xvf jode-xxx.jar bin/jode.bat resp. bin/jode
+ jar -xvf jode-.jar bin/jode.bat resp. bin/jode
+Edit the file to adapt it to your paths and put it to a convenient location.
Command Line Interface
@@ -41,6 +43,13 @@ following command will give a complete list of the available commands:
java jode.decompiler.Main --help
+If you want to decompile a jar package you can do it this way:
+
+java jode.decompiler.Main --dest srcdir program.jar
+
+If you have installed the batch file/script, you can use it like this:
+jode --dest srcdir program.jar
+
AWT Interface
The AWT Interface looks exactly like the
@@ -60,43 +69,45 @@ appear. You can save it via the save button.
For the swing interface you need java version 1.2 or the separately
available swing package (see link
-page. You can invoke it like this:
+page. You can invoke it with the following command:
-java jode.swingui.Main --classpath classes.jar
+java jode.swingui.Main classes.jar
+resp. jode swi classes.jar
-The swing interface will show the package hierarchie of all classes
+The swing interface will show the package hierarchie of all classes
in the classpath on the left side. You can now select a class and the
decompiled code will appear on the right side. Via the menu, you may
change the classpath or switch between package hierarchie tree and
-class inheritence tree.
+class inheritence tree.
-The swing interface is very useful to browse through class files if
+The swing interface is very useful to browse through class files if
you don't have the source code. You can also use it to trace bugs in
library code. It is not meant to generate java files and so
-you won't find a save option there.
+you won't find a save option there.
Java Interface
-If you want to integrate JODE into your own java program, you
-can use the jode.decompiler.Decompiler
+If you want to integrate JODE into your own java program,
+you can use the jode.decompiler.Decompiler
class. Note that the GPL only allows you to integrate JODE
-into GPL programs. Please contact me if you use JODE in this
-way.
+into GPL programs. Please tell me if you use JODE in this
+way.
-You may use this stripped
-down jar archive containing all necessary classes.
+You should ship jode-1.1-embedded.jar with your program. This jar file is
+available in the sflink("project/showfiles.php") ?>download area.
+It works only under JDK 1.2 and above.
Using the Obfuscator
-To use the obfuscator you should first create a script file, say To use the obfuscator you should first create a script file, say myproject.jos. Then you can invoke the
obfuscator with:
java jode.obfuscator.Main myproject.jos
-
+
The script file should contain the following options:
diff --git a/jode/jode/GlobalOptions.java.in b/jode/jode/GlobalOptions.java.in
index de63c2f..97cd37c 100644
--- a/jode/jode/GlobalOptions.java.in
+++ b/jode/jode/GlobalOptions.java.in
@@ -1,4 +1,4 @@
-/* GlobalOptions Copyright (C) 1999-2000 Jochen Hoenicke.
+/* GlobalOptions 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
@@ -25,7 +25,7 @@ public class GlobalOptions {
public final static String version = "@VERSION@";
public final static String email = "jochen@gnu.org";
public final static String copyright =
- "Jode (c) 1998-2000 Jochen Hoenicke <"+email+">";
+ "Jode (c) 1998-2001 Jochen Hoenicke <"+email+">";
public final static String URL = "http://jode.sourceforge.net/";
public static PrintWriter err = new PrintWriter(System.err, true);
diff --git a/jode/jode/bytecode/BytecodeInfo.java.in b/jode/jode/bytecode/BytecodeInfo.java.in
index d7a0169..3301319 100644
--- a/jode/jode/bytecode/BytecodeInfo.java.in
+++ b/jode/jode/bytecode/BytecodeInfo.java.in
@@ -25,6 +25,8 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.EOFException;
import java.io.IOException;
+import java.util.BitSet;
+import java.util.Stack;
import java.util.Vector;
import java.util.Enumeration;
import java.util.NoSuchElementException;
@@ -845,6 +847,21 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
int index = input.readUnsignedShort();
exceptionHandlers[i].type = (index == 0) ? null
: cp.getClassName(index);
+
+ if (exceptionHandlers[i].catcher.getOpcode() == opc_athrow) {
+ /* There is an obfuscator, which inserts bogus
+ * exception entries jumping directly to a throw
+ * instruction. Remove those handlers.
+ */
+ handlersLength--;
+ i--;
+ }
+ }
+ if (handlersLength < exceptionHandlers.length) {
+ Handler[] newHandlers = new Handler[handlersLength];
+ System.arraycopy(exceptionHandlers, 0, newHandlers, 0,
+ handlersLength);
+ exceptionHandlers = newHandlers;
}
}
readAttributes(cp, input, FULLINFO);
@@ -901,9 +918,73 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
}
}
+ private void calculateMaxStack() {
+ maxStack = 0;
+ int[] stackHeights = new int[instructions.getCodeLength()];
+ int[] poppush = new int[2];
+ Stack todo = new Stack();
+
+ for (int i=0; i < stackHeights.length; i++)
+ stackHeights[i] = -1;
+
+ stackHeights[0] = 0;
+ todo.push(instructions.get(0));
+ while (!todo.isEmpty()) {
+ Instruction instr = (Instruction) todo.pop();
+ Instruction next = instr.getNextByAddr();
+ Instruction[] succs = instr.getSuccs();
+ int addr = instr.getAddr();
+ instr.getStackPopPush(poppush);
+ int sh = stackHeights[addr] - poppush[0] + poppush[1];
+// System.err.println("Instr: "+instr.getDescription()+
+// "; before: "+stackHeights[addr]+" after: "+sh);
+ if (maxStack < sh)
+ maxStack = sh;
+ if (instr.getOpcode() == opc_jsr) {
+ if (stackHeights[next.getAddr()] == -1) {
+ stackHeights[next.getAddr()] = sh - 1;
+ todo.push(next);
+ }
+ if (stackHeights[succs[0].getAddr()] == -1) {
+ stackHeights[succs[0].getAddr()] = sh;
+ todo.push(succs[0]);
+ }
+ } else {
+ if (succs != null) {
+ for (int i=0; i < succs.length; i++) {
+ if (stackHeights[succs[i].getAddr()] == -1) {
+ stackHeights[succs[i].getAddr()] = sh;
+ todo.push(succs[i]);
+ }
+ }
+ }
+ if (!instr.doesAlwaysJump()
+ && stackHeights[next.getAddr()] == -1) {
+ stackHeights[next.getAddr()] = sh;
+ todo.push(next);
+ }
+ }
+ for (int i=0; i< exceptionHandlers.length; i++) {
+ if (exceptionHandlers[i].start.compareTo(instr) <= 0
+ && exceptionHandlers[i].end.compareTo(instr) >= 0) {
+ int catcher = exceptionHandlers[i].catcher.getAddr();
+ if (stackHeights[catcher] == -1) {
+ stackHeights[catcher] = 1;
+ todo.push(exceptionHandlers[i].catcher);
+ }
+ }
+ }
+ }
+// System.err.println("New maxStack: "+maxStack+" Locals: "+maxLocals);
+ }
+
public void prepareWriting(GrowableConstantPool gcp) {
- /* Recalculate addr, length and add all constants to gcp */
+ /* Recalculate addr, length, maxStack, maxLocals and add all
+ * constants to gcp */
int addr = 0;
+ maxLocals = (methodInfo.isStatic() ? 0 : 1) +
+ TypeSignature.getArgumentSize(methodInfo.getType());
+
for (Iterator iter = instructions.iterator(); iter.hasNext(); ) {
Instruction instr = (Instruction) iter.next();
int opcode = instr.getOpcode();
@@ -957,22 +1038,44 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
length = 3;
else
length = 6;
+ if (slot >= maxLocals)
+ maxLocals = slot + 1;
break;
}
- case opc_iload: case opc_lload:
- case opc_fload: case opc_dload: case opc_aload:
- case opc_istore: case opc_lstore:
- case opc_fstore: case opc_dstore: case opc_astore:
- if (instr.getLocalSlot() < 4) {
+ case opc_iload: case opc_fload: case opc_aload:
+ case opc_istore: case opc_fstore: case opc_astore: {
+ int slot = instr.getLocalSlot();
+ if (slot < 4)
length = 1;
- break;
- }
- /* fall through */
+ else if (slot < 256)
+ length = 2;
+ else
+ length = 4;
+ if (slot >= maxLocals)
+ maxLocals = slot + 1;
+ break;
+ }
+ case opc_lload: case opc_dload:
+ case opc_lstore: case opc_dstore: {
+ int slot = instr.getLocalSlot();
+ if (slot < 4)
+ length = 1;
+ else if (slot < 256)
+ length = 2;
+ else
+ length = 4;
+ if (slot+1 >= maxLocals)
+ maxLocals = slot + 2;
+ break;
+ }
case opc_ret: {
- if (instr.getLocalSlot() < 256)
+ int slot = instr.getLocalSlot();
+ if (slot < 256)
length = 2;
else
length = 4;
+ if (slot >= maxLocals)
+ maxLocals = slot + 1;
break;
}
case opc_lookupswitch: {
@@ -1092,6 +1195,12 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
addr += length;
}
instructions.setLastAddr(addr);
+ try {
+ calculateMaxStack();
+ } catch (RuntimeException ex) {
+ ex.printStackTrace();
+ dumpCode(GlobalOptions.err);
+ }
for (int i=0; i< exceptionHandlers.length; i++)
if (exceptionHandlers[i].type != null)
gcp.putClassName(exceptionHandlers[i].type);
@@ -1483,14 +1592,6 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes {
return lnt;
}
- public void setMaxStack(int ms) {
- maxStack = ms;
- }
-
- public void setMaxLocals(int ml) {
- maxLocals = ml;
- }
-
public void setExceptionHandlers(Handler[] handlers) {
exceptionHandlers = handlers;
}
diff --git a/jode/jode/bytecode/ClassInfo.java.in b/jode/jode/bytecode/ClassInfo.java.in
index abb5a01..687bf94 100644
--- a/jode/jode/bytecode/ClassInfo.java.in
+++ b/jode/jode/bytecode/ClassInfo.java.in
@@ -192,6 +192,19 @@ public class ClassInfo extends BinaryInfo {
int access = input.readUnsignedShort();
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.
+ */
+ if (outer != null && innername != null
+ && inner.length() > outer.length() + 2 + innername.length()
+ && inner.startsWith(outer+"$")
+ && inner.endsWith("$"+innername)
+ && Character.isDigit(inner.charAt(outer.length() + 1)))
+ outer = null;
+
InnerClassInfo ici = new InnerClassInfo
(inner, outer, innername, access);
@@ -272,10 +285,11 @@ public class ClassInfo extends BinaryInfo {
/* header */
if (input.readInt() != 0xcafebabe)
throw new ClassFormatException("Wrong magic");
- if (input.readUnsignedShort() > 3)
- throw new ClassFormatException("Wrong minor");
- if (input.readUnsignedShort() != 45)
- throw new ClassFormatException("Wrong major");
+ int version = input.readUnsignedShort();
+ version |= input.readUnsignedShort() << 16;
+ if (version < (45 << 16 | 0)
+ || version > (47 << 16 | 0))
+ throw new ClassFormatException("Wrong class version");
/* constant pool */
ConstantPool cpool = new ConstantPool();
@@ -603,9 +617,6 @@ public class ClassInfo extends BinaryInfo {
String message = ex.getMessage();
if ((howMuch & ~(FIELDS|METHODS|HIERARCHY
|INNERCLASSES|OUTERCLASSES)) != 0) {
- GlobalOptions.err.println
- ("Can't read class " + name + ".");
- ex.printStackTrace(GlobalOptions.err);
throw new NoClassDefFoundError(name);
}
// Try getting the info through the reflection interface
diff --git a/jode/jode/bytecode/MethodInfo.java b/jode/jode/bytecode/MethodInfo.java
index 8c52c5f..da52e1e 100644
--- a/jode/jode/bytecode/MethodInfo.java
+++ b/jode/jode/bytecode/MethodInfo.java
@@ -54,8 +54,7 @@ public class MethodInfo extends BinaryInfo {
if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("Code")) {
bytecode = new BytecodeInfo(this);
bytecode.read(cp, input);
- } else if ((howMuch & KNOWNATTRIBS) != 0
- && name.equals("Exceptions")) {
+ } else if (name.equals("Exceptions")) {
int count = input.readUnsignedShort();
exceptions = new String[count];
for (int i=0; i< count; i++)
diff --git a/jode/jode/bytecode/SearchPath.java b/jode/jode/bytecode/SearchPath.java
index 83b864c..a93e73a 100644
--- a/jode/jode/bytecode/SearchPath.java
+++ b/jode/jode/bytecode/SearchPath.java
@@ -279,7 +279,7 @@ public class SearchPath {
} catch (SecurityException ex) {
GlobalOptions.err.println
("Warning: Security exception while accessing "
- +bases[i]+".");
+ + bases[i] + ".");
}
} catch (MalformedURLException ex) {
/* disable entry */
@@ -309,6 +309,10 @@ public class SearchPath {
}
public boolean exists(String filename) {
+ String localFileName =
+ (java.io.File.separatorChar != '/')
+ ? filename.replace('/', java.io.File.separatorChar)
+ : filename;
for (int i=0; i 0) {
- int count = read(tmpbuf, 0,
- (int)Math.min(n, 512L));
- if (count == -1)
- return skipped;
- skipped += count;
- n -= count;
- }
- return skipped;
- }
- };
+/// // The skip method in jdk1.1.7 ZipInputStream
+/// // is buggy. We return a wrapper that fixes
+/// // this.
+/// return new FilterInputStream(zis) {
+/// private byte[] tmpbuf = new byte[512];
+/// public long skip(long n) throws IOException {
+/// long skipped = 0;
+/// while (n > 0) {
+/// int count = read(tmpbuf, 0,
+/// (int)Math.min(n, 512L));
+/// if (count == -1)
+/// return skipped;
+/// skipped += count;
+/// n -= count;
+/// }
+/// return skipped;
+/// }
+/// };
///#else
-/// return zis;
+ return zis;
///#endif
}
zis.closeEntry();
@@ -413,8 +418,8 @@ public class SearchPath {
return conn.getInputStream();
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
- +" while accessing "
- +bases[i]+filename);
+ + " while accessing "
+ + bases[i] + filename);
ex.printStackTrace(GlobalOptions.err);
/* ignore and take next element */
} catch (FileNotFoundException ex) {
@@ -431,17 +436,14 @@ public class SearchPath {
if (ze != null)
return zips[i].getInputStream(ze);
} else {
- if (java.io.File.separatorChar != '/')
- filename = filename
- .replace('/', java.io.File.separatorChar);
try {
- File f = new File(dirs[i], filename);
+ File f = new File(dirs[i], localFileName);
if (f.exists())
return new FileInputStream(f);
} catch (SecurityException ex) {
GlobalOptions.err.println("Warning: SecurityException"
- +" while accessing "
- +dirs[i]+filename);
+ + " while accessing "
+ + dirs[i] + localFileName);
/* ignore and take next element */
}
}
@@ -457,6 +459,10 @@ public class SearchPath {
* @return true, if filename exists and is a directory, false otherwise.
*/
public boolean isDirectory(String filename) {
+ String localFileName =
+ (java.io.File.separatorChar != '/')
+ ? filename.replace('/', java.io.File.separatorChar)
+ : filename;
for (int i=0; i 0)
+ writer.print(modif + " ");
+ if (isStrictFP()) {
+ /* The STRICTFP modifier is set.
+ * We handle it, since java.lang.reflect.Modifier is too dumb.
+ */
+ writer.print("strictfp ");
+ }
+ /* interface is in modif */
+ if (!clazz.isInterface())
+ writer.print("class ");
+ writer.print(name);
+ ClassInfo superClazz = clazz.getSuperclass();
+ if (superClazz != null &&
+ superClazz != ClassInfo.javaLangObject) {
+ writer.breakOp();
+ writer.print(" extends " + (writer.getClassString
+ (superClazz, Scope.CLASSNAME)));
+ }
+ ClassInfo[] interfaces = clazz.getInterfaces();
+ if (interfaces.length > 0) {
+ writer.breakOp();
+ writer.print(clazz.isInterface() ? " extends " : " implements ");
+ writer.startOp(writer.EXPL_PAREN, 1);
+ for (int i=0; i < interfaces.length; i++) {
+ if (i > 0) {
+ writer.print(", ");
+ writer.breakOp();
+ }
+ writer.print(writer.getClassString
+ (interfaces[i], Scope.CLASSNAME));
+ }
+ writer.endOp();
+ }
+ writer.println();
+
+ writer.openBraceClass();
+ writer.tab();
+ dumpBlock(writer, pl, done, scale);
+ writer.untab();
+ writer.closeBraceClass();
+ }
+
public void dumpBlock(TabbedPrintWriter writer)
throws IOException
{
@@ -501,6 +591,7 @@ public class ClassAnalyzer
needNewLine = true;
}
writer.popScope();
+ clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS);
}
public void dumpSource(TabbedPrintWriter writer)
@@ -513,67 +604,8 @@ public class ClassAnalyzer
ProgressListener pl, double done, double scale)
throws IOException
{
- if (fields == null) {
- /* This means that the class could not be loaded.
- * give up.
- */
- return;
- }
-
- writer.startOp(writer.NO_PAREN, 0);
- /* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */
- int modifiedModifiers = modifiers & ~Modifier.SYNCHRONIZED;
- if (clazz.isInterface())
- /* interfaces are implicitily abstract */
- modifiedModifiers &= ~Modifier.ABSTRACT;
- if (parent instanceof MethodAnalyzer) {
- /* method scope classes are implicitly private */
- modifiedModifiers &= ~Modifier.PRIVATE;
- /* anonymous classes are implicitly final */
- if (name == null)
- modifiedModifiers &= ~Modifier.FINAL;
- }
- String modif = Modifier.toString(modifiedModifiers);
- if (modif.length() > 0)
- writer.print(modif + " ");
- /* interface is in modif */
- if (!clazz.isInterface())
- writer.print("class ");
- writer.print(name);
- ClassInfo superClazz = clazz.getSuperclass();
- if (superClazz != null &&
- superClazz != ClassInfo.javaLangObject) {
- writer.breakOp();
- writer.print(" extends " + (writer.getClassString
- (superClazz, Scope.CLASSNAME)));
- }
- ClassInfo[] interfaces = clazz.getInterfaces();
- if (interfaces.length > 0) {
- writer.breakOp();
- writer.print(clazz.isInterface() ? " extends " : " implements ");
- writer.startOp(writer.EXPL_PAREN, 1);
- for (int i=0; i < interfaces.length; i++) {
- if (i > 0) {
- writer.print(", ");
- writer.breakOp();
- }
- writer.print(writer.getClassString
- (interfaces[i], Scope.CLASSNAME));
- }
- writer.endOp();
- }
+ dumpDeclaration(writer, pl, done, scale);
writer.println();
-
- writer.openBrace();
- writer.tab();
- dumpBlock(writer, pl, done, scale);
- writer.untab();
- if (parent instanceof MethodAnalyzer) {
- /* This is a method scope class */
- writer.closeBraceNoSpace();
- } else
- writer.closeBrace();
- clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS);
}
public void dumpJavaFile(TabbedPrintWriter writer)
@@ -611,7 +643,11 @@ public class ClassAnalyzer
}
public boolean conflicts(String name, int usageType) {
- ClassInfo info = clazz;
+ return conflicts(clazz, name, usageType);
+ }
+
+ private static boolean conflicts(ClassInfo info,
+ String name, int usageType) {
while (info != null) {
if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) {
MethodInfo[] minfos = info.getMethods();
@@ -639,6 +675,11 @@ public class ClassAnalyzer
if (usageType == NOSUPERFIELDNAME
|| usageType == NOSUPERMETHODNAME)
return false;
+
+ ClassInfo[] ifaces = info.getInterfaces();
+ for (int i = 0; i < ifaces.length; i++)
+ if (conflicts(ifaces[i], name, usageType))
+ return true;
info = info.getSuperclass();
}
return false;
diff --git a/jode/jode/decompiler/Decompiler.java b/jode/jode/decompiler/Decompiler.java
index eb8c3c9..88574d1 100644
--- a/jode/jode/decompiler/Decompiler.java
+++ b/jode/jode/decompiler/Decompiler.java
@@ -103,6 +103,8 @@ public class Decompiler {
Options.outputStyle = Options.GNU_STYLE;
else if (value.equals("sun"))
Options.outputStyle = Options.SUN_STYLE;
+ else if (value.equals("pascal"))
+ Options.outputStyle = Options.PASCAL_STYLE;
else
throw new IllegalArgumentException("Invalid style "+value);
return;
diff --git a/jode/jode/decompiler/FieldAnalyzer.java.in b/jode/jode/decompiler/FieldAnalyzer.java.in
index 0ac9c4f..78fbcdc 100644
--- a/jode/jode/decompiler/FieldAnalyzer.java.in
+++ b/jode/jode/decompiler/FieldAnalyzer.java.in
@@ -57,7 +57,7 @@ public class FieldAnalyzer implements Analyzer {
if (fd.getConstant() != null) {
constant = new ConstOperator(fd.getConstant());
constant.setType(type);
- constant.makeInitializer();
+ constant.makeInitializer(type);
}
}
@@ -107,7 +107,7 @@ public class FieldAnalyzer implements Analyzer {
}
analyzedSynthetic();
} else
- expr.makeInitializer();
+ expr.makeInitializer(type);
constant = expr;
return true;
diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java
index 261a548..88cdaf4 100644
--- a/jode/jode/decompiler/LocalInfo.java
+++ b/jode/jode/decompiler/LocalInfo.java
@@ -126,56 +126,58 @@ public class LocalInfo implements Declarable {
* If this is called with ourself nothing will happen.
* @param li the local info that we want to shadow.
*/
- public void combineWith(LocalInfo li) {
- li = li.getLocalInfo();
- if (shadow != null) {
- getLocalInfo().combineWith(li);
- } else {
- if (this != li) {
- shadow = li;
- if (!nameIsGenerated)
- shadow.name = name;
- if (constExpr != null) {
- if (shadow.constExpr != null)
- throw new jode.AssertError
- ("local has multiple constExpr");
- shadow.constExpr = constExpr;
- }
-
-// GlobalOptions.err.println("combining "+name+"("+type+") and "
-// +shadow.name+"("+shadow.type+")");
- shadow.setType(type);
-
-
- boolean needTypeUpdate = !li.type.equals(type);
-
- java.util.Enumeration enum = operators.elements();
- while (enum.hasMoreElements()) {
- LocalVarOperator lvo =
- (LocalVarOperator) enum.nextElement();
- if (needTypeUpdate) {
- if ((GlobalOptions.debuggingFlags
- & GlobalOptions.DEBUG_TYPES) != 0)
- GlobalOptions.err.println("updating " + lvo);
- lvo.updateType();
- }
- shadow.operators.addElement(lvo);
- }
-
- enum = hints.elements();
- while (enum.hasMoreElements()) {
- Object hint = enum.nextElement();
- if (!shadow.hints.contains(hint))
- shadow.hints.addElement(hint);
- }
+ public void combineWith(LocalInfo shadow) {
+ if (this.shadow != null) {
+ getLocalInfo().combineWith(shadow);
+ return;
+ }
- /* Clear unused fields, to allow garbage collection.
- */
- type = null;
- name = null;
- operators = null;
- }
- }
+ shadow = shadow.getLocalInfo();
+ if (this == shadow)
+ return;
+
+ this.shadow = shadow;
+ if (!nameIsGenerated)
+ shadow.name = name;
+ if (constExpr != null) {
+ if (shadow.constExpr != null)
+ throw new jode.AssertError
+ ("local has multiple constExpr");
+ shadow.constExpr = constExpr;
+ }
+
+// GlobalOptions.err.println("combining "+name+"("+type+") and "
+// +shadow.name+"("+shadow.type+")");
+ shadow.setType(type);
+
+
+ boolean needTypeUpdate = !shadow.type.equals(type);
+
+ java.util.Enumeration enum = operators.elements();
+ while (enum.hasMoreElements()) {
+ LocalVarOperator lvo =
+ (LocalVarOperator) enum.nextElement();
+ if (needTypeUpdate) {
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_TYPES) != 0)
+ GlobalOptions.err.println("updating " + lvo);
+ lvo.updateType();
+ }
+ shadow.operators.addElement(lvo);
+ }
+
+ enum = hints.elements();
+ while (enum.hasMoreElements()) {
+ Object hint = enum.nextElement();
+ if (!shadow.hints.contains(hint))
+ shadow.hints.addElement(hint);
+ }
+
+ /* Clear unused fields, to allow garbage collection.
+ */
+ type = null;
+ name = null;
+ operators = null;
}
/**
@@ -359,15 +361,13 @@ public class LocalInfo implements Declarable {
}
public boolean isConstant() {
- LocalInfo li = getLocalInfo();
- Enumeration enum = li.operators.elements();
- int writes = 0;
- while (enum.hasMoreElements()) {
- if (((LocalVarOperator) enum.nextElement()).isWrite())
- writes++;
- }
- if (writes > 1)
- return false;
+ /* Checking if a local can be declared final is tricky,
+ * since it can also be the case if it is written in
+ * the "then" and "else" part of an if statement.
+ *
+ * We return true now, otherwise some code would not be
+ * decompilable.
+ */
return true;
}
@@ -383,8 +383,7 @@ public class LocalInfo implements Declarable {
if (((LocalVarOperator) enum.nextElement()).isWrite())
writes++;
}
- if (writes > 1)
- return false;
+ /* FIXME: Check if declaring final is okay */
li.isFinal = true;
return true;
}
diff --git a/jode/jode/decompiler/Main.java b/jode/jode/decompiler/Main.java
index 8b84c48..72e0e93 100644
--- a/jode/jode/decompiler/Main.java
+++ b/jode/jode/decompiler/Main.java
@@ -1,4 +1,4 @@
-/* Main Copyright (C) 1998-1999 Jochen Hoenicke.
+/* Main Copyright (C) 1998-2001 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
package jode.decompiler;
import jode.bytecode.ClassInfo;
-import jode.bytecode.SearchPath;
import jode.GlobalOptions;
import java.io.BufferedOutputStream;
@@ -28,7 +27,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.zip.ZipOutputStream;
+import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
+import java.util.Enumeration;
import gnu.getopt.LongOpt;
import gnu.getopt.Getopt;
@@ -73,7 +74,10 @@ public class Main extends Options {
public static void usage() {
PrintWriter err = GlobalOptions.err;
err.println("Version: " + GlobalOptions.version);
- err.println("Usage: java jode.decompiler.Main [OPTIONS]... [CLASSES]...");
+ err.println("Usage: java jode.decompiler.Main [OPTION]* {CLASS|JAR}*");
+ err.println("Give a fully qualified CLASS name, e.g. jode.decompiler.Main, if you want to");
+ err.println("decompile a single class, or a JAR file containing many classes.");
+ err.println("OPTION is any of these:");
err.println(" -h, --help "+
"show this information.");
err.println(" -V, --version "+
@@ -94,9 +98,13 @@ public class Main extends Options {
err.println(" "+
"and packages with more then pkglimit used classes.");
err.println(" "+
- "Limit 0 means, never import, default is 0,1.");
+ "Limit 0 means never import. Default is 0,1.");
+ err.println(" -D, --debug=... "+
+ "use --debug=help for more information.");
- err.println("The following options can be turned on or off with `yes' or `no' argument.");
+ err.println("NOTE: The following options can be turned on or off with `yes' or `no'.");
+ err.println("The options tagged with (default) are normally on. Omitting the yes/no");
+ err.println("argument will toggle the option, e.g. --verify is equivalent to --verify=no.");
err.println(" --inner "+
"decompile inner classes (default).");
err.println(" --anonymous "+
@@ -106,7 +114,7 @@ public class Main extends Options {
err.println(" --lvt "+
"use the local variable table (default).");
err.println(" --pretty "+
- "use `pretty' names for local variables.");
+ "use `pretty' names for local variables (default).");
err.println(" --push "+
"allow PUSH instructions in output.");
err.println(" --decrypt "+
@@ -116,10 +124,7 @@ public class Main extends Options {
err.println(" --immediate "+
"output source immediately (may produce buggy code).");
err.println(" --verify "+
- "verify code before decompiling it.");
- err.println("Debugging options, mainly used to debug this decompiler:");
- err.println(" -D, --debug=... "+
- "use --debug=help for more information.");
+ "verify code before decompiling it (default).");
}
public static boolean handleOption(int option, int longind, String arg) {
@@ -131,21 +136,98 @@ public class Main extends Options {
options &= ~(1 << option);
else {
GlobalOptions.err.println
- ("jode.decompiler.Main: option --"+longOptions[longind].getName()
- +" takes one of `yes', `no', `on', `off' as parameter");
+ ("jode.decompiler.Main: option --"
+ + longOptions[longind].getName()
+ + " takes one of `yes', `no', `on', `off' as parameter");
return false;
}
return true;
}
+ public static void decompileClass(String className,
+ ZipOutputStream destZip, String destDir,
+ TabbedPrintWriter writer,
+ ImportHandler imports) {
+ try {
+ ClassInfo clazz;
+ try {
+ clazz = ClassInfo.forName(className);
+ } catch (IllegalArgumentException ex) {
+ GlobalOptions.err.println
+ ("`"+className+"' is not a class name");
+ return;
+ }
+ if (skipClass(clazz))
+ return;
+
+ String filename =
+ className.replace('.', File.separatorChar)+".java";
+ if (destZip != null) {
+ writer.flush();
+ destZip.putNextEntry(new ZipEntry(filename));
+ } else if (destDir != null) {
+ File file = new File (destDir, filename);
+ File directory = new File(file.getParent());
+ if (!directory.exists() && !directory.mkdirs()) {
+ GlobalOptions.err.println
+ ("Could not create directory "
+ + directory.getPath() + ", check permissions.");
+ }
+ writer = new TabbedPrintWriter
+ (new BufferedOutputStream(new FileOutputStream(file)),
+ imports, false);
+ }
+
+ GlobalOptions.err.println(className);
+
+ ClassAnalyzer clazzAna = new ClassAnalyzer(clazz, imports);
+ clazzAna.dumpJavaFile(writer);
+
+ if (destZip != null) {
+ writer.flush();
+ destZip.closeEntry();
+ } else if (destDir != null)
+ writer.close();
+ /* Now is a good time to clean up */
+ System.gc();
+ } catch (IOException ex) {
+ GlobalOptions.err.println
+ ("Can't write source of "+className+".");
+ GlobalOptions.err.println("Check the permissions.");
+ ex.printStackTrace(GlobalOptions.err);
+ }
+ }
+
public static void main(String[] params) {
+ try {
+ decompile(params);
+ } catch (ExceptionInInitializerError ex) {
+ ex.getException().printStackTrace();
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ /* When AWT applications are compiled with insufficient
+ * classpath the type guessing by reflection code can
+ * generate an awt thread that will prevent normal
+ * exiting.
+ */
+ System.exit(0);
+ }
+
+ public static void decompile(String[] params) {
if (params.length == 0) {
usage();
return;
}
String classPath = System.getProperty("java.class.path")
- .replace(File.pathSeparatorChar, SearchPath.altPathSeparatorChar);
+ .replace(File.pathSeparatorChar, Decompiler.altPathSeparatorChar);
+ String bootClassPath = System.getProperty("sun.boot.class.path");
+ if (bootClassPath != null)
+ classPath += Decompiler.altPathSeparatorChar
+ + bootClassPath.replace(File.pathSeparatorChar,
+ Decompiler.altPathSeparatorChar);
+
String destDir = null;
int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_LIMIT;
@@ -202,6 +284,8 @@ public class Main extends Options {
outputStyle = SUN_STYLE;
else if ("gnu".startsWith(arg))
outputStyle = GNU_STYLE;
+ else if ("pascal".startsWith(arg))
+ outputStyle = Options.PASCAL_STYLE;
else {
GlobalOptions.err.println
("jode.decompiler.Main: Unknown style `"+arg+"'.");
@@ -246,7 +330,7 @@ public class Main extends Options {
}
if (errorInParams)
return;
- ClassInfo.setClassPath(classPath.toString());
+ ClassInfo.setClassPath(classPath);
ImportHandler imports = new ImportHandler(importPackageLimit,
importClassLimit);
@@ -268,51 +352,32 @@ public class Main extends Options {
}
for (int i= g.getOptind(); i< params.length; i++) {
try {
- ClassInfo clazz;
- try {
- clazz = ClassInfo.forName(params[i]);
- } catch (IllegalArgumentException ex) {
- GlobalOptions.err.println
- ("`"+params[i]+"' is not a class name");
- continue;
- }
- if (skipClass(clazz))
- continue;
-
- String filename =
- params[i].replace('.', File.separatorChar)+".java";
- if (destZip != null) {
- writer.flush();
- destZip.putNextEntry(new ZipEntry(filename));
- } else if (destDir != null) {
- File file = new File (destDir, filename);
- File directory = new File(file.getParent());
- if (!directory.exists() && !directory.mkdirs()) {
- GlobalOptions.err.println
- ("Could not create directory "
- + directory.getPath() + ", check permissions.");
+ if ((params[i].endsWith(".jar") || params[i].endsWith(".zip"))
+ && new File(params[i]).isFile()) {
+ /* The user obviously wants to decompile a jar/zip file.
+ * Lets do him a pleasure and allow this.
+ */
+ ClassInfo.setClassPath(params[i]
+ + Decompiler.altPathSeparatorChar
+ + classPath);
+ Enumeration enum = new ZipFile(params[i]).entries();
+ while (enum.hasMoreElements()) {
+ String entry
+ = ((ZipEntry) enum.nextElement()).getName();
+ if (entry.endsWith(".class")) {
+ entry = entry.substring(0, entry.length() - 6)
+ .replace('/', '.');
+ decompileClass(entry, destZip, destDir,
+ writer, imports);
+ }
}
- writer = new TabbedPrintWriter
- (new BufferedOutputStream(new FileOutputStream(file)),
- imports, false);
- }
-
- GlobalOptions.err.println(params[i]);
-
- ClassAnalyzer clazzAna = new ClassAnalyzer(clazz, imports);
- clazzAna.dumpJavaFile(writer);
-
- if (destZip != null) {
- writer.flush();
- destZip.closeEntry();
- } else if (destDir != null)
- writer.close();
- /* Now is a good time to clean up */
- System.gc();
+ ClassInfo.setClassPath(classPath);
+ } else
+ decompileClass(params[i], destZip, destDir,
+ writer, imports);
} catch (IOException ex) {
GlobalOptions.err.println
- ("Can't write source of "+params[i]+".");
- GlobalOptions.err.println("Check the permissions.");
+ ("Can't read zip file " + params[i] + ".");
ex.printStackTrace(GlobalOptions.err);
}
}
diff --git a/jode/jode/decompiler/MethodAnalyzer.java.in b/jode/jode/decompiler/MethodAnalyzer.java.in
index 8b5585c..724f08d 100644
--- a/jode/jode/decompiler/MethodAnalyzer.java.in
+++ b/jode/jode/decompiler/MethodAnalyzer.java.in
@@ -27,6 +27,7 @@ import jode.bytecode.Handler;
import jode.bytecode.Instruction;
import jode.bytecode.LocalVariableInfo;
import jode.jvm.SyntheticAnalyzer;
+import jode.decompiler.Options;
import jode.type.*;
import jode.expr.Expression;
import jode.expr.ConstOperator;
@@ -80,6 +81,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
* The minimal visible complexity.
*/
private static double STEP_COMPLEXITY = 0.01;
+ /**
+ * The value of the strictfp modifier.
+ * JDK1.1 doesn't define it.
+ */
+ private static int STRICTFP = 0x800;
/**
* The import handler where we should register our types.
*/
@@ -316,6 +322,14 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
return minfo.isSynthetic();
}
+ /**
+ * Checks if this method is strictfp
+ * @return true, iff this method is synthetic.
+ */
+ public final boolean isStrictFP() {
+ return (minfo.getModifiers() & STRICTFP) != 0;
+ }
+
/**
* Tells if this method is the constructor$xx method generated by jikes.
* @param value true, iff this method is the jikes constructor.
@@ -593,7 +607,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
CodeVerifier verifier
= new CodeVerifier(getClazz(), minfo, code);
try {
- verifier.verify();
+ verifier.verify();
} catch (VerifyException ex) {
ex.printStackTrace(GlobalOptions.err);
throw new jode.AssertError("Verification error");
@@ -719,7 +733,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (synth.getKind() == synth.GETCLASS)
return true;
if (synth.getKind() >= synth.ACCESSGETFIELD
- && synth.getKind() <= synth.ACCESSCONSTRUCTOR
+ && synth.getKind() <= synth.ACCESSDUPPUTSTATIC
&& (Options.options & Options.OPTION_INNER) != 0
&& (Options.options & Options.OPTION_ANON) != 0)
return true;
@@ -746,10 +760,12 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (isJikesBlockInitializer)
return true;
- /* The default constructor must be empty of course */
+ /* The default constructor must be empty
+ * and mustn't throw exceptions */
if (getMethodHeader() == null
|| !(getMethodHeader().getBlock() instanceof jode.flow.EmptyBlock)
- || !getMethodHeader().hasNoJumps())
+ || !getMethodHeader().hasNoJumps()
+ || exceptions.length > 0)
return false;
if (declareAsConstructor
@@ -839,6 +855,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
if (isConstructor() && isStatic())
modifiedModifiers &= ~(Modifier.FINAL | Modifier.PUBLIC
| Modifier.PROTECTED | Modifier.PRIVATE);
+ modifiedModifiers &= ~STRICTFP;
writer.startOp(writer.NO_PAREN, 1);
String delim = "";
@@ -847,11 +864,28 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
delim = " ";
}
+
String modif = Modifier.toString(modifiedModifiers);
if (modif.length() > 0) {
writer.print(delim + modif);
delim = " ";
}
+ if (isStrictFP()) {
+ /* The STRICTFP modifier is set.
+ * We handle it, since java.lang.reflect.Modifier is too dumb.
+ */
+
+ /* If STRICTFP is already set for class don't set it for method.
+ * And don't set STRICTFP for native methods or constructors.
+ */
+ if (!classAnalyzer.isStrictFP()
+ && !isConstructor()
+ && (modifiedModifiers & Modifier.NATIVE) == 0) {
+ writer.print(delim + "strictfp");
+ delim = " ";
+ }
+ }
+
if (isConstructor
&& (isStatic()
|| (classAnalyzer.getName() == null
@@ -866,6 +900,8 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
writer.print(" " + methodName);
}
writer.breakOp();
+ if ((Options.outputStyle & Options.GNU_SPACING) != 0)
+ writer.print(" ");
writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0);
int offset = skipParams + (isStatic() ? 0 : 1);
@@ -895,11 +931,11 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
}
writer.endOp();
if (code != null) {
- writer.openBrace();
+ writer.openBraceNoIndent();
writer.tab();
methodHeader.dumpSource(writer);
writer.untab();
- writer.closeBrace();
+ writer.closeBraceNoIndent();
} else
writer.println(";");
writer.popScope();
@@ -1008,8 +1044,7 @@ public class MethodAnalyzer implements Scope, ClassDeclarer {
expr).getSubExpressions()[0];
if (expr instanceof ThisOperator) {
outerValueArray[j] =
- new ThisOperator(((ThisOperator)
- expr).getClassInfo());
+ new ThisOperator(((ThisOperator) expr).getClassInfo());
continue;
}
LocalInfo li = null;
diff --git a/jode/jode/decompiler/Options.java b/jode/jode/decompiler/Options.java
index a083181..e34f750 100644
--- a/jode/jode/decompiler/Options.java
+++ b/jode/jode/decompiler/Options.java
@@ -22,10 +22,13 @@ import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
public class Options {
- public static final int TAB_SIZE_MASK = 0x0f;
- public static final int BRACE_AT_EOL = 0x10;
- public static final int SUN_STYLE = 0x14;
- public static final int GNU_STYLE = 0x02;
+ public static final int TAB_SIZE_MASK = 0x0f;
+ public static final int BRACE_AT_EOL = 0x10;
+ public static final int BRACE_FLUSH_LEFT = 0x20;
+ public static final int GNU_SPACING = 0x40;
+ public static final int SUN_STYLE = 0x14;
+ public static final int GNU_STYLE = 0x42;
+ public static final int PASCAL_STYLE = 0x24;
public static final int OPTION_LVT = 0x0001;
public static final int OPTION_INNER = 0x0002;
diff --git a/jode/jode/decompiler/OuterValues.java b/jode/jode/decompiler/OuterValues.java
index 580c7f5..d38f3fd 100644
--- a/jode/jode/decompiler/OuterValues.java
+++ b/jode/jode/decompiler/OuterValues.java
@@ -71,6 +71,7 @@ public class OuterValues
private Expression[] head;
private Vector ovListeners;
private boolean jikesAnonymousInner;
+ private boolean implicitOuterClass;
/**
* The maximal number of parameters used for outer values.
@@ -270,6 +271,17 @@ public class OuterValues
return jikesAnonymousInner;
}
+ /**
+ * Javac 1.3 doesn't give an outer class reference for anonymous
+ * classes that extend inner classes, provided the outer class is
+ * the normal this parameter. Instead it takes a normal outer
+ * value parameter for this. This method tells if this is such a
+ * class.
+ */
+ public boolean isImplicitOuterClass() {
+ return implicitOuterClass;
+ }
+
public void addOuterValueListener(OuterValueListener l) {
if (ovListeners == null)
ovListeners = new Vector();
@@ -285,6 +297,10 @@ public class OuterValues
jikesAnonymousInner = value;
}
+ public void setImplicitOuterClass(boolean value) {
+ implicitOuterClass = value;
+ }
+
private static int countSlots(Expression[] exprs, int length) {
int slots = 0;
for (int i=0; i < length; i++)
@@ -346,8 +362,8 @@ public class OuterValues
}
if (jikesAnonymousInner)
sb.append("!jikesAnonymousInner");
+ if (implicitOuterClass)
+ sb.append("!implicitOuterClass");
return sb.append("]").toString();
}
}
-
-
diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java
index b8df179..b4cbbe9 100644
--- a/jode/jode/decompiler/TabbedPrintWriter.java
+++ b/jode/jode/decompiler/TabbedPrintWriter.java
@@ -101,11 +101,8 @@ public class TabbedPrintWriter {
}
public void startOp(int opts, int penalty, int pos) {
- if (startPos != -1) {
- System.err.println("WARNING: missing breakOp");
- Thread.dumpStack();
- return;
- }
+ if (startPos != -1)
+ throw new InternalError("missing breakOp");
startPos = pos;
options = opts;
breakPenalty = penalty;
@@ -120,17 +117,15 @@ public class TabbedPrintWriter {
public void endOp(int pos) {
endPos = pos;
if (childBPs.size() == 1) {
- BreakPoint child =
- (BreakPoint) currentBP.childBPs.elementAt(0);
- if (child.startPos == -1) {
- startPos = endPos = -1;
- childBPs = null;
- } else if (child.startPos == currentBP.startPos
- && child.endPos == currentBP.endPos) {
- if (options == DONT_BREAK)
- options = child.options;
- childBPs = child.childBPs;
- }
+ /* There is no breakpoint in this op, replace this with
+ * our child, if possible.
+ */
+ BreakPoint child = (BreakPoint) childBPs.elementAt(0);
+ options = Math.min(options, child.options);
+ startPos = child.startPos;
+ endPos = child.endPos;
+ breakPenalty = child.breakPenalty;
+ childBPs = child.childBPs;
}
}
@@ -541,8 +536,9 @@ public class TabbedPrintWriter {
Stack state = new Stack();
int pos = currentLine.length();
while (currentBP.parentBP != null) {
- state.push(new Integer(currentBP.options));
state.push(new Integer(currentBP.breakPenalty));
+ /* We don't want parentheses or unconventional line breaking */
+ currentBP.options = DONT_BREAK;
currentBP.endPos = pos;
currentBP = currentBP.parentBP;
}
@@ -553,8 +549,7 @@ public class TabbedPrintWriter {
Stack state = (Stack) s;
while (!state.isEmpty()) {
int penalty = ((Integer) state.pop()).intValue();
- int options = ((Integer) state.pop()).intValue();
- startOp(options, penalty);
+ startOp(DONT_BREAK, penalty);
}
}
@@ -746,12 +741,38 @@ public class TabbedPrintWriter {
} else {
if (currentLine.length() > 0)
println();
- if (currentIndent > 0)
+ if ((Options.outputStyle & Options.BRACE_FLUSH_LEFT) == 0
+ && currentIndent > 0)
tab();
println("{");
}
}
+ public void openBraceClass() {
+ if (currentLine.length() > 0) {
+ if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0)
+ print(" ");
+ else
+ println();
+ }
+ println("{");
+ }
+
+ /**
+ * Print a opening brace with the current indentation style.
+ * Called at the end the line of a method declaration.
+ */
+ public void openBraceNoIndent() {
+ if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0) {
+ print(currentLine.length() > 0 ? " {" : "{");
+ println();
+ } else {
+ if (currentLine.length() > 0)
+ println();
+ println("{");
+ }
+ }
+
/**
* Print a opening brace with the current indentation style.
* Called at the end of the line of the instance that opens the
@@ -763,7 +784,8 @@ public class TabbedPrintWriter {
else {
if (currentLine.length() > 0)
println();
- if (currentIndent > 0)
+ if ((Options.outputStyle & Options.BRACE_FLUSH_LEFT) == 0
+ && currentIndent > 0)
tab();
println("{");
}
@@ -774,19 +796,14 @@ public class TabbedPrintWriter {
print("} ");
else {
println("}");
- if (currentIndent > 0)
+ if ((Options.outputStyle & Options.BRACE_FLUSH_LEFT) == 0
+ && currentIndent > 0)
untab();
}
}
- public void closeBraceNoSpace() {
- if ((Options.outputStyle & Options.BRACE_AT_EOL) != 0)
- print("}");
- else {
- println("}");
- if (currentIndent > 0)
- untab();
- }
+ public void closeBraceClass() {
+ print("}");
}
public void closeBrace() {
@@ -794,11 +811,16 @@ public class TabbedPrintWriter {
println("}");
else {
println("}");
- if (currentIndent > 0)
+ if ((Options.outputStyle & Options.BRACE_FLUSH_LEFT) == 0
+ && currentIndent > 0)
untab();
}
}
+ public void closeBraceNoIndent() {
+ println("}");
+ }
+
public void flush() {
pw.flush();
}
diff --git a/jode/jode/expr/ArrayStoreOperator.java b/jode/jode/expr/ArrayStoreOperator.java
index 796262b..3f1e736 100644
--- a/jode/jode/expr/ArrayStoreOperator.java
+++ b/jode/jode/expr/ArrayStoreOperator.java
@@ -32,4 +32,29 @@ public class ArrayStoreOperator extends ArrayLoadOperator
public boolean matches(Operator loadop) {
return loadop instanceof ArrayLoadOperator;
}
+
+ public void dumpExpression(TabbedPrintWriter writer)
+ throws java.io.IOException {
+ Type arrType = subExpressions[0].getType().getHint();
+ if (arrType instanceof ArrayType) {
+ Type elemType = ((ArrayType) arrType).getElementType();
+ if (!elemType.isOfType(getType())) {
+ /* We need an explicit widening cast */
+ writer.print("(");
+ writer.startOp(writer.EXPL_PAREN, 1);
+ writer.print("(");
+ writer.printType(Type.tArray(getType().getHint()));
+ writer.print(") ");
+ writer.breakOp();
+ subExpressions[0].dumpExpression(writer, 700);
+ writer.print(")");
+ writer.breakOp();
+ writer.print("[");
+ subExpressions[1].dumpExpression(writer, 0);
+ writer.print("]");
+ return;
+ }
+ }
+ super.dumpExpression(writer);
+ }
}
diff --git a/jode/jode/expr/CompareToIntOperator.java b/jode/jode/expr/CompareToIntOperator.java
index 82f77f3..0589116 100644
--- a/jode/jode/expr/CompareToIntOperator.java
+++ b/jode/jode/expr/CompareToIntOperator.java
@@ -54,6 +54,7 @@ public class CompareToIntOperator extends Operator {
throws java.io.IOException
{
subExpressions[0].dumpExpression(writer, 550);
+ writer.breakOp();
writer.print(" <=>");
if (allowsNaN)
writer.print(greaterOnNaN ? "g" : "l");
diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java
index 9cfcb9b..f9ac42d 100644
--- a/jode/jode/expr/ConstOperator.java
+++ b/jode/jode/expr/ConstOperator.java
@@ -109,7 +109,7 @@ public class ConstOperator extends NoArgOperator {
return false;
}
- public void makeInitializer() {
+ public void makeInitializer(Type type) {
isInitializer = true;
}
diff --git a/jode/jode/expr/ConstantArrayOperator.java b/jode/jode/expr/ConstantArrayOperator.java
index 0a3be5a..eb9269e 100644
--- a/jode/jode/expr/ConstantArrayOperator.java
+++ b/jode/jode/expr/ConstantArrayOperator.java
@@ -48,7 +48,7 @@ public class ConstantArrayOperator extends Operator {
empty = new ConstOperator(emptyVal);
empty.setType(argType);
- empty.makeInitializer();
+ empty.makeInitializer(argType);
initOperands(size);
for (int i=0; i < subExpressions.length; i++)
setSubExpressions(i, empty);
@@ -74,7 +74,7 @@ public class ConstantArrayOperator extends Operator {
setType(Type.tSuperType(Type.tArray(value.getType())));
subExpressions[index] = value;
value.parent = this;
- value.makeInitializer();
+ value.makeInitializer(argType);
return true;
}
@@ -82,8 +82,9 @@ public class ConstantArrayOperator extends Operator {
return 200;
}
- public void makeInitializer() {
- isInitializer = true;
+ public void makeInitializer(Type type) {
+ if (type.getHint().isOfType(getType()))
+ isInitializer = true;
}
public Expression simplify() {
diff --git a/jode/jode/expr/Expression.java.in b/jode/jode/expr/Expression.java.in
index 8e2cd4f..d7127da 100644
--- a/jode/jode/expr/Expression.java.in
+++ b/jode/jode/expr/Expression.java.in
@@ -53,23 +53,7 @@ public abstract class Expression {
}
public void updateParentType(Type otherType) {
- Type newType = otherType.intersection(type);
- if (type.equals(newType))
- return;
-
- if (newType == Type.tError) {
- if (otherType == Type.tError) {
- // Don't propagate type errors.
- return;
- }
- GlobalOptions.err.println("updateParentType: Type error in "
- +this+": merging "+getType()
- +" and "+otherType);
- if ((GlobalOptions.debuggingFlags
- & GlobalOptions.DEBUG_TYPES) != 0)
- Thread.dumpStack();
- }
- type = newType;
+ setType(otherType);
if (parent != null)
parent.updateType();
}
@@ -224,7 +208,7 @@ public abstract class Expression {
return null;
}
- public void makeInitializer() {
+ public void makeInitializer(Type type) {
}
public boolean isConstant() {
@@ -299,6 +283,7 @@ public abstract class Expression {
dumpExpression(writer);
} catch (RuntimeException ex) {
writer.print("(RUNTIME ERROR IN EXPRESSION)");
+ ex.printStackTrace(GlobalOptions.err);
}
if (needEndOp2) {
diff --git a/jode/jode/expr/FieldOperator.java.in b/jode/jode/expr/FieldOperator.java.in
index 951f874..7d24ba1 100644
--- a/jode/jode/expr/FieldOperator.java.in
+++ b/jode/jode/expr/FieldOperator.java.in
@@ -25,6 +25,7 @@ import jode.bytecode.FieldInfo;
import jode.bytecode.ClassInfo;
import jode.bytecode.Reference;
import jode.bytecode.InnerClassInfo;
+import jode.bytecode.TypeSignature;
import jode.decompiler.MethodAnalyzer;
import jode.decompiler.ClassAnalyzer;
import jode.decompiler.MethodAnalyzer;
@@ -33,6 +34,7 @@ import jode.decompiler.Options;
import jode.decompiler.TabbedPrintWriter;
import jode.decompiler.Scope;
+import java.lang.reflect.Modifier;
import @COLLECTIONS@.Collection;
/**
@@ -120,6 +122,33 @@ public abstract class FieldOperator extends Operator {
return Type.tType(ref.getType());
}
+ private static FieldInfo getFieldInfo(ClassInfo clazz,
+ String name, String type) {
+ while (clazz != null) {
+ FieldInfo field = clazz.findField(name, type);
+ if (field != null)
+ return field;
+
+ ClassInfo[] ifaces = clazz.getInterfaces();
+ for (int i = 0; i < ifaces.length; i++) {
+ field = getFieldInfo(ifaces[i], name, type);
+ if (field != null)
+ return field;
+ }
+
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+ public FieldInfo getFieldInfo() {
+ ClassInfo clazz;
+ if (ref.getClazz().charAt(0) == '[')
+ clazz = ClassInfo.javaLangObject;
+ else
+ clazz = TypeSignature.getClassInfo(ref.getClazz());
+ return getFieldInfo(clazz, ref.getName(), ref.getType());
+ }
+
public boolean needsCast(Type type) {
if (type instanceof NullType)
return true;
@@ -129,6 +158,37 @@ public abstract class FieldOperator extends Operator {
ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo();
ClassInfo parClazz = ((ClassInterfacesType) type).getClassInfo();
+ FieldInfo field = clazz.findField(ref.getName(), ref.getType());
+
+ find_field:
+ while (field == null) {
+ ClassInfo ifaces[] = clazz.getInterfaces();
+ for (int i = 0; i < ifaces.length; i++) {
+ field = ifaces[i].findField(ref.getName(), ref.getType());
+ if (field != null)
+ break find_field;
+ }
+ clazz = clazz.getSuperclass();
+ if (clazz == null)
+ /* Weird, field not existing? */
+ return false;
+ field = clazz.findField(ref.getName(), ref.getType());
+ }
+ if (Modifier.isPrivate(field.getModifiers()))
+ return parClazz != clazz;
+ else if ((field.getModifiers()
+ & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
+ /* Field is protected. We need a cast if parClazz is in
+ * other package than clazz.
+ */
+ int lastDot = clazz.getName().lastIndexOf('.');
+ if (lastDot == -1
+ || lastDot != parClazz.getName().lastIndexOf('.')
+ || !(parClazz.getName()
+ .startsWith(clazz.getName().substring(0,lastDot))))
+ return true;
+ }
+
while (clazz != parClazz && clazz != null) {
FieldInfo[] fields = parClazz.getFields();
for (int i = 0; i < fields.length; i++) {
@@ -216,20 +276,8 @@ public abstract class FieldOperator extends Operator {
*/
getField() == null
&& writer.conflicts(fieldName, null,
- Scope.NOSUPERFIELDNAME))) {
-
- ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
- while (ana.getParent() instanceof ClassAnalyzer
- && ana != scope)
- ana = (ClassAnalyzer) ana.getParent();
- if (ana == scope)
- // For a simple outer class we can say this
- writer.print("this");
- else {
- // For a class that owns a method that owns
- // us, we have to give the full class name
- thisOp.dumpExpression(writer, 950);
- }
+ Scope.NOSUPERFIELDNAME))) {
+ thisOp.dumpExpression(writer, 950);
writer.breakOp();
writer.print(".");
}
diff --git a/jode/jode/expr/IIncOperator.java b/jode/jode/expr/IIncOperator.java
index 0d1f5ee..9053108 100644
--- a/jode/jode/expr/IIncOperator.java
+++ b/jode/jode/expr/IIncOperator.java
@@ -81,7 +81,9 @@ public class IIncOperator extends Operator
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
- subExpressions[0].dumpExpression(writer, 950);
+ writer.startOp(writer.NO_PAREN, 2);
+ subExpressions[0].dumpExpression(writer);
+ writer.endOp();
writer.print(getOperatorString() + value);
}
}
diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java
index 586460e..1e4ad3b 100644
--- a/jode/jode/expr/IfThenElseOperator.java
+++ b/jode/jode/expr/IfThenElseOperator.java
@@ -39,9 +39,9 @@ public class IfThenElseOperator extends Operator {
}
public void updateType() {
- Type subType = Type.tSuperType(subExpressions[1].getType())
+ Type commonType = Type.tSuperType(subExpressions[1].getType())
.intersection(Type.tSuperType(subExpressions[2].getType()));
- updateParentType(subType);
+ updateParentType(commonType);
}
public Expression simplify() {
@@ -59,37 +59,46 @@ public class IfThenElseOperator extends Operator {
}
}
if (subExpressions[0] instanceof CompareUnaryOperator
- && (subExpressions[1] instanceof GetFieldOperator)
- && (subExpressions[2] instanceof StoreInstruction)) {
- // Check for
- // class$classname != null ? class$classname :
- // (class$classname = class$("classname"))
- // and replace with
- // classname.class
+ && ((((CompareUnaryOperator) subExpressions[0])
+ .getOperatorIndex() & ~1) == Operator.COMPARE_OP)) {
CompareUnaryOperator cmp
= (CompareUnaryOperator) subExpressions[0];
- GetFieldOperator get = (GetFieldOperator) subExpressions[1];
- StoreInstruction put = (StoreInstruction) subExpressions[2];
- FieldAnalyzer field;
- if (cmp.getOperatorIndex() == Operator.NOTEQUALS_OP
- && put.getLValue() instanceof PutFieldOperator
- && ((field = ((PutFieldOperator)put.getLValue()).getField())
- != null) && field.isSynthetic()
- && put.lvalueMatches(get)
- && cmp.subExpressions[0] instanceof GetFieldOperator
- && put.lvalueMatches((GetFieldOperator)cmp.subExpressions[0])
- && put.subExpressions[1] instanceof InvokeOperator) {
- InvokeOperator invoke = (InvokeOperator) put.subExpressions[1];
- if (invoke.isGetClass()
- && invoke.subExpressions[0] instanceof ConstOperator
- && (invoke.subExpressions[0].getType()
- .equals(Type.tString))) {
- String clazz = (String)
- ((ConstOperator)invoke.subExpressions[0]).getValue();
- if (field.setClassConstant(clazz))
- return new ClassFieldOperator(clazz.charAt(0) == '['
- ? Type.tType(clazz)
- : Type.tClass(clazz));
+ int cmpType = cmp.getOperatorIndex() & 1;
+ if ((subExpressions[2 - cmpType] instanceof GetFieldOperator)
+ && (subExpressions[1 + cmpType] instanceof StoreInstruction)) {
+ // Check for
+ // class$classname != null ? class$classname :
+ // (class$classname = class$("classname"))
+ // and replace with
+ // classname.class
+ GetFieldOperator get
+ = (GetFieldOperator) subExpressions[2 - cmpType];
+ StoreInstruction put
+ = (StoreInstruction) subExpressions[1 + cmpType];
+ int opIndex = cmp.getOperatorIndex();
+ FieldAnalyzer field;
+ if (put.getLValue() instanceof PutFieldOperator
+ && ((field = ((PutFieldOperator)put.getLValue())
+ .getField()) != null) && field.isSynthetic()
+ && put.lvalueMatches(get)
+ && (cmp.subExpressions[0] instanceof GetFieldOperator)
+ && put.lvalueMatches((GetFieldOperator)
+ cmp.subExpressions[0])
+ && put.subExpressions[1] instanceof InvokeOperator) {
+ InvokeOperator invoke = (InvokeOperator)
+ put.subExpressions[1];
+ if (invoke.isGetClass()
+ && invoke.subExpressions[0] instanceof ConstOperator
+ && (invoke.subExpressions[0].getType()
+ .equals(Type.tString))) {
+ String clazz = (String)
+ ((ConstOperator)invoke.subExpressions[0])
+ .getValue();
+ if (field.setClassConstant(clazz))
+ return new ClassFieldOperator
+ (clazz.charAt(0) == '['
+ ? Type.tType(clazz) : Type.tClass(clazz));
+ }
}
}
}
diff --git a/jode/jode/expr/InvokeOperator.java.in b/jode/jode/expr/InvokeOperator.java.in
index 5c6b131..6ded1f6 100644
--- a/jode/jode/expr/InvokeOperator.java.in
+++ b/jode/jode/expr/InvokeOperator.java.in
@@ -58,6 +58,7 @@ public final class InvokeOperator extends Operator
int methodFlag;
MethodType methodType;
String methodName;
+ Reference ref;
int skippedArgs;
Type classType;
Type[] hints;
@@ -127,6 +128,7 @@ public final class InvokeOperator extends Operator
public InvokeOperator(MethodAnalyzer methodAnalyzer,
int methodFlag, Reference reference) {
super(Type.tUnknown, 0);
+ this.ref = reference;
this.methodType = Type.tMethod(reference.getType());
this.methodName = reference.getName();
this.classType = Type.tType(reference.getClazz());
@@ -166,6 +168,25 @@ public final class InvokeOperator extends Operator
return methodName;
}
+ private static MethodInfo getMethodInfo(ClassInfo clazz,
+ String name, String type) {
+ while (clazz != null) {
+ MethodInfo method = clazz.findMethod(name, type);
+ if (method != null)
+ return method;
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+ public MethodInfo getMethodInfo() {
+ ClassInfo clazz;
+ if (ref.getClazz().charAt(0) == '[')
+ clazz = ClassInfo.javaLangObject;
+ else
+ clazz = TypeSignature.getClassInfo(ref.getClazz());
+ return getMethodInfo(clazz, ref.getName(), ref.getType());
+ }
+
public Type getClassType() {
return classType;
}
@@ -201,7 +222,7 @@ public final class InvokeOperator extends Operator
}
/**
- * Makes a non void expression out of this store instruction.
+ * Makes a non void expression, in case this is a constructor.
*/
public void makeNonVoid() {
if (type != Type.tVoid)
@@ -398,12 +419,12 @@ public final class InvokeOperator extends Operator
* @return true if this is the magic class$ method, false otherwise.
*/
public boolean isGetClass() {
- if (isThis()) {
- SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
- if (synth != null && synth.getKind() == SyntheticAnalyzer.GETCLASS)
- return true;
- }
- return false;
+ MethodAnalyzer mana = getMethodAnalyzer();
+ if (mana == null)
+ return false;
+ SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
+ return (synth != null
+ && synth.getKind() == SyntheticAnalyzer.GETCLASS);
}
class Environment extends SimpleRuntimeEnvironment {
@@ -419,8 +440,10 @@ public final class InvokeOperator extends Operator
Object cls, Object[] params)
throws InterpreterException, InvocationTargetException {
if (cls == null && ref.getClazz().equals(classSig)) {
- BytecodeInfo info =
- ClassInfo.forName(ref.getClazz())
+ String clazzName = ref.getClazz();
+ clazzName = clazzName.substring(1, ref.getClazz().length() - 1)
+ .replace('/', '.');
+ BytecodeInfo info = ClassInfo.forName(clazzName)
.findMethod(ref.getName(), ref.getType())
.getBytecode();
if (info != null)
@@ -572,6 +595,7 @@ public final class InvokeOperator extends Operator
if (getMethodAnalyzer() != null) {
SyntheticAnalyzer synth = getMethodAnalyzer().getSynthetic();
if (synth != null) {
+ int unifyParam = synth.getUnifyParam();
Expression op = null;
switch (synth.getKind()) {
case SyntheticAnalyzer.ACCESSGETFIELD:
@@ -583,14 +607,20 @@ public final class InvokeOperator extends Operator
synth.getReference());
break;
case SyntheticAnalyzer.ACCESSPUTFIELD:
+ case SyntheticAnalyzer.ACCESSDUPPUTFIELD:
op = new StoreInstruction
(new PutFieldOperator(methodAnalyzer, false,
synth.getReference()));
+ if (synth.getKind() == synth.ACCESSDUPPUTFIELD)
+ ((StoreInstruction) op).makeNonVoid();
break;
case SyntheticAnalyzer.ACCESSPUTSTATIC:
+ case SyntheticAnalyzer.ACCESSDUPPUTSTATIC:
op = new StoreInstruction
(new PutFieldOperator(methodAnalyzer, true,
synth.getReference()));
+ if (synth.getKind() == synth.ACCESSDUPPUTSTATIC)
+ ((StoreInstruction) op).makeNonVoid();
break;
case SyntheticAnalyzer.ACCESSMETHOD:
op = new InvokeOperator(methodAnalyzer, ACCESSSPECIAL,
@@ -601,9 +631,9 @@ public final class InvokeOperator extends Operator
synth.getReference());
break;
case SyntheticAnalyzer.ACCESSCONSTRUCTOR:
- if (subExpressions[1] instanceof ConstOperator
+ if (subExpressions[unifyParam] instanceof ConstOperator
&& ((ConstOperator)
- subExpressions[1]).getValue() == null) {
+ subExpressions[unifyParam]).getValue() == null) {
op = new InvokeOperator(methodAnalyzer, CONSTRUCTOR,
synth.getReference());
}
@@ -613,7 +643,7 @@ public final class InvokeOperator extends Operator
if (op != null) {
if (subExpressions != null) {
for (int i=subExpressions.length; i-- > 0; ) {
- if (i == 1 && synth.getKind()
+ if (i == unifyParam && synth.getKind()
== SyntheticAnalyzer.ACCESSCONSTRUCTOR)
// skip the null param.
continue;
@@ -633,9 +663,35 @@ public final class InvokeOperator extends Operator
Type realClassType;
if (methodFlag == STATIC)
realClassType = classType;
- else {
- if (param == 0)
- return paramTypes[0] instanceof NullType;
+ else if (param == 0) {
+ if (paramTypes[0] instanceof NullType)
+ return true;
+ if (!(paramTypes[0] instanceof ClassInterfacesType
+ && classType instanceof ClassInterfacesType))
+ return false;
+
+ ClassInfo clazz = ((ClassInterfacesType) classType).getClassInfo();
+ ClassInfo parClazz
+ = ((ClassInterfacesType) paramTypes[0]).getClassInfo();
+ MethodInfo method = getMethodInfo();
+ if (method == null)
+ /* This is a NoSuchMethodError */
+ return false;
+ if (Modifier.isPrivate(method.getModifiers()))
+ return parClazz != clazz;
+ else if ((method.getModifiers()
+ & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
+ /* Method is protected. We need a cast if parClazz is in
+ * other package than clazz.
+ */
+ int lastDot = clazz.getName().lastIndexOf('.');
+ if (lastDot != parClazz.getName().lastIndexOf('.')
+ || !(parClazz.getName()
+ .startsWith(clazz.getName().substring(0,lastDot+1))))
+ return true;
+ }
+ return false;
+ } else {
realClassType = paramTypes[0];
}
@@ -675,9 +731,10 @@ public final class InvokeOperator extends Operator
}
for (int p = offset; p < paramTypes.length; p++) {
if (!paramTypes[p]
- .isOfType(Type.tSubType(otherParamTypes[p-offset])))
+ .isOfType(Type.tSubType(otherParamTypes[p-offset]))){
/* No conflict here */
continue next_method;
+ }
}
/* There is a conflict that can be resolved by a cast. */
return true;
@@ -727,6 +784,7 @@ public final class InvokeOperator extends Operator
int arg = 1;
int length = subExpressions.length;
boolean jikesAnonymousInner = false;
+ boolean implicitOuterClass = false;
if ((Options.options & Options.OPTION_ANON) != 0
&& clazzAna != null
@@ -735,6 +793,7 @@ public final class InvokeOperator extends Operator
OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount();
jikesAnonymousInner = ov.isJikesAnonymousInner();
+ implicitOuterClass = ov.isImplicitOuterClass();
for (int i=1; i< arg; i++) {
Expression expr = subExpressions[i];
@@ -764,7 +823,9 @@ public final class InvokeOperator extends Operator
if ((Options.options & Options.OPTION_INNER) != 0
&& outer != null && outer.outer != null && outer.name != null
- && !Modifier.isStatic(outer.modifiers)) {
+ && !Modifier.isStatic(outer.modifiers)
+ && !implicitOuterClass
+ && arg < length) {
Expression outerExpr = jikesAnonymousInner
? subExpressions[--length]
@@ -826,6 +887,7 @@ public final class InvokeOperator extends Operator
boolean qualifiedNew = false;
boolean jikesAnonymousInner = false;
+ boolean implicitOuterClass = false;
/* Check if this is an anonymous constructor. In this case
@@ -833,7 +895,8 @@ public final class InvokeOperator extends Operator
* super class and anonymousNew will be set.
*/
InnerClassInfo outer = getOuterClassInfo(clazz);
- if (outer != null && outer.name == null)
+ if (outer != null && outer.name == null
+ && (Options.options & Options.OPTION_ANON) != 0)
anonymousNew = true;
clazzAna = methodAnalyzer.getClassAnalyzer(clazz);
if ((~Options.options &
@@ -846,6 +909,7 @@ public final class InvokeOperator extends Operator
OuterValues ov = clazzAna.getOuterValues();
arg += ov.getCount();
jikesAnonymousInner = ov.isJikesAnonymousInner();
+ implicitOuterClass = ov.isImplicitOuterClass();
if (outer.name == null) {
/* This is an anonymous class */
@@ -888,47 +952,55 @@ public final class InvokeOperator extends Operator
(Options.OPTION_INNER
| Options.OPTION_CONTRAFO)) == 0) {
- Expression outerExpr = jikesAnonymousInner
- ? subExpressions[--length]
+ if (implicitOuterClass) {
+ /* Outer class is "this" and is not given
+ * explicitly. No need to print something.
+ */
+ } else if (arg < length) {
+ Expression outerExpr = jikesAnonymousInner
+ ? subExpressions[--length]
: subExpressions[arg++];
- if (outerExpr instanceof CheckNullOperator) {
- CheckNullOperator cno = (CheckNullOperator) outerExpr;
- outerExpr = cno.subExpressions[0];
- } else if (!(outerExpr instanceof ThisOperator)) {
- if (!jikesAnonymousInner)
- // Bug in jikes: it doesn't do a check null.
- // We don't complain here.
- writer.print("MISSING CHECKNULL ");
- }
+ if (outerExpr instanceof CheckNullOperator) {
+ CheckNullOperator cno = (CheckNullOperator) outerExpr;
+ outerExpr = cno.subExpressions[0];
+ } else {
+ /* We used to complain about MISSING CHECKNULL
+ * here except for ThisOperators. But javac
+ * v8 doesn't seem to create CHECKNULL ops.
+ */
+ }
- if (outerExpr instanceof ThisOperator) {
- Scope scope = writer.getScope
- (((ThisOperator) outerExpr).getClassInfo(),
- Scope.CLASSSCOPE);
- if (writer.conflicts(outer.name, scope, Scope.CLASSNAME)) {
+ if (outerExpr instanceof ThisOperator) {
+ Scope scope = writer.getScope
+ (((ThisOperator) outerExpr).getClassInfo(),
+ Scope.CLASSSCOPE);
+ if (writer.conflicts(outer.name, scope, Scope.CLASSNAME)) {
+ qualifiedNew = true;
+ outerExpr.dumpExpression(writer, 950);
+ writer.breakOp();
+ writer.print(".");
+ }
+ } else {
qualifiedNew = true;
- outerExpr.dumpExpression(writer, 950);
+ if (outerExpr.getType().getCanonic()
+ instanceof NullType) {
+ writer.print("(");
+ writer.startOp(writer.EXPL_PAREN, 1);
+ writer.print("(");
+ writer.printType(Type.tClass
+ (ClassInfo.forName(outer.outer)));
+ writer.print(") ");
+ writer.breakOp();
+ outerExpr.dumpExpression(writer, 700);
+ writer.endOp();
+ writer.print(")");
+ } else
+ outerExpr.dumpExpression(writer, 950);
writer.breakOp();
writer.print(".");
}
- } else {
- qualifiedNew = true;
- if (outerExpr.getType() instanceof NullType) {
- writer.print("(");
- writer.startOp(writer.EXPL_PAREN, 1);
- writer.print("(");
- writer.printType(Type.tClass
- (ClassInfo.forName(outer.outer)));
- writer.print(") ");
- writer.breakOp();
- outerExpr.dumpExpression(writer, 700);
- writer.endOp();
- writer.print(")");
- } else
- outerExpr.dumpExpression(writer, 950);
- writer.breakOp();
- writer.print(".");
- }
+ } else
+ writer.print("MISSING OUTEREXPR ");
}
if (subExpressions[0] instanceof NewOperator
@@ -1056,30 +1128,19 @@ public final class InvokeOperator extends Operator
ThisOperator thisOp = (ThisOperator) subExpressions[0];
Scope scope = writer.getScope(thisOp.getClassInfo(),
Scope.CLASSSCOPE);
- if (writer.conflicts(methodName, scope, Scope.METHODNAME)) {
+ if (writer.conflicts(methodName, scope, Scope.METHODNAME)
+ || (/* This method is inherited from the parent of
+ * an outer class, or it is inherited from the
+ * parent of this class and there is a conflicting
+ * method in some outer class.
+ */
+ getMethodAnalyzer() == null
+ && (!isThis() ||
+ writer.conflicts(methodName, null,
+ Scope.NOSUPERMETHODNAME)))) {
thisOp.dumpExpression(writer, 950);
writer.breakOp();
writer.print(".");
- } else if (/* This is a inherited field conflicting
- * with a field name in some outer class.
- */
- getMethodAnalyzer() == null
- && writer.conflicts(methodName, null,
- Scope.NOSUPERMETHODNAME)) {
- ClassAnalyzer ana = methodAnalyzer.getClassAnalyzer();
- while (ana.getParent() instanceof ClassAnalyzer
- && ana != scope)
- ana = (ClassAnalyzer) ana.getParent();
- if (ana == scope) {
- // For a simple outer class we can say this
- writer.print("this");
- } else {
- // For a class that owns a method that owns
- // us, we have to give the full class name
- thisOp.dumpExpression(writer, 950);
- }
- writer.breakOp();
- writer.print(".");
}
} else {
if (needsCast(0, paramTypes)){
@@ -1103,6 +1164,8 @@ public final class InvokeOperator extends Operator
writer.endOp();
writer.breakOp();
+ if ((Options.outputStyle & Options.GNU_SPACING) != 0)
+ writer.print(" ");
writer.print("(");
writer.startOp(writer.EXPL_PAREN, 0);
boolean first = true;
@@ -1136,11 +1199,11 @@ public final class InvokeOperator extends Operator
* dump the source code of the anonymous class.
*/
Object state = writer.saveOps();
- writer.openBrace();
+ writer.openBraceClass();
writer.tab();
clazzAna.dumpBlock(writer);
writer.untab();
- writer.closeBraceNoSpace();
+ writer.closeBraceClass();
writer.restoreOps(state);
}
}
diff --git a/jode/jode/expr/NewArrayOperator.java b/jode/jode/expr/NewArrayOperator.java
index 4fa7dd5..2ed5895 100644
--- a/jode/jode/expr/NewArrayOperator.java
+++ b/jode/jode/expr/NewArrayOperator.java
@@ -57,6 +57,7 @@ public class NewArrayOperator extends Operator {
writer.print("new ");
writer.printType(flat.getHint());
for (int i=0; i< depth; i++) {
+ writer.breakOp();
writer.print("[");
if (i < subExpressions.length)
subExpressions[i].dumpExpression(writer, 0);
diff --git a/jode/jode/expr/PopOperator.java b/jode/jode/expr/PopOperator.java
index 4fa839c..9d55640 100644
--- a/jode/jode/expr/PopOperator.java
+++ b/jode/jode/expr/PopOperator.java
@@ -41,8 +41,17 @@ public class PopOperator extends Operator {
public void updateType() {
}
+ public int getBreakPenalty() {
+ if (subExpressions[0] instanceof Operator)
+ return ((Operator) subExpressions[0]).getBreakPenalty();
+ return 0;
+ }
+
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
- subExpressions[0].dumpExpression(writer, 0);
+ /* Don't give a priority; we can't allow parens around
+ * a statement.
+ */
+ subExpressions[0].dumpExpression(writer);
}
}
diff --git a/jode/jode/expr/PrePostFixOperator.java b/jode/jode/expr/PrePostFixOperator.java
index 1b0857c..a0ac65f 100644
--- a/jode/jode/expr/PrePostFixOperator.java
+++ b/jode/jode/expr/PrePostFixOperator.java
@@ -52,13 +52,11 @@ public class PrePostFixOperator extends Operator {
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
- boolean needBrace = false;
- int priority = 700;
- if (!postfix) {
+ if (!postfix)
writer.print(getOperatorString());
- priority = 800;
- }
- subExpressions[0].dumpExpression(writer, priority);
+ writer.startOp(writer.NO_PAREN, 2);
+ subExpressions[0].dumpExpression(writer);
+ writer.endOp();
if (postfix)
writer.print(getOperatorString());
}
diff --git a/jode/jode/expr/StoreInstruction.java b/jode/jode/expr/StoreInstruction.java
index 1870977..538ead4 100644
--- a/jode/jode/expr/StoreInstruction.java
+++ b/jode/jode/expr/StoreInstruction.java
@@ -111,7 +111,9 @@ public class StoreInstruction extends Operator
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException
{
- subExpressions[0].dumpExpression(writer, 950);
+ writer.startOp(writer.NO_PAREN, 2);
+ subExpressions[0].dumpExpression(writer);
+ writer.endOp();
writer.breakOp();
writer.print(getOperatorString());
subExpressions[1].dumpExpression(writer, 100);
diff --git a/jode/jode/expr/UnaryOperator.java b/jode/jode/expr/UnaryOperator.java
index 1722442..72c3003 100644
--- a/jode/jode/expr/UnaryOperator.java
+++ b/jode/jode/expr/UnaryOperator.java
@@ -19,6 +19,7 @@
package jode.expr;
import jode.type.Type;
+import jode.decompiler.Options;
import jode.decompiler.TabbedPrintWriter;
public class UnaryOperator extends Operator {
@@ -57,6 +58,8 @@ public class UnaryOperator extends Operator {
public void dumpExpression(TabbedPrintWriter writer)
throws java.io.IOException {
writer.print(getOperatorString());
+ if ((Options.outputStyle & Options.GNU_SPACING) != 0)
+ writer.print(" ");
subExpressions[0].dumpExpression(writer, 700);
}
}
diff --git a/jode/jode/flow/CaseBlock.java b/jode/jode/flow/CaseBlock.java
index b176261..0c7f607 100644
--- a/jode/jode/flow/CaseBlock.java
+++ b/jode/jode/flow/CaseBlock.java
@@ -19,6 +19,7 @@
package jode.flow;
import jode.expr.ConstOperator;
+import jode.type.Type;
/**
* This block represents a case instruction. A case instruction is a
@@ -173,8 +174,9 @@ public class CaseBlock extends StructuredBlock {
writer.untab();
}
ConstOperator constOp = new ConstOperator(new Integer(value));
- constOp.setType(((SwitchBlock)outer).getInstruction().getType());
- constOp.makeInitializer();
+ Type type = ((SwitchBlock)outer).getInstruction().getType();
+ constOp.setType(type);
+ constOp.makeInitializer(type);
writer.print("case " + constOp.toString() + ":");
}
if (subBlock != null) {
diff --git a/jode/jode/flow/CatchBlock.java.in b/jode/jode/flow/CatchBlock.java.in
index bc24007..f47d587 100644
--- a/jode/jode/flow/CatchBlock.java.in
+++ b/jode/jode/flow/CatchBlock.java.in
@@ -20,13 +20,16 @@
package jode.flow;
import jode.type.Type;
import jode.decompiler.LocalInfo;
+import jode.decompiler.Declarable;
import jode.expr.Expression;
import jode.expr.LocalLoadOperator;
import jode.expr.LocalStoreOperator;
+import jode.expr.NopOperator;
import jode.expr.StoreInstruction;
import jode.util.SimpleSet;
import @COLLECTIONS@.Collections;
+import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.Set;
@@ -160,6 +163,16 @@ public class CatchBlock extends StructuredBlock {
ib.appendBlock(catchBlock);
catchBlock = ib;
exceptionLocal = dummyLocal;
+ String localName = dummyLocal.guessName();
+ Iterator doneIter = done.iterator();
+ while (doneIter.hasNext()) {
+ Declarable previous = (Declarable) doneIter.next();
+ if (localName.equals(previous.getName())) {
+ /* A name conflict happened. */
+ dummyLocal.makeNameUnique();
+ break;
+ }
+ }
}
}
}
@@ -206,17 +219,19 @@ public class CatchBlock extends StructuredBlock {
} else if (firstInstr instanceof InstructionBlock) {
Expression instr =
((InstructionBlock) firstInstr).getInstruction();
- if (instr instanceof StoreInstruction
- && (((StoreInstruction)instr).getLValue()
- instanceof LocalStoreOperator)) {
- /* The exception is stored in a local variable */
- exceptionLocal = ((LocalStoreOperator)
- ((StoreInstruction)instr).getLValue())
- .getLocalInfo();
- exceptionLocal.setType(exceptionType);
- firstInstr.removeBlock();
- return true;
- }
+ if (instr instanceof StoreInstruction) {
+ StoreInstruction store = (StoreInstruction) instr;
+ if (store.getOperatorIndex() == store.OPASSIGN_OP
+ && store.getSubExpressions()[1] instanceof NopOperator
+ && store.getLValue() instanceof LocalStoreOperator) {
+ /* The exception is stored in a local variable */
+ exceptionLocal = ((LocalStoreOperator) store.getLValue())
+ .getLocalInfo();
+ exceptionLocal.setType(exceptionType);
+ firstInstr.removeBlock();
+ return true;
+ }
+ }
}
return false;
}
diff --git a/jode/jode/flow/CreateClassField.java b/jode/jode/flow/CreateClassField.java
index afaa91e..0724b00 100644
--- a/jode/jode/flow/CreateClassField.java
+++ b/jode/jode/flow/CreateClassField.java
@@ -63,10 +63,12 @@ public class CreateClassField {
return false;
InvokeOperator invoke = (InvokeOperator) store.getSubExpressions()[1];
+ if (!invoke.isGetClass())
+ return false;
+
Expression param = invoke.getSubExpressions()[0];
- if (invoke.isGetClass()
- && param instanceof ConstOperator
+ if (param instanceof ConstOperator
&& ((ConstOperator)param).getValue() instanceof String) {
String clazz = (String) ((ConstOperator)param).getValue();
if (put.getField().setClassConstant(clazz)) {
diff --git a/jode/jode/flow/EmptyBlock.java b/jode/jode/flow/EmptyBlock.java
index 840e56b..1099934 100644
--- a/jode/jode/flow/EmptyBlock.java
+++ b/jode/jode/flow/EmptyBlock.java
@@ -56,6 +56,17 @@ public class EmptyBlock extends StructuredBlock {
return block;
}
+ /**
+ * Prepends a block to this block.
+ * @return the new combined block.
+ */
+ public StructuredBlock prependBlock(StructuredBlock block) {
+ /* For empty blocks: append == prepend modulo jump */
+ block = appendBlock(block);
+ block.moveJump(this.jump);
+ return block;
+ }
+
public void dumpInstruction(TabbedPrintWriter writer)
throws java.io.IOException
{
diff --git a/jode/jode/flow/FlowBlock.java.in b/jode/jode/flow/FlowBlock.java.in
index b7a2e83..0f69a97 100644
--- a/jode/jode/flow/FlowBlock.java.in
+++ b/jode/jode/flow/FlowBlock.java.in
@@ -1,4 +1,4 @@
-/* FlowBlock Copyright (C) 1998-1999 Jochen Hoenicke.
+/* FlowBlock Copyright (C) 1998-2001 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -748,7 +748,8 @@ public class FlowBlock {
StructuredBlock last = lastModified;
while (last.outer instanceof SequentialBlock
- || last.outer instanceof TryBlock)
+ || last.outer instanceof TryBlock
+ || last.outer instanceof FinallyBlock)
last = last.outer;
if (last.outer != null)
throw new AssertError("Inconsistency");
@@ -1708,7 +1709,8 @@ public class FlowBlock {
java.io.StringWriter strw = new java.io.StringWriter();
TabbedPrintWriter writer = new TabbedPrintWriter(strw);
writer.println(super.toString() + ": "+addr+"-"+(addr+length));
- if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INOUT) != 0) {
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_INOUT) != 0) {
writer.println("in: "+in);
}
writer.tab();
@@ -1728,6 +1730,8 @@ public class FlowBlock {
}
}
return strw.toString();
+ } catch (RuntimeException ex) {
+ return super.toString() + ": (RUNTIME EXCEPTION)";
} catch (java.io.IOException ex) {
return super.toString();
}
diff --git a/jode/jode/flow/InstructionBlock.java.in b/jode/jode/flow/InstructionBlock.java.in
index ad8a4dc..dc710d2 100644
--- a/jode/jode/flow/InstructionBlock.java.in
+++ b/jode/jode/flow/InstructionBlock.java.in
@@ -116,7 +116,6 @@ public class InstructionBlock extends InstructionContainer {
* change this to a initializing variable declaration.
*/
isDeclaration = true;
- storeOp.getSubExpressions()[1].makeInitializer();
declareSet.remove(local);
}
}
@@ -144,6 +143,7 @@ public class InstructionBlock extends InstructionContainer {
local.dumpDeclaration(writer);
writer.breakOp();
writer.print(" = ");
+ store.getSubExpressions()[1].makeInitializer(local.getType());
store.getSubExpressions()[1].dumpExpression(writer.IMPL_PAREN,
writer);
writer.endOp();
diff --git a/jode/jode/flow/JsrBlock.java b/jode/jode/flow/JsrBlock.java
index 6bf2491..7e10b1d 100644
--- a/jode/jode/flow/JsrBlock.java
+++ b/jode/jode/flow/JsrBlock.java
@@ -33,14 +33,22 @@ public class JsrBlock extends StructuredBlock {
* The inner block that jumps to the subroutine.
*/
StructuredBlock innerBlock;
+ boolean good = false;
public JsrBlock(Jump subroutine, Jump next) {
innerBlock = new EmptyBlock(subroutine);
innerBlock.outer = this;
setJump(next);
}
-
+ public void setGood(boolean g) {
+ good = g;
+ }
+
+ public boolean isGood() {
+ return good;
+ }
+
/* The implementation of getNext[Flow]Block is the standard
* implementation */
diff --git a/jode/jode/flow/LoopBlock.java.in b/jode/jode/flow/LoopBlock.java.in
index 0d050bc..b5f4d1f 100644
--- a/jode/jode/flow/LoopBlock.java.in
+++ b/jode/jode/flow/LoopBlock.java.in
@@ -265,7 +265,6 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
* change this to a initializing variable declaration.
*/
isDeclaration = true;
- storeOp.getSubExpressions()[1].makeInitializer();
declareSet.remove(local);
}
}
@@ -332,6 +331,8 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
local.dumpDeclaration(writer);
writer.breakOp();
writer.print(" = ");
+ store.getSubExpressions()[1]
+ .makeInitializer(local.getType());
store.getSubExpressions()[1].dumpExpression(writer, 100);
writer.endOp();
} else
diff --git a/jode/jode/flow/Makefile.am b/jode/jode/flow/Makefile.am
index fe0175d..35bbdce 100644
--- a/jode/jode/flow/Makefile.am
+++ b/jode/jode/flow/Makefile.am
@@ -41,6 +41,7 @@ MY_JAVA_FILES = \
RetBlock.java \
ReturnBlock.java \
SequentialBlock.java \
+ SlotSet.java \
SpecialBlock.java \
StructuredBlock.java \
SwitchBlock.java \
diff --git a/jode/jode/flow/SpecialBlock.java b/jode/jode/flow/SpecialBlock.java
index fc7916b..5110d5d 100644
--- a/jode/jode/flow/SpecialBlock.java
+++ b/jode/jode/flow/SpecialBlock.java
@@ -148,6 +148,9 @@ public class SpecialBlock extends StructuredBlock {
* to:
* method_invocation()
*
+ * With java1.3 due to access$ methods the method_invocation can
+ * already be a non void store instruction.
+ *
* PUSH arg1
* PUSH arg2
* POP2
@@ -174,7 +177,8 @@ public class SpecialBlock extends StructuredBlock {
if (instr.getType().stackSize() == count) {
StructuredBlock newBlock;
- if (instr instanceof InvokeOperator) {
+ if (instr instanceof InvokeOperator
+ || instr instanceof StoreInstruction) {
Expression newExpr
= new PopOperator(instr.getType()).addOperand(instr);
prev.setInstruction(newExpr);
diff --git a/jode/jode/flow/StructuredBlock.java.in b/jode/jode/flow/StructuredBlock.java.in
index ed6e754..9d6a3e3 100644
--- a/jode/jode/flow/StructuredBlock.java.in
+++ b/jode/jode/flow/StructuredBlock.java.in
@@ -301,6 +301,18 @@ public abstract class StructuredBlock {
}
}
+ /**
+ * Prepends a block to this block.
+ * @return the new combined block.
+ */
+ public StructuredBlock prependBlock(StructuredBlock block) {
+ SequentialBlock sequBlock = new SequentialBlock();
+ sequBlock.replace(this);
+ sequBlock.setFirst(block);
+ sequBlock.setSecond(this);
+ return sequBlock;
+ }
+
/**
* Removes this block, or replaces it with an EmptyBlock.
*/
diff --git a/jode/jode/flow/TransformConstructors.java b/jode/jode/flow/TransformConstructors.java
index 1ea93ab..1e798d0 100644
--- a/jode/jode/flow/TransformConstructors.java
+++ b/jode/jode/flow/TransformConstructors.java
@@ -1,4 +1,4 @@
-/* TransformConstructors Copyright (C) 1998-1999 Jochen Hoenicke.
+/* TransformConstructors Copyright (C) 1998-2001 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@ import jode.type.MethodType;
import jode.type.Type;
import jode.bytecode.ClassInfo;
import jode.bytecode.InnerClassInfo;
+import jode.bytecode.MethodInfo;
import java.util.Vector;
import java.util.Enumeration;
@@ -99,14 +100,13 @@ public class TransformConstructors {
OuterValues outerValues;
- boolean jikesAnonInner = false;
-
public TransformConstructors(ClassAnalyzer clazzAnalyzer,
boolean isStatic, MethodAnalyzer[] cons) {
this.clazzAnalyzer = clazzAnalyzer;
this.isStatic = isStatic;
this.cons = cons;
- this.outerValues = clazzAnalyzer.getOuterValues();
+ if (!isStatic)
+ this.outerValues = clazzAnalyzer.getOuterValues();
lookForConstructorCall();
}
@@ -320,7 +320,18 @@ public class TransformConstructors {
}
}
- if (minSuperOuter > 0) {
+ if (minSuperOuter == 1
+ && superAna.getParent() instanceof ClassAnalyzer) {
+ /* Check if this is the implicit Outer Class */
+ LocalLoadOperator llop = (LocalLoadOperator) subExpr[start];
+ if (outerValues.getValueBySlot(llop.getLocalInfo().getSlot())
+ instanceof ThisOperator) {
+ minSuperOuter = 0;
+ outerValues.setImplicitOuterClass(true);
+ }
+ }
+
+ if (minSuperOuter > 0) {
if (superOV == null || superOV.getCount() < minSuperOuter) {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_CONSTRS) != 0)
@@ -573,7 +584,8 @@ public class TransformConstructors {
* @param expr the initializer to check
* @return the transformed initializer or null if expr is not valid.
*/
- public Expression transformFieldInitializer(Expression expr) {
+ private Expression transformFieldInitializer(int fieldSlot,
+ Expression expr) {
if (expr instanceof LocalVarOperator) {
if (!(expr instanceof LocalLoadOperator)) {
if ((GlobalOptions.debuggingFlags
@@ -594,11 +606,39 @@ public class TransformConstructors {
+" "+outerValues);
return null;
}
+ if (expr instanceof FieldOperator) {
+ if (expr instanceof PutFieldOperator)
+ return null;
+ FieldOperator fo = (FieldOperator) expr;
+ if (fo.getClassInfo() == clazzAnalyzer.getClazz()
+ && clazzAnalyzer.getFieldIndex(fo.getFieldName(),
+ fo.getFieldType()) >= fieldSlot)
+ return null;
+ }
+ if (expr instanceof InvokeOperator) {
+ /* Don't allow method invocations that can throw a checked
+ * exception to leave the constructor.
+ */
+ MethodInfo method = ((InvokeOperator) expr).getMethodInfo();
+ String[] excs = method == null ? null : method.getExceptions();
+ if (excs != null) {
+ ClassInfo runtimeException
+ = ClassInfo.forName("java.lang.RuntimeException");
+ ClassInfo error = ClassInfo.forName("java.lang.Error");
+ for (int i = 0; i < excs.length; i++) {
+ ClassInfo exClass = ClassInfo.forName(excs[i]);
+ if (!runtimeException.superClassOf(exClass)
+ && !error.superClassOf(exClass))
+ return null;
+ }
+ }
+ }
if (expr instanceof Operator) {
Operator op = (Operator) expr;
Expression[] subExpr = op.getSubExpressions();
for (int i=0; i< subExpr.length; i++) {
- Expression transformed = transformFieldInitializer(subExpr[i]);
+ Expression transformed
+ = transformFieldInitializer(fieldSlot, subExpr[i]);
if (transformed == null)
return null;
if (transformed != subExpr[i])
@@ -675,7 +715,7 @@ public class TransformConstructors {
break big_loop;
Expression expr = store.getSubExpressions()[1];
- expr = transformFieldInitializer(expr);
+ expr = transformFieldInitializer(field, expr);
if (expr == null)
break big_loop;
@@ -762,8 +802,14 @@ public class TransformConstructors {
}
}
+ int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(),
+ pfo.getFieldType());
+
+ if (field <= lastField)
+ return -1;
+
Expression expr = store.getSubExpressions()[1];
- expr = transformFieldInitializer(expr);
+ expr = transformFieldInitializer(field, expr);
if (expr == null)
return -1;
@@ -772,9 +818,6 @@ public class TransformConstructors {
GlobalOptions.err.println(" field " + pfo.getFieldName()
+ " = " + expr);
- int field = clazzAnalyzer.getFieldIndex(pfo.getFieldName(),
- pfo.getFieldType());
-
// if field does not exists: -1 <= lastField.
if (field <= lastField
|| !(clazzAnalyzer.getField(field).setInitializer(expr))) {
diff --git a/jode/jode/flow/TransformExceptionHandlers.java.in b/jode/jode/flow/TransformExceptionHandlers.java.in
index 1b20429..2358c86 100644
--- a/jode/jode/flow/TransformExceptionHandlers.java.in
+++ b/jode/jode/flow/TransformExceptionHandlers.java.in
@@ -99,80 +99,139 @@ public class TransformExceptionHandlers {
}
- /* simple try catch block:
+ /**
+ * Merge the try flow block with the catch flow block. This is a kind
+ * of special T2 transformation, as all jumps to the catch block are
+ * implicit (exception can be thrown everywhere).
+ *
+ * This method doesn't actually merge the contents of the blocks. The
+ * caller should do it right afterwards.
*
- * try-header
- * |- first instruction
- * | ...
- * | last instruction
- * |- optional jump (last+1)
- * | ...
- * `- catch block
+ * The flow block catchFlow mustn't have any predecessors.
+ * @param tryFlow the flow block containing the try.
+ * @param catchFlow the flow block containing the catch handler.
*/
- static void analyzeCatchBlock(Type type,
- FlowBlock tryFlow, FlowBlock catchFlow) {
+ static void mergeTryCatch(FlowBlock tryFlow, FlowBlock catchFlow) {
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_ANALYZE) != 0)
+ GlobalOptions.err.println
+ ("mergeTryCatch(" + tryFlow.getAddr()
+ + ", " + catchFlow.getAddr() + ")");
+ tryFlow.updateInOutCatch(catchFlow);
+ tryFlow.mergeSuccessors(catchFlow);
+ tryFlow.mergeAddr(catchFlow);
+ }
- StructuredBlock catchBlock = catchFlow.block;
+
+ /**
+ * Analyzes a simple try/catch block. The try and catch part are both
+ * analyzed, the try block is already created, but the catch block
+ * isn't.
+ * The catchFlow block mustn't have any predecessors.
+ *
+ * @param type The type of the exception which is caught.
+ * @param tryFlow The flow block containing the try. The contained
+ * block must be a try block.
+ * @param catchFlow the flow block containing the catch handler.
+ */
+ static void analyzeCatchBlock(Type type, FlowBlock tryFlow,
+ FlowBlock catchFlow) {
+ /* Merge try and catch flow blocks */
+ mergeTryCatch(tryFlow, catchFlow);
+ /* Insert catch block into tryFlow */
CatchBlock newBlock = new CatchBlock(type);
((TryBlock)tryFlow.block).addCatchBlock(newBlock);
newBlock.setCatchBlock(catchFlow.block);
- tryFlow.mergeSuccessors(catchFlow);
- tryFlow.mergeAddr(catchFlow);
}
/* And now the complicated parts. */
/**
- * This transforms a sub routine, that is checks if the beginning
- * local assignment matches the final ret and then returns.
+ * This transforms a sub routine, i.e. it checks if the beginning
+ * local assignment matches the final ret and removes both. It also
+ * accepts sub routines that just pop their return address.
*/
- boolean transformSubRoutine(StructuredBlock subRoutine) {
- if (!(subRoutine instanceof SequentialBlock)
- || !(subRoutine.getSubBlocks()[0] instanceof InstructionBlock))
- return false;
- SequentialBlock sequBlock = (SequentialBlock) subRoutine;
- InstructionBlock instr = (InstructionBlock)sequBlock.subBlocks[0];
-
- if (!(instr.getInstruction() instanceof StoreInstruction)
- || !(((StoreInstruction) instr.getInstruction()).getLValue()
- instanceof LocalStoreOperator))
- return false;
- LocalStoreOperator store = (LocalStoreOperator)
- ((StoreInstruction)instr.getInstruction()).getLValue();
-
- while (sequBlock.subBlocks[1] instanceof SequentialBlock)
- sequBlock = (SequentialBlock) sequBlock.subBlocks[1];
+ boolean transformSubRoutine(StructuredBlock subRoutineBlock) {
+ StructuredBlock firstBlock = subRoutineBlock;
+ if (firstBlock instanceof SequentialBlock)
+ firstBlock = subRoutineBlock.getSubBlocks()[0];
+
+ LocalInfo local = null;
+ if (firstBlock instanceof SpecialBlock) {
+ SpecialBlock popBlock
+ = (SpecialBlock) firstBlock;
+ if (popBlock.type != SpecialBlock.POP
+ || popBlock.count != 1)
+ return false;
+ } else if (firstBlock instanceof InstructionBlock) {
+ Expression expr
+ = ((InstructionBlock) firstBlock).getInstruction();
+ if (expr instanceof StoreInstruction
+ && ((StoreInstruction)
+ expr).getLValue() instanceof LocalStoreOperator) {
+ LocalStoreOperator store = (LocalStoreOperator)
+ ((StoreInstruction)expr).getLValue();
+ local = store.getLocalInfo();
+ expr = ((StoreInstruction) expr).getSubExpressions()[1];
+ }
+ if (!(expr instanceof NopOperator))
+ return false;
+ } else
+ return false;
+
+ /* We are now committed and can start changing code. Remove
+ * the first Statement which stores/removes the return
+ * address.
+ */
+ firstBlock.removeBlock();
- /* XXX - Check that the local isn't used for any other purposes
- * than RET and replace any RET with a flow control to end of
- * flow block.
+ /* We don't check if there is a RET in the middle.
*
- * This is a complicated task which isn't needed for javac nor jikes.
+ * This is a complicated task which isn't needed for javac nor
+ * jikes. We just check if the last instruction is a ret and
+ * remove this. This will never produce code with wrong semantic,
+ * as long as the bytecode was verified correctly.
*/
- if (sequBlock.subBlocks[1].jump != null
- && sequBlock.subBlocks[1].jump.destination
- == FlowBlock.END_OF_METHOD) {
- instr.removeBlock();
- return true;
+ while (subRoutineBlock instanceof SequentialBlock)
+ subRoutineBlock = subRoutineBlock.getSubBlocks()[1];
+
+ if (subRoutineBlock instanceof RetBlock
+ && (((RetBlock) subRoutineBlock).local.equals(local))) {
+ subRoutineBlock.removeBlock();
}
- if (! (sequBlock.subBlocks[1] instanceof RetBlock)
- || !(((RetBlock)sequBlock.subBlocks[1])
- .local.equals(store.getLocalInfo())))
- return false;
-
- instr.removeBlock();
- sequBlock.subBlocks[1].removeBlock();
- return true;
+ return true;
}
/**
- * Remove the JSR's jumping to the specified subRoutine. It
- * is checked if the next block is a leaving instruction, and
- * otherwise the JsrBlock is not removed (to give the user a
- * hint that something went wrong). This will also remove the
- * local javac generates for returns.
- * @param tryFlow the FlowBLock of the try block.
+ * Remove the locale that javac introduces to temporary store the return
+ * value, when it executes a finally block resp. monitorexit
+ * @param ret the ReturnBlock.
+ */
+ private void removeReturnLocal(ReturnBlock ret) {
+ StructuredBlock pred = getPredecessor(ret);
+ if (!(pred instanceof InstructionBlock))
+ return;
+ Expression instr = ((InstructionBlock) pred).getInstruction();
+ if (!(instr instanceof StoreInstruction))
+ return;
+
+ Expression retInstr = ret.getInstruction();
+ if (!(retInstr instanceof LocalLoadOperator
+ && ((StoreInstruction) instr).lvalueMatches
+ ((LocalLoadOperator) retInstr)))
+ return;
+
+ Expression rvalue = ((StoreInstruction) instr).getSubExpressions()[1];
+ ret.setInstruction(rvalue);
+ ret.replace(ret.outer);
+ }
+
+ /**
+ * Remove the JSRs jumping to the specified subRoutine. The right
+ * JSRs are marked and we can just remove them. For the other JSR
+ * instructions we replace them with a warning.
+ * @param tryFlow the FlowBlock of the try block.
* @param subRoutine the FlowBlock of the sub routine.
*/
private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
@@ -180,80 +239,94 @@ public class TransformExceptionHandlers {
jumps != null; jumps = jumps.next) {
StructuredBlock prev = jumps.prev;
- prev.removeJump();
+ prev.removeJump();
+
if (prev instanceof EmptyBlock
- && prev.outer instanceof JsrBlock) {
- if (prev.outer.getNextFlowBlock() != null) {
- /* The jsr is directly before a jump, okay. */
- prev.outer.removeBlock();
- continue;
- }
- if (prev.outer.outer instanceof SequentialBlock
- && prev.outer.outer.getSubBlocks()[0] == prev.outer) {
- SequentialBlock seq = (SequentialBlock) prev.outer.outer;
- if (seq.subBlocks[1] instanceof JsrBlock
- || (seq.subBlocks[1] instanceof SequentialBlock
- && seq.subBlocks[1].getSubBlocks()[0]
- instanceof JsrBlock)) {
- /* The jsr is followed by a jsr, okay. */
- prev.outer.removeBlock();
- continue;
- }
- if (seq.subBlocks[1] instanceof ReturnBlock
- && !(seq.subBlocks[1] instanceof ThrowBlock)) {
-
- /* The jsr is followed by a return, okay. */
- ReturnBlock ret = (ReturnBlock) seq.subBlocks[1];
- prev.outer.removeBlock();
-
- if (ret.outer != null
- && ret.outer instanceof SequentialBlock) {
- /* Try to eliminate the local that javac uses
- * in this case.
- */
- try {
- StoreInstruction store = (StoreInstruction)
- ((InstructionBlock)
- ret.outer.getSubBlocks()[0]).instr;
- LocalInfo local =
- ((LocalStoreOperator) store.getLValue())
- .getLocalInfo();
- if (store.lvalueMatches
- ((LocalLoadOperator)
- ret.getInstruction())) {
- Expression expr =
- store.getSubExpressions()[1];
- ret.setInstruction(expr);
- ret.replace(ret.outer);
- }
- } catch(ClassCastException ex) {
- /* didn't succeed */
- }
- }
- continue;
- }
- }
+ && prev.outer instanceof JsrBlock
+ && ((JsrBlock) prev.outer).isGood()) {
+ StructuredBlock next = prev.outer.getNextBlock();
+ prev.outer.removeBlock();
+ if (next instanceof ReturnBlock)
+ removeReturnLocal((ReturnBlock) next);
+ } else {
+ /* We have a jump to the subroutine, that is badly placed.
+ * We complain here.
+ */
+ DescriptionBlock msg = new DescriptionBlock
+ ("ERROR: invalid jump to finally block!");
+ prev.appendBlock(msg);
+ }
+ }
+ }
+
+ private static StructuredBlock getPredecessor(StructuredBlock stmt)
+ {
+ if (stmt.outer instanceof SequentialBlock) {
+ SequentialBlock seq = (SequentialBlock) stmt.outer;
+ if (seq.subBlocks[1] == stmt)
+ return seq.subBlocks[0];
+ else if (seq.outer instanceof SequentialBlock)
+ return seq.outer.getSubBlocks()[0];
+ }
+ return null;
+ }
+
+ /**
+ * Gets the slot of the monitorexit instruction instr in the
+ * stmt, or -1 if stmt isn't a InstructionBlock with a
+ * monitorexit instruction.
+ * @param stmt the stmt, may be null.
+ */
+ private static int getMonitorExitSlot(StructuredBlock stmt) {
+ if (stmt instanceof InstructionBlock) {
+ Expression instr = ((InstructionBlock) stmt).getInstruction();
+ if (instr instanceof MonitorExitOperator) {
+ MonitorExitOperator monExit = (MonitorExitOperator)instr;
+ if (monExit.getFreeOperandCount() == 0
+ && (monExit.getSubExpressions()[0]
+ instanceof LocalLoadOperator))
+ return ((LocalLoadOperator) monExit.getSubExpressions()[0])
+ .getLocalInfo().getSlot();
}
- /* Now we have a jump to the subroutine, that is wrong.
- * We complain here.
- */
- DescriptionBlock msg
- = new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!");
- prev.appendBlock(msg);
}
+ return -1;
}
- public void checkAndRemoveJSR(FlowBlock tryFlow, FlowBlock subRoutine,
- int startOutExit, int endOutExit) {
- boolean foundSub = false;
+ private boolean isMonitorExitSubRoutine(FlowBlock subRoutine,
+ LocalInfo local) {
+ if (transformSubRoutine(subRoutine.block)
+ && getMonitorExitSlot(subRoutine.block) == local.getSlot())
+ return true;
+ return false;
+ }
+
+ private static StructuredBlock skipFinExitChain(StructuredBlock block)
+ {
+ StructuredBlock pred, result;
+ if (block instanceof ReturnBlock)
+ pred = getPredecessor(block);
+ else
+ pred = block;
+ result = null;
+
+ while (pred instanceof JsrBlock
+ || getMonitorExitSlot(pred) >= 0) {
+ result = pred;
+ pred = getPredecessor(pred);
+ }
+ return result;
+ }
+
+
+ private void checkAndRemoveJSR(FlowBlock tryFlow,
+ FlowBlock subRoutine,
+ int startOutExit, int endOutExit) {
Iterator iter = tryFlow.getSuccessors().iterator();
dest_loop:
while (iter.hasNext()) {
FlowBlock dest = (FlowBlock) iter.next();
- if (dest == subRoutine) {
- foundSub = true;
+ if (dest == subRoutine)
continue dest_loop;
- }
boolean isFirstJump = true;
for (Jump jumps = tryFlow.getJumps(dest);
@@ -266,41 +339,31 @@ public class TransformExceptionHandlers {
*/
continue;
}
- if (prev instanceof JsrBlock) {
- /* The jump is directly preceeded by a jsr.
- * Everything okay.
- */
- continue;
- }
-
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
- /* If jump is a jsr check the outer
- * block instead.
- */
- prev = prev.outer;
- }
- if ((prev instanceof ReturnBlock
- || prev instanceof JsrBlock)
- && prev.outer instanceof SequentialBlock) {
- SequentialBlock seq = (SequentialBlock) prev.outer;
- if (seq.subBlocks[1] == prev
- && (seq.subBlocks[0] instanceof JsrBlock)) {
- /* The jump is preceeded by another jsr, okay.
- */
- continue;
- }
- if (seq.subBlocks[0] == prev
- && seq.outer instanceof SequentialBlock
- && (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) {
- /* Again the jump is preceeded by another jsr, okay.
- */
- continue;
- }
+ /* This jump is a jsr, since it doesn't leave the
+ * block forever, we can ignore it.
+ */
+ continue;
}
+ StructuredBlock pred = skipFinExitChain(prev);
+
+ if (pred instanceof JsrBlock) {
+ JsrBlock jsr = (JsrBlock) pred;
+ StructuredBlock jsrInner = jsr.innerBlock;
+ if (jsrInner instanceof EmptyBlock
+ && jsrInner.jump != null
+ && jsrInner.jump.destination == subRoutine) {
+ /* The jump is preceeded by the right jsr. Mark the
+ * jsr as good.
+ */
+ jsr.setGood(true);
+ continue;
+ }
+ }
- if (isFirstJump) {
- /* Now we have a jump that is not preceded by the
+ if (pred == null && isFirstJump) {
+ /* Now we have a jump that is not preceded by any
* jsr. There's a last chance: the jump jumps
* directly to a correct jsr instruction, which
* lies outside the try/catch block.
@@ -330,45 +393,22 @@ public class TransformExceptionHandlers {
* Complain!
*/
DescriptionBlock msg
- = new DescriptionBlock("ERROR: NO JSR TO FINALLY");
- prev.appendBlock(msg);
- msg.moveJump(jumps);
+ = new DescriptionBlock("ERROR: no jsr to finally");
+ if (pred != null)
+ pred.prependBlock(msg);
+ else {
+ prev.appendBlock(msg);
+ msg.moveJump(prev.jump);
+ }
}
}
- if (foundSub)
+ if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, subRoutine);
}
- static boolean isMonitorExit(Expression instr, LocalInfo local) {
- if (instr instanceof MonitorExitOperator) {
- MonitorExitOperator monExit = (MonitorExitOperator)instr;
- if (monExit.getFreeOperandCount() == 0
- && monExit.getSubExpressions()[0] instanceof LocalLoadOperator
- && (((LocalLoadOperator) monExit.getSubExpressions()[0])
- .getLocalInfo().getSlot() == local.getSlot())) {
- return true;
- }
- }
- return false;
- }
-
- boolean isMonitorExitSubRoutine(FlowBlock subRoutine, LocalInfo local) {
- if (transformSubRoutine(subRoutine.block)) {
- if (subRoutine.block instanceof InstructionBlock) {
- Expression instr =
- ((InstructionBlock)subRoutine.block)
- .getInstruction();
- if (isMonitorExit(instr, local)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public void checkAndRemoveMonitorExit(FlowBlock tryFlow, LocalInfo local,
- int startOutExit, int endOutExit,
- int startMonExit, int endMonExit) {
+ private void checkAndRemoveMonitorExit(FlowBlock tryFlow,
+ LocalInfo local,
+ int start, int end) {
FlowBlock subRoutine = null;
Iterator succs = tryFlow.getSuccessors().iterator();
dest_loop:
@@ -379,87 +419,54 @@ public class TransformExceptionHandlers {
jumps != null; jumps = jumps.next, isFirstJump = false) {
StructuredBlock prev = jumps.prev;
-
if (prev instanceof ThrowBlock) {
- /* The jump is a throw. We have a catch all block
- * that will do the monitorexit.
- */
- continue;
- }
- if (prev instanceof JsrBlock) {
- /* The jump is directly preceeded by a jsr.
+ /* The jump is a throw. We have a catch-all block
+ * that will do the finally.
*/
continue;
}
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
- /* If jump is a jsr check the outer
- * block instead.
- */
- prev = prev.outer;
- }
-
- /* If the block is a jsr or a return block, check if
- * it is preceeded by another jsr.
- */
- if ((prev instanceof JsrBlock
- || prev instanceof ReturnBlock)
- && prev.outer instanceof SequentialBlock) {
- SequentialBlock seq = (SequentialBlock) prev.outer;
- StructuredBlock pred = null;
- if (seq.subBlocks[1] == prev)
- pred = seq.subBlocks[0];
- else if (seq.outer instanceof SequentialBlock)
- pred = seq.outer.getSubBlocks()[0];
-
- if (pred != null) {
- if (pred instanceof JsrBlock)
- /* The jump is preceeded by another jsr, okay.
- */
- continue;
-
- if (pred instanceof InstructionBlock) {
- Expression instr =
- ((InstructionBlock)pred).getInstruction();
- if (isMonitorExit(instr, local))
- continue;
- }
- }
- }
-
- if (prev instanceof InstructionBlock
- && isMonitorExit(((InstructionBlock)prev).instr, local)) {
- /* This is probably the last expression in the
- * synchronized block, and has the right monitor exit
- * attached. Remove this block.
- */
- prev.removeBlock();
- continue;
- }
-
- if (isFirstJump) {
- /* This is the first jump to that destination.
- * Check if the destination does the monitorExit
+ /* This jump is really a jsr, since it doesn't
+ * leave the block forever, we can ignore it.
*/
+ continue;
+ }
+ StructuredBlock pred = skipFinExitChain(prev);
+ if (pred instanceof JsrBlock) {
+ JsrBlock jsr = (JsrBlock) pred;
+ StructuredBlock jsrInner = jsr.innerBlock;
+ if (jsrInner instanceof EmptyBlock
+ && jsrInner.jump != null) {
+ FlowBlock dest = jsrInner.jump.destination;
- /* The block is a jsr that is not preceeded by
- * another jsr. This must be the monitorexit
- * subroutine.
- */
- if (prev instanceof JsrBlock) {
if (subRoutine == null
- && successor.getAddr() >= startMonExit
- && successor.getNextAddr() <= endMonExit) {
- successor.analyze(startMonExit, endMonExit);
-
- if (isMonitorExitSubRoutine(successor, local))
- subRoutine = successor;
+ && dest.getAddr() >= start
+ && dest.getNextAddr() <= end) {
+ dest.analyze(start, end);
+ if (isMonitorExitSubRoutine(dest, local))
+ subRoutine = dest;
}
- if (subRoutine == successor)
- continue dest_loop;
+ if (dest == subRoutine) {
+ /* The jump is preceeded by the right jsr.
+ * Mark it as good.
+ */
+ jsr.setGood(true);
+ continue;
+ }
}
+ } else if (getMonitorExitSlot(pred) == local.getSlot()) {
+ /* The jump is preceeded by the right monitor
+ * exit instruction.
+ */
+ pred.removeBlock();
+ if (prev instanceof ReturnBlock)
+ removeReturnLocal((ReturnBlock) prev);
+ continue;
+ }
+ if (pred == null && isFirstJump) {
/* Now we have a jump that is not preceded by a
* monitorexit. There's a last chance: the jump
* jumps directly to the correct monitorexit
@@ -467,9 +474,9 @@ public class TransformExceptionHandlers {
* block.
*/
if (successor.predecessors.size() == 1
- && successor.getAddr() >= startOutExit
- && successor.getNextAddr() <= endOutExit) {
- successor.analyze(startOutExit, endOutExit);
+ && successor.getAddr() >= start
+ && successor.getNextAddr() <= end) {
+ successor.analyze(start, end);
StructuredBlock sb = successor.block;
if (sb instanceof SequentialBlock)
@@ -479,25 +486,23 @@ public class TransformExceptionHandlers {
StructuredBlock jsrInner = sb.getSubBlocks()[0];
FlowBlock dest = jsrInner.jump.destination;
if (subRoutine == null
- && dest.getAddr() >= startMonExit
- && dest.getNextAddr() <= endMonExit) {
- dest.analyze(startMonExit, endMonExit);
+ && dest.getAddr() >= start
+ && dest.getNextAddr() <= end) {
+ dest.analyze(start, end);
if (isMonitorExitSubRoutine(dest, local))
subRoutine = dest;
}
if (subRoutine == dest) {
+ successor.removeSuccessor(jsrInner.jump);
+ jsrInner.removeJump();
sb.removeBlock();
continue dest_loop;
}
}
- if (sb instanceof InstructionBlock) {
- Expression instr = ((InstructionBlock)sb)
- .getInstruction();
- if (isMonitorExit(instr, local)) {
- sb.removeBlock();
- continue dest_loop;
- }
+ if (getMonitorExitSlot(sb) == local.getSlot()) {
+ sb.removeBlock();
+ continue dest_loop;
}
}
}
@@ -505,156 +510,230 @@ public class TransformExceptionHandlers {
/* Complain!
*/
DescriptionBlock msg
- = new DescriptionBlock("ERROR: NO MONITOREXIT");
+ = new DescriptionBlock("ERROR: no monitorexit");
prev.appendBlock(msg);
msg.moveJump(jumps);
}
}
if (subRoutine != null) {
- removeJSR(tryFlow, subRoutine);
- tryFlow.mergeAddr(subRoutine);
+ if (tryFlow.getSuccessors().contains(subRoutine))
+ removeJSR(tryFlow, subRoutine);
+ if (subRoutine.predecessors.size() == 0)
+ tryFlow.mergeAddr(subRoutine);
}
}
+ private StoreInstruction getExceptionStore(StructuredBlock catchBlock) {
+ if (!(catchBlock instanceof SequentialBlock)
+ || !(catchBlock.getSubBlocks()[0] instanceof InstructionBlock))
+ return null;
+
+ Expression instr =
+ ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
+ if (!(instr instanceof StoreInstruction))
+ return null;
+
+ StoreInstruction store = (StoreInstruction) instr;
+ if (!(store.getLValue() instanceof LocalStoreOperator
+ && store.getSubExpressions()[1] instanceof NopOperator))
+ return null;
+
+ return store;
+ }
+
private boolean analyzeSynchronized(FlowBlock tryFlow,
FlowBlock catchFlow,
int endHandler) {
- if (!(catchFlow.block instanceof SequentialBlock
- && catchFlow.block.getSubBlocks()[0]
+ /* Check if this is a synchronized block. We mustn't change
+ * anything until we are sure.
+ */
+ StructuredBlock catchBlock = catchFlow.block;
+
+ /* Check for a optional exception store and skip it */
+ StoreInstruction excStore = getExceptionStore(catchBlock);
+ if (excStore != null)
+ catchBlock = catchBlock.getSubBlocks()[1];
+
+ /* Check for the monitorexit instruction */
+ if (!(catchBlock instanceof SequentialBlock
+ && catchBlock.getSubBlocks()[0]
instanceof InstructionBlock))
return false;
-
- SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
Expression instr =
- ((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
-
- if (instr instanceof MonitorExitOperator
- && instr.getFreeOperandCount() == 0
- && (((MonitorExitOperator)instr).getSubExpressions()[0]
- instanceof LocalLoadOperator)
- && catchBlock.subBlocks[1] instanceof ThrowBlock
- && (((ThrowBlock)catchBlock.subBlocks[1]).instr
- instanceof NopOperator)) {
-
- /* This is a synchronized block:
- *
- * local_x = monitor object; // later
- * monitorenter local_x // later
- * tryFlow:
- * |- synchronized block
- * | ...
- * | every jump to outside is preceded by jsr subroutine-,
- * | ... |
- * |- monitorexit local_x |
- * ` jump after this block (without jsr monexit) |
- * |
- * catchFlow: |
- * local_n = stack |
- * monitorexit local_x |
- * throw local_n |
- * optional subroutine: <-----------------------------------'
- * astore_n
- * monitorexit local_x
- * return_n
- */
+ ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
+ if (!(instr instanceof MonitorExitOperator
+ && instr.getFreeOperandCount() == 0
+ && (((MonitorExitOperator)instr).getSubExpressions()[0]
+ instanceof LocalLoadOperator)
+ && catchBlock.getSubBlocks()[1] instanceof ThrowBlock))
+ return false;
- /* Now remove the jump (after the throw) from the
- * catch block so that we can forget about it.
- */
-
- catchFlow.removeSuccessor(catchBlock.subBlocks[1].jump);
- MonitorExitOperator monexit = (MonitorExitOperator)
- ((InstructionBlock) catchBlock.subBlocks[0]).instr;
- LocalInfo local =
- ((LocalLoadOperator)monexit.getSubExpressions()[0])
- .getLocalInfo();
+ Expression throwInstr =
+ ((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction();
+
+ if (excStore != null) {
+ if (!(throwInstr instanceof Operator
+ && excStore.lvalueMatches((Operator)throwInstr)))
+ return false;
+ } else {
+ if (!(throwInstr instanceof NopOperator))
+ return false;
+ }
- if ((GlobalOptions.debuggingFlags
- & GlobalOptions.DEBUG_ANALYZE) != 0)
- GlobalOptions.err.println
- ("analyzeSynchronized(" + tryFlow.getAddr()
- + "," + tryFlow.getNextAddr() + "," + catchFlow.getAddr()
- + "," + catchFlow.getNextAddr() + "," + endHandler + ")");
-
- checkAndRemoveMonitorExit
- (tryFlow, local,
- tryFlow.getNextAddr(), catchFlow.getAddr(),
- catchFlow.getNextAddr(), endHandler);
-
- tryFlow.mergeAddr(catchFlow);
- SynchronizedBlock syncBlock = new SynchronizedBlock(local);
- TryBlock tryBlock = (TryBlock) tryFlow.block;
- syncBlock.replace(tryBlock);
- syncBlock.moveJump(tryBlock.jump);
- syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1
- ? tryBlock.subBlocks[0] : tryBlock);
- tryFlow.lastModified = syncBlock;
- return true;
- }
- return false;
+ /* This is a synchronized block:
+ *
+ * local_x = monitor object; // later
+ * monitorenter local_x // later
+ * tryFlow:
+ * |- synchronized block
+ * | ...
+ * | every jump to outside is preceded by jsr subroutine-,
+ * | ... |
+ * |- monitorexit local_x |
+ * ` jump after this block (without jsr monexit) |
+ * |
+ * catchBlock: |
+ * local_n = stack |
+ * monitorexit local_x |
+ * throw local_n |
+ * [OR ALTERNATIVELY:] |
+ * monitorexit local_x |
+ * throw stack |
+ * optional subroutine: <-----------------------------------'
+ * astore_n
+ * monitorexit local_x
+ * return_n
+ */
+
+ /* Merge try and catch flow blocks. No need to insert the
+ * catchFlow.block into the try flow though, since all its
+ * instruction are synthetic.
+ *
+ * Though we need to remove the jump of the throw
+ * instruction.
+ */
+ catchFlow.removeSuccessor(catchBlock.getSubBlocks()[1].jump);
+ mergeTryCatch(tryFlow, catchFlow);
+
+ MonitorExitOperator monexit = (MonitorExitOperator)
+ ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr;
+ LocalInfo local =
+ ((LocalLoadOperator)monexit.getSubExpressions()[0])
+ .getLocalInfo();
+
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_ANALYZE) != 0)
+ GlobalOptions.err.println
+ ("analyzeSynchronized(" + tryFlow.getAddr()
+ + "," + tryFlow.getNextAddr() + "," + endHandler + ")");
+
+ checkAndRemoveMonitorExit
+ (tryFlow, local, tryFlow.getNextAddr(), endHandler);
+
+ SynchronizedBlock syncBlock = new SynchronizedBlock(local);
+ TryBlock tryBlock = (TryBlock) tryFlow.block;
+ syncBlock.replace(tryBlock);
+ syncBlock.moveJump(tryBlock.jump);
+ syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1
+ ? tryBlock.subBlocks[0] : tryBlock);
+ tryFlow.lastModified = syncBlock;
+ return true;
}
- private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow,
- int end) {
-
- /* Layout of a try-finally block:
- *
- * tryFlow:
- * |- first instruction
- * | ...
- * | every jump to outside is preceded by jsr finally
- * | ...
- * | jsr finally -----------------,
- * `- jump after finally |
- * |
- * catchFlow: (already checked) |
- * local_n = stack v
- * jsr finally ---------------->|
- * throw local_n; |
- * finally: <-----------------------'
- * astore_n
- * ...
- * return_n
- */
-
- if (!(catchFlow.block instanceof SequentialBlock)
- || !(catchFlow.block.getSubBlocks()[0]
- instanceof InstructionBlock)
- || !(catchFlow.block.getSubBlocks()[1]
- instanceof SequentialBlock))
+ /**
+ * Merge try and finally flow blocks.
+ * @param tryFlow The try flow block. Its contained block must be
+ * a try block.
+ * @param catchFlow The catch flow block that contains the finally
+ * block.
+ * @param finallyBlock block that either contains the finally block.
+ * It is part of the catchFlow. The other parts of catchFlow are
+ * synthetic and can be removed.
+ */
+ private void mergeFinallyBlock(FlowBlock tryFlow, FlowBlock catchFlow,
+ StructuredBlock finallyBlock) {
+ TryBlock tryBlock = (TryBlock) tryFlow.block;
+ if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
+ /* A try { try { } catch {} } finally{} is equivalent
+ * to a try {} catch {} finally {}
+ * so remove the surrounding tryBlock.
+ */
+ TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
+ innerTry.gen = tryBlock.gen;
+ innerTry.replace(tryBlock);
+ tryBlock = innerTry;
+ tryFlow.lastModified = tryBlock;
+ tryFlow.block = tryBlock;
+ }
+
+ /* Now merge try and catch flow blocks */
+ mergeTryCatch(tryFlow, catchFlow);
+ FinallyBlock newBlock = new FinallyBlock();
+ newBlock.setCatchBlock(finallyBlock);
+ tryBlock.addCatchBlock(newBlock);
+ }
+
+ private boolean analyzeFinally(FlowBlock tryFlow,
+ FlowBlock catchFlow, int end) {
+
+ /* Layout of a try-finally block:
+ *
+ * tryFlow:
+ * |- first instruction
+ * | ...
+ * | every jump to outside is preceded by jsr finally
+ * | ...
+ * | jsr finally -----------------,
+ * `- jump after finally |
+ * |
+ * catchBlock: |
+ * local_n = stack v
+ * jsr finally ---------------->|
+ * throw local_n; |
+ * finally: <-----------------------'
+ * astore_n
+ * ...
+ * return_n
+ */
+
+ StructuredBlock catchBlock = catchFlow.block;
+ StoreInstruction excStore = getExceptionStore(catchBlock);
+ if (excStore == null)
+ return false;
+
+ catchBlock = catchBlock.getSubBlocks()[1];
+ if (!(catchBlock instanceof SequentialBlock))
return false;
StructuredBlock finallyBlock = null;
- SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
- Expression instr =
- ((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
- catchBlock = (SequentialBlock)catchBlock.subBlocks[1];
- if (catchBlock.subBlocks[0] instanceof LoopBlock) {
- /* In case the try block has no exit (that means, it throws
- * an exception), the finallyBlock was already merged with
- * the catchBlock. We have to check for this case separately:
+ if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) {
+ /* In case the try block has no exit (that means, it
+ * throws an exception or loops forever), the finallyBlock
+ * was already merged with the catchBlock. We have to
+ * check for this case separately:
*
* do {
* JSR
* break;
* throw local_x
* } while(false);
- * finallyBlock;
+ * finallyBlock; (starts with POP or local_y = POP)
*/
- LoopBlock doWhileFalse = (LoopBlock)catchBlock.subBlocks[0];
+ LoopBlock doWhileFalse = (LoopBlock)catchBlock.getSubBlocks()[0];
if (doWhileFalse.type == LoopBlock.DOWHILE
&& doWhileFalse.cond == LoopBlock.FALSE
&& doWhileFalse.bodyBlock instanceof SequentialBlock) {
- finallyBlock = catchBlock.subBlocks[1];
- catchBlock = (SequentialBlock) doWhileFalse.bodyBlock;
+ if (transformSubRoutine(catchBlock.getSubBlocks()[1])) {
+ finallyBlock = catchBlock.getSubBlocks()[1];
+ catchBlock = (SequentialBlock) doWhileFalse.bodyBlock;
+ }
}
}
if (!(catchBlock instanceof SequentialBlock
&& catchBlock.getSubBlocks()[0] instanceof JsrBlock
- && instr instanceof StoreInstruction
&& catchBlock.getSubBlocks()[1] instanceof ThrowBlock))
return false;
@@ -663,320 +742,277 @@ public class TransformExceptionHandlers {
ThrowBlock throwBlock = (ThrowBlock) catchBlock.getSubBlocks()[1];
- if (!(throwBlock.instr instanceof LocalLoadOperator)
- || !(((StoreInstruction) instr).lvalueMatches
- ((LocalLoadOperator) throwBlock.instr)))
+ if (!(throwBlock.getInstruction() instanceof Operator
+ && excStore.lvalueMatches((Operator)
+ throwBlock.getInstruction())))
return false;
- /* Wow that was complicated :-)
- * But now we know that the catch block looks
- * exactly like it should:
- *
- * catchBlock:
- * JSR
- * finally
- * throw local_n <- matches the local in instr.
- */
-
+ FlowBlock subRoutine;
if (finallyBlock != null) {
- /* Check if the jsr breaks (see two comments above). We don't
+ /* Check if the jsr breaks (see comment above). We don't
* need to check if it breaks to the right block, because
* we know that there is only one Block around the jsr.
*/
if (!(jsrBlock.innerBlock instanceof BreakBlock))
return false;
- /* Check if the try block has no exit (except throws)
- */
- Set succs = tryFlow.getSuccessors();
- if (succs.size() > 0) {
- if (!succs.contains(FlowBlock.END_OF_METHOD))
- return false;
- Jump throwJumps
- = tryFlow.getJumps(FlowBlock.END_OF_METHOD);
- for (/**/; throwJumps != null;
- throwJumps = throwJumps.next) {
- if (!(throwJumps.prev instanceof ThrowBlock))
- /* There is a return exit in the try block */
- return false;
- }
- }
- /* Remove the jump of the throw instruction.
+ /* TODO - Check if the try block has no exit (except throws)
*/
+// if (tryFlow.getSuccessors().size() > 0)
+// return false;
+
+ catchBlock = finallyBlock;
+ subRoutine = null;
catchFlow.removeSuccessor(throwBlock.jump);
- throwBlock.removeJump();
-
- /* Replace the catchBlock with the finallyBlock.
- */
- finallyBlock.replace(catchFlow.block);
- transformSubRoutine(finallyBlock);
-
- tryFlow.updateInOutCatch(catchFlow);
- tryFlow.mergeAddr(catchFlow);
- finallyBlock = catchFlow.block;
- tryFlow.mergeSuccessors(catchFlow);
-
} else {
- FlowBlock subRoutine = jsrBlock.innerBlock.jump.destination;
- checkAndRemoveJSR(tryFlow, subRoutine,
- tryFlow.getNextAddr(), catchFlow.getAddr());
-
- while (subRoutine.analyze(catchFlow.getNextAddr(), end));
+ if (!(jsrBlock.innerBlock instanceof EmptyBlock))
+ return false;
+ finallyBlock = jsrBlock.innerBlock;
+ subRoutine = finallyBlock.jump.destination;
- /* Now check if the subroutine is correct and has only the
- * catchFlow as predecessor.
+ /* We are committed now and can start changing the blocks.
*/
- if (subRoutine.predecessors.size() == 1
- && transformSubRoutine(subRoutine.block)) {
- subRoutine.mergeAddr(catchFlow);
+ catchFlow.removeSuccessor(throwBlock.jump);
+ checkAndRemoveJSR(tryFlow, subRoutine,
+ tryFlow.getNextAddr(), end);
- /* Now remove the jump to the JSR from the catch block
- * and the jump of the throw instruction.
+ /* Now analyze and transform the subroutine.
+ */
+ while (subRoutine.analyze(tryFlow.getNextAddr(), end));
+ if (subRoutine.predecessors.size() == 1) {
+ /* catchFlow is synthetic, so we can safely remove it
+ * here.
*/
- catchFlow.removeSuccessor(jsrBlock.innerBlock.jump);
- jsrBlock.innerBlock.removeJump();
- catchFlow.removeSuccessor(throwBlock.jump);
- throwBlock.removeJump();
-
- tryFlow.updateInOutCatch(subRoutine);
- tryFlow.mergeAddr(subRoutine);
- tryFlow.mergeSuccessors(subRoutine);
+ catchFlow.removeSuccessor(finallyBlock.jump);
+ subRoutine.mergeAddr(catchFlow);
+ catchFlow = subRoutine;
+
+ if (!transformSubRoutine(subRoutine.block)) {
+ finallyBlock = subRoutine.block;
+ DescriptionBlock msg = new DescriptionBlock
+ ("ERROR: Missing return address handling");
+ StructuredBlock subblock = subRoutine.block;
+ msg.replace(finallyBlock);
+ msg.appendBlock(finallyBlock);
+ }
finallyBlock = subRoutine.block;
- } else {
- catchFlow.removeSuccessor(throwBlock.jump);
- throwBlock.removeJump();
- finallyBlock = jsrBlock;
-
- finallyBlock.replace(catchFlow.block);
- transformSubRoutine(finallyBlock);
-
- tryFlow.updateInOutCatch(catchFlow);
- tryFlow.mergeAddr(catchFlow);
- finallyBlock = catchFlow.block;
- tryFlow.mergeSuccessors(catchFlow);
- }
- }
-
- TryBlock tryBlock = (TryBlock)tryFlow.block;
- if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
- /* remove the surrounding tryBlock */
- TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
- innerTry.gen = tryBlock.gen;
- innerTry.replace(tryBlock);
- tryBlock = innerTry;
- tryFlow.lastModified = tryBlock;
- tryFlow.block = tryBlock;
+ }
}
- FinallyBlock newBlock = new FinallyBlock();
- newBlock.setCatchBlock(finallyBlock);
- tryBlock.addCatchBlock(newBlock);
+
+ /* Now finish it.
+ */
+ mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
return true;
}
private boolean analyzeSpecialFinally(FlowBlock tryFlow,
- FlowBlock catchFlow, int end) {
-
+ FlowBlock catchFlow, int end) {
+ StructuredBlock finallyBlock = catchFlow.block;
StructuredBlock firstInstr =
- catchFlow.block instanceof SequentialBlock
- ? catchFlow.block.getSubBlocks()[0]: catchFlow.block;
+ finallyBlock instanceof SequentialBlock
+ ? finallyBlock.getSubBlocks()[0]: finallyBlock;
- if (firstInstr instanceof SpecialBlock
- && ((SpecialBlock)firstInstr).type == SpecialBlock.POP
- && ((SpecialBlock)firstInstr).count == 1) {
-
- /* This is a special try/finally-block, where
- * the finally block ends with a break, return or
- * similar.
- */
+ if (!(firstInstr instanceof SpecialBlock
+ && ((SpecialBlock)firstInstr).type == SpecialBlock.POP
+ && ((SpecialBlock)firstInstr).count == 1))
+ return false;
- FlowBlock succ = (firstInstr.jump != null) ?
- firstInstr.jump.destination : null;
- boolean hasExit = false;
- Iterator iter = tryFlow.getSuccessors().iterator();
- while (iter.hasNext()) {
- FlowBlock dest = (FlowBlock) iter.next();
- if (dest == succ)
- continue;
- if (dest != FlowBlock.END_OF_METHOD) {
- /* There is another exit in the try block, bad */
- return false;
- }
- for (Jump throwJumps = (Jump) tryFlow.getJumps(dest);
- throwJumps != null; throwJumps = throwJumps.next) {
- if (!(throwJumps.prev instanceof ThrowBlock)) {
- /* There is a return exit in the try block */
- return false;
- }
- }
- }
+ /* This is a special try/finally-block, where
+ * the finally block ends with a break, return or
+ * similar.
+ */
- /* remove the pop now */
- firstInstr.removeBlock();
- tryFlow.mergeAddr(catchFlow);
+ /* Make sure that resolveJump only works on the inside of the try
+ */
+ tryFlow.lastModified = tryFlow.block.getSubBlocks()[0];
+ FlowBlock finallyFlow;
+ if (finallyBlock instanceof SequentialBlock) {
+ finallyBlock = finallyBlock.getSubBlocks()[1];
+ finallyFlow = null;
+ } else {
+ finallyBlock = new EmptyBlock();
+ finallyBlock.moveJump(firstInstr.jump);
- if (succ != null) {
- Jump jumps = tryFlow.removeJumps(succ);
- /* Handle the jumps in the tryFlow.
- */
- jumps = tryFlow.resolveSomeJumps(jumps, succ);
- tryFlow.resolveRemaining(jumps);
- }
+ /* Handle the jumps in the tryFlow to finallyFlow.
+ */
+ finallyFlow = finallyBlock.jump.destination;
+ if (tryFlow.getSuccessors().contains(finallyFlow)) {
+ Jump jumps = tryFlow.removeJumps(finallyFlow);
+ jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow);
+ tryFlow.resolveRemaining(jumps);
+ }
+ }
- TryBlock tryBlock = (TryBlock)tryFlow.block;
- if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
- /* remove the unnecessary tryBlock */
- TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
- innerTry.gen = tryBlock.gen;
- innerTry.replace(tryBlock);
- tryBlock = innerTry;
- tryFlow.lastModified = innerTry;
- }
- FinallyBlock newBlock = new FinallyBlock();
- tryBlock.addCatchBlock(newBlock);
- /* try block has no successors */
-
- if (succ != null && succ.predecessors.size() == 1) {
- while (succ.analyze(catchFlow.getNextAddr(), end));
- tryFlow.mergeAddr(succ);
- tryFlow.removeJumps(succ);
- newBlock.setCatchBlock(succ.block);
- tryFlow.mergeSuccessors(succ);
- } else {
- /* Put the catchBlock in instead.
- */
- newBlock.setCatchBlock(catchFlow.block);
- tryFlow.mergeSuccessors(catchFlow);
- }
- return true;
+ /* Complain about all other jumps in try block */
+ Set trySuccs = tryFlow.getSuccessors();
+ for (Iterator i = trySuccs.iterator(); i.hasNext(); ) {
+ FlowBlock succ = (FlowBlock) i.next();
+ if (succ != FlowBlock.END_OF_METHOD) {
+ /* This seems to be a illegal succ in try block that
+ * doesn't go to the finally block. There is a last
+ * chance though. If the destination directly jumps
+ * to the try block, its okay.
+ */
+ if (succ.block instanceof EmptyBlock
+ && succ.block.jump.destination == finallyFlow) {
+ Jump jumps = tryFlow.removeJumps(succ);
+ jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow);
+ tryFlow.resolveRemaining(jumps);
+ if (succ.predecessors.size() == 0) {
+ succ.removeJumps(finallyFlow);
+ tryFlow.mergeAddr(succ);
+ }
+ continue;
+ }
+ }
+ for (Jump jumps = tryFlow.getJumps(succ);
+ jumps != null; jumps = jumps.next) {
+ if (jumps.prev instanceof ThrowBlock)
+ continue;
+
+ DescriptionBlock msg =
+ new DescriptionBlock
+ ("ERROR: doesn't go through finally block!");
+ if (jumps.prev instanceof ReturnBlock) {
+ msg.replace(jumps.prev);
+ msg.appendBlock(jumps.prev);
+ } else {
+ jumps.prev.appendBlock(msg);
+ msg.moveJump(jumps);
+ }
+ }
}
- return false;
+
+ mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
+ /* Following code will work be put inside the finallyBlock */
+ tryFlow.lastModified = finallyBlock;
+ return true;
}
- /**
- * Analyzes all exception handlers to try/catch/finally or
- * synchronized blocks.
- */
- public void analyze() {
+ void checkTryCatchOrder() {
/* Check if try/catch ranges are okay. The following succeeds
* for all classes generated by the sun java compiler, but hand
* optimized classes (or generated by other compilers) will fail.
*/
- {
- Handler last = null;
- for (Iterator i = handlers.iterator(); i.hasNext(); ) {
- Handler exc = (Handler) i.next();
- int start = exc.start.getAddr();
- int end = exc.endAddr;
- int handler = exc.handler.getAddr();
- if (start >= end || handler < end)
+ Handler last = null;
+ for (Iterator i = handlers.iterator(); i.hasNext(); ) {
+ Handler exc = (Handler) i.next();
+ int start = exc.start.getAddr();
+ int end = exc.endAddr;
+ int handler = exc.handler.getAddr();
+ if (start >= end || handler < end)
+ throw new AssertError
+ ("ExceptionHandler order failed: not "
+ + start + " < " + end + " <= " + handler);
+ if (last != null
+ && (last.start.getAddr() != start || last.endAddr != end)) {
+ /* The last handler does catch another range.
+ * Due to the order:
+ * start < last.start.getAddr() || end > last.end.getAddr()
+ */
+ if (end > last.start.getAddr() && end < last.endAddr)
throw new AssertError
- ("ExceptionHandler order failed: not "
- + start + " < " + end + " <= " + handler);
- if (last != null
- && (last.start.getAddr() != start || last.endAddr != end)) {
- /* The last handler does catch another range.
- * Due to the order:
- * start < last.start.getAddr() || end > last.end.getAddr()
- */
- if (end > last.start.getAddr() && end < last.endAddr)
- throw new AssertError
- ("Exception handlers ranges are intersecting: ["
+ ("Exception handlers ranges are intersecting: ["
+ last.start.getAddr()+", "+last.endAddr+"] and ["
- + start+", "+end+"].");
- }
- last = exc;
+ + start+", "+end+"].");
}
+ last = exc;
}
-
- {
- Iterator i = handlers.iterator();
- Handler exc = null;
- Handler next = i.hasNext() ? (Handler) i.next() : null;
- while(next != null) {
- Handler last = exc;
- exc = next;
- next = i.hasNext() ? (Handler) i.next() : null;
- int endHandler = Integer.MAX_VALUE;
- /* If the next exception handler catches a bigger range
- * it must surround the handler completely.
+ }
+
+ /**
+ * Analyzes all exception handlers to try/catch/finally or
+ * synchronized blocks.
+ */
+ public void analyze() {
+ checkTryCatchOrder();
+
+ Iterator i = handlers.iterator();
+ Handler exc = null;
+ Handler next = i.hasNext() ? (Handler) i.next() : null;
+ while(next != null) {
+ Handler last = exc;
+ exc = next;
+ next = i.hasNext() ? (Handler) i.next() : null;
+ int endHandler = Integer.MAX_VALUE;
+ /* If the next exception handler catches a bigger range
+ * it must surround the handler completely.
+ */
+ if (next != null && next.endAddr > exc.endAddr)
+ endHandler = next.endAddr;
+
+ FlowBlock tryFlow = exc.start;
+ tryFlow.checkConsistent();
+
+ if (last == null || exc.type == null
+ || last.start.getAddr() != exc.start.getAddr()
+ || last.endAddr != exc.endAddr) {
+ /* The last handler does catch another range.
+ * Create a new try block.
*/
- if (next != null && next.endAddr > exc.endAddr)
- endHandler = next.endAddr;
-
- FlowBlock tryFlow = exc.start;
- tryFlow.checkConsistent();
-
- if (last == null || exc.type == null
- || last.start.getAddr() != exc.start.getAddr()
- || last.endAddr != exc.endAddr) {
- /* The last handler does catch another range.
- * Create a new try block.
- */
- if ((GlobalOptions.debuggingFlags
- & GlobalOptions.DEBUG_ANALYZE) != 0)
- GlobalOptions.err.println
- ("analyzeTry("
- + exc.start.getAddr() + ", " + exc.endAddr+")");
- while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr));
-
- TryBlock tryBlock = new TryBlock(tryFlow);
- } else if (!(tryFlow.block instanceof TryBlock))
- throw new AssertError("no TryBlock");
-
- FlowBlock catchFlow = exc.handler;
- boolean isMultiUsed = catchFlow.predecessors.size() != 0;
- if (!isMultiUsed && next != null) {
- for (Iterator j = handlers.tailSet(next).iterator();
- j.hasNext();) {
- Handler h = (Handler) j.next();
- if (h.handler == catchFlow) {
- isMultiUsed = true;
- break;
- }
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_ANALYZE) != 0)
+ GlobalOptions.err.println
+ ("analyzeTry("
+ + exc.start.getAddr() + ", " + exc.endAddr+")");
+ while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr));
+
+ TryBlock tryBlock = new TryBlock(tryFlow);
+ } else if (!(tryFlow.block instanceof TryBlock))
+ throw new AssertError("no TryBlock");
+
+ FlowBlock catchFlow = exc.handler;
+ boolean isMultiUsed = catchFlow.predecessors.size() != 0;
+ if (!isMultiUsed && next != null) {
+ for (Iterator j = handlers.tailSet(next).iterator();
+ j.hasNext();) {
+ Handler h = (Handler) j.next();
+ if (h.handler == catchFlow) {
+ isMultiUsed = true;
+ break;
}
}
-
- if (isMultiUsed) {
- /* If this exception is used in other exception handlers,
- * create a new flow block, that jumps to the handler.
- * This will be our new exception handler.
- */
- EmptyBlock jump = new EmptyBlock(new Jump(catchFlow));
- FlowBlock newFlow = new FlowBlock(catchFlow.method,
- catchFlow.getAddr());
- newFlow.appendBlock(jump, 0);
- catchFlow.prevByAddr.nextByAddr = newFlow;
- newFlow.prevByAddr = catchFlow.prevByAddr;
- newFlow.nextByAddr = catchFlow;
- catchFlow.prevByAddr = newFlow;
- catchFlow = newFlow;
- } else {
- if ((GlobalOptions.debuggingFlags
- & GlobalOptions.DEBUG_ANALYZE) != 0)
- GlobalOptions.err.println
- ("analyzeCatch("
- + catchFlow.getAddr() + ", " + endHandler + ")");
- while (catchFlow.analyze(catchFlow.getAddr(), endHandler));
- }
-
- tryFlow.updateInOutCatch(catchFlow);
- if (exc.type != null)
- analyzeCatchBlock(exc.type, tryFlow, catchFlow);
-
- else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler)
- && ! analyzeFinally(tryFlow, catchFlow, endHandler)
- && ! analyzeSpecialFinally(tryFlow, catchFlow,
- endHandler))
-
- analyzeCatchBlock(Type.tObject, tryFlow, catchFlow);
-
- tryFlow.checkConsistent();
+ }
+
+ if (isMultiUsed) {
+ /* If this exception is used in other exception handlers,
+ * create a new flow block, that jumps to the handler.
+ * This will be our new exception handler.
+ */
+ EmptyBlock jump = new EmptyBlock(new Jump(catchFlow));
+ FlowBlock newFlow = new FlowBlock(catchFlow.method,
+ catchFlow.getAddr());
+ newFlow.appendBlock(jump, 0);
+ catchFlow.prevByAddr.nextByAddr = newFlow;
+ newFlow.prevByAddr = catchFlow.prevByAddr;
+ newFlow.nextByAddr = catchFlow;
+ catchFlow.prevByAddr = newFlow;
+ catchFlow = newFlow;
+ } else {
if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println
- ("analyzeTryCatch(" + tryFlow.getAddr() + ", "
- + tryFlow.getNextAddr() + ") done.");
+ ("analyzeCatch("
+ + catchFlow.getAddr() + ", " + endHandler + ")");
+ while (catchFlow.analyze(catchFlow.getAddr(), endHandler));
}
+
+ if (exc.type != null)
+ analyzeCatchBlock(exc.type, tryFlow, catchFlow);
+
+ else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler)
+ && ! analyzeFinally(tryFlow, catchFlow, endHandler)
+ && ! analyzeSpecialFinally(tryFlow, catchFlow,
+ endHandler))
+
+ analyzeCatchBlock(Type.tObject, tryFlow, catchFlow);
+
+ tryFlow.checkConsistent();
+ if ((GlobalOptions.debuggingFlags
+ & GlobalOptions.DEBUG_ANALYZE) != 0)
+ GlobalOptions.err.println
+ ("analyzeTryCatch(" + tryFlow.getAddr() + ", "
+ + tryFlow.getNextAddr() + ") done.");
}
}
}
diff --git a/jode/jode/jvm/CodeVerifier.java.in b/jode/jode/jvm/CodeVerifier.java.in
index 556902f..b8fb2f8 100644
--- a/jode/jode/jvm/CodeVerifier.java.in
+++ b/jode/jode/jvm/CodeVerifier.java.in
@@ -207,19 +207,17 @@ public class CodeVerifier implements Opcodes {
dimensions++;
}
- if (c1 == '[' || c2 == '[') {
- // Only one of them is array now, the other must be an
- // object, the common super is tObject
- if (c1 == 'L' || c2 == 'L') {
- if (dimensions == 0)
- return tObject;
- StringBuffer result = new StringBuffer(dimensions + 18);
- for (int i=0; i< dimensions; i++)
- result.append("[");
- result.append("Ljava/lang/Object;");
- return tType(result.toString());
- }
- return tNone;
+ // One of them is array now, the other is an object,
+ // the common super is tObject
+ if ((c1 == '[' && c2 == 'L')
+ || (c1 == 'L' && c2 == '[')) {
+ if (dimensions == 0)
+ return tObject;
+ StringBuffer result = new StringBuffer(dimensions + 18);
+ for (int i=0; i< dimensions; i++)
+ result.append("[");
+ result.append("Ljava/lang/Object;");
+ return tType(result.toString());
}
if (c1 == 'L' && c2 == 'L') {
@@ -240,6 +238,18 @@ public class CodeVerifier implements Opcodes {
.append(clazz1.getName().replace('.', '/')).append(";");
return tType(result.toString());
}
+
+ // Both were arrays, but of different primitive types. The
+ // common super is tObject with one dimension less.
+ if (dimensions > 0) {
+ if (dimensions == 1)
+ return tObject;
+ StringBuffer result = new StringBuffer(dimensions + 17);
+ for (int i=0; i < dimensions - 1; i++)
+ result.append("[");
+ result.append("Ljava/lang/Object;");
+ return tType(result.toString());
+ }
return tNone;
}
@@ -1044,21 +1054,6 @@ public class CodeVerifier implements Opcodes {
}
if (instr.getSuccs() != null) {
for (int i=0; i< instr.getSuccs().length; i++) {
- if (instr.getSuccs()[i].getAddr() < instr.getAddr()) {
- /* This is a backwards branch */
- for (int j = 0; j < prevInfo.locals.length; j++) {
- if (prevInfo.locals[j]
- .getTypeSig().charAt(0) == 'N')
- throw new VerifyException
- ("Uninitialized local in back-branch");
- }
- for (int j = 0; j < prevInfo.stackHeight; j++) {
- if (prevInfo.stack[j]
- .getTypeSig().charAt(0) == 'N')
- throw new VerifyException
- ("Uninitialized stack in back-branch");
- }
- }
if (mergeInfo(instr.getSuccs()[i],
(VerifyInfo) info.clone()))
todoSet.add(instr.getSuccs()[i]);
@@ -1067,12 +1062,6 @@ public class CodeVerifier implements Opcodes {
for (int i=0; i= 0) {
- for (int j = 0; j < prevInfo.locals.length; j++) {
- if (prevInfo.locals[j]
- .getTypeSig().charAt(0) == 'N')
- throw new VerifyException
- ("Uninitialized local in try block");
- }
VerifyInfo excInfo = (VerifyInfo) prevInfo.clone();
excInfo.stackHeight = 1;
if (handlers[i].type != null)
diff --git a/jode/jode/jvm/SyntheticAnalyzer.java.in b/jode/jode/jvm/SyntheticAnalyzer.java.in
index 4e55ef4..76080e9 100644
--- a/jode/jode/jvm/SyntheticAnalyzer.java.in
+++ b/jode/jode/jvm/SyntheticAnalyzer.java.in
@@ -27,6 +27,7 @@ import jode.bytecode.Instruction;
import jode.bytecode.MethodInfo;
import jode.bytecode.Opcodes;
import jode.bytecode.Reference;
+import jode.bytecode.TypeSignature;
import jode.type.Type;
import jode.type.MethodType;
@@ -44,10 +45,13 @@ public class SyntheticAnalyzer implements Opcodes {
public final static int ACCESSPUTSTATIC = 6;
public final static int ACCESSSTATICMETHOD = 7;
public final static int ACCESSCONSTRUCTOR = 8;
+ public final static int ACCESSDUPPUTFIELD = 9;
+ public final static int ACCESSDUPPUTSTATIC = 10;
int kind = UNKNOWN;
Reference reference;
MethodInfo method;
+ int unifyParam = -1;
public SyntheticAnalyzer(MethodInfo method, boolean checkName) {
this.method = method;
@@ -72,6 +76,14 @@ public class SyntheticAnalyzer implements Opcodes {
return reference;
}
+ /**
+ * Gets the index of the dummy parameter for an ACCESSCONSTRUCTOR.
+ * Normally the 1 but for inner classes it may be 2.
+ */
+ public int getUnifyParam() {
+ return unifyParam;
+ }
+
private static final int[] getClassOpcodes = {
opc_aload, opc_invokestatic, opc_areturn,
opc_astore, opc_new, opc_dup, opc_aload,
@@ -105,6 +117,8 @@ public class SyntheticAnalyzer implements Opcodes {
int i = 0;
for (Iterator iter = bytecode.getInstructions().iterator(); iter.hasNext(); i++) {
Instruction instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (i == getClassOpcodes.length
|| instr.getOpcode() != getClassOpcodes[i])
return false;
@@ -131,27 +145,29 @@ public class SyntheticAnalyzer implements Opcodes {
return true;
}
- private final int modifierMask = (Modifier.PRIVATE | Modifier.PROTECTED |
- Modifier.PUBLIC | Modifier.STATIC);
+ private final int modifierMask = Modifier.PUBLIC | Modifier.STATIC;
public boolean checkStaticAccess() {
ClassInfo clazzInfo = method.getClazzInfo();
BytecodeInfo bytecode = method.getBytecode();
Iterator iter = bytecode.getInstructions().iterator();
+ boolean dupSeen = false;
Instruction instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (instr.getOpcode() == opc_getstatic) {
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
FieldInfo refField
- = clazzInfo.findField(ref.getName(), ref.getType());
- if ((refField.getModifiers() & modifierMask) !=
- (Modifier.PRIVATE | Modifier.STATIC))
+ = refClazz.findField(ref.getName(), ref.getType());
+ if ((refField.getModifiers() & modifierMask) != Modifier.STATIC)
return false;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (instr.getOpcode() < opc_ireturn
|| instr.getOpcode() > opc_areturn)
return false;
@@ -168,42 +184,62 @@ public class SyntheticAnalyzer implements Opcodes {
slot += (instr.getOpcode() == opc_lload
|| instr.getOpcode() == opc_dload) ? 2 : 1;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ }
+ if (instr.getOpcode() == (opc_dup - 3) + 3 * slot) {
+ /* This is probably a opc_dup or opc_dup2,
+ * preceding a opc_putstatic
+ */
+ instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ if (instr.getOpcode() != opc_putstatic)
+ return false;
+ dupSeen = true;
}
if (instr.getOpcode() == opc_putstatic) {
if (params != 1)
return false;
/* For valid bytecode the type of param matches automatically */
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
FieldInfo refField
- = clazzInfo.findField(ref.getName(), ref.getType());
- if ((refField.getModifiers() & modifierMask) !=
- (Modifier.PRIVATE | Modifier.STATIC))
+ = refClazz.findField(ref.getName(), ref.getType());
+ if ((refField.getModifiers() & modifierMask) != Modifier.STATIC)
return false;
instr = (Instruction) iter.next();
- if (instr.getOpcode() != opc_return)
- return false;
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ if (dupSeen) {
+ if (instr.getOpcode() < opc_ireturn
+ || instr.getOpcode() > opc_areturn)
+ return false;
+ kind = ACCESSDUPPUTSTATIC;
+ } else {
+ if (instr.getOpcode() != opc_return)
+ return false;
+ kind = ACCESSPUTSTATIC;
+ }
reference = ref;
- kind = ACCESSPUTSTATIC;
return true;
}
if (instr.getOpcode() == opc_invokestatic) {
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
MethodInfo refMethod
- = clazzInfo.findMethod(ref.getName(), ref.getType());
+ = refClazz.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(ref.getType());
- if ((refMethod.getModifiers() & modifierMask) !=
- (Modifier.PRIVATE | Modifier.STATIC)
+ if ((refMethod.getModifiers() & modifierMask) != Modifier.STATIC
|| refType.getParameterTypes().length != params)
return false;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (refType.getReturnType() == Type.tVoid) {
if (instr.getOpcode() != opc_return)
return false;
@@ -225,6 +261,7 @@ public class SyntheticAnalyzer implements Opcodes {
ClassInfo clazzInfo = method.getClazzInfo();
BytecodeInfo bytecode = method.getBytecode();
Handler[] excHandlers = bytecode.getExceptionHandlers();
+ boolean dupSeen = false;
if (excHandlers != null && excHandlers.length != 0)
return false;
@@ -235,21 +272,26 @@ public class SyntheticAnalyzer implements Opcodes {
Iterator iter = bytecode.getInstructions().iterator();
Instruction instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0)
return false;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (instr.getOpcode() == opc_getfield) {
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
FieldInfo refField
- = clazzInfo.findField(ref.getName(), ref.getType());
- if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE)
+ = refClazz.findField(ref.getName(), ref.getType());
+ if ((refField.getModifiers() & modifierMask) != 0)
return false;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (instr.getOpcode() < opc_ireturn
|| instr.getOpcode() > opc_areturn)
return false;
@@ -266,40 +308,63 @@ public class SyntheticAnalyzer implements Opcodes {
slot += (instr.getOpcode() == opc_lload
|| instr.getOpcode() == opc_dload) ? 2 : 1;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ }
+ if (instr.getOpcode() == (opc_dup_x1 - 6) + 3 * slot) {
+ /* This is probably a opc_dup_x1 or opc_dup2_x1,
+ * preceding a opc_putfield
+ */
+ instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ if (instr.getOpcode() != opc_putfield)
+ return false;
+ dupSeen = true;
}
if (instr.getOpcode() == opc_putfield) {
if (params != 1)
return false;
/* For valid bytecode the type of param matches automatically */
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
FieldInfo refField
- = clazzInfo.findField(ref.getName(), ref.getType());
- if ((refField.getModifiers() & modifierMask) != Modifier.PRIVATE)
+ = refClazz.findField(ref.getName(), ref.getType());
+ if ((refField.getModifiers() & modifierMask) != 0)
return false;
+
instr = (Instruction) iter.next();
- if (instr.getOpcode() != opc_return)
- return false;
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ if (dupSeen) {
+ if (instr.getOpcode() < opc_ireturn
+ || instr.getOpcode() > opc_areturn)
+ return false;
+ kind = ACCESSDUPPUTFIELD;
+ } else {
+ if (instr.getOpcode() != opc_return)
+ return false;
+ kind = ACCESSPUTFIELD;
+ }
reference = ref;
- kind = ACCESSPUTFIELD;
return true;
}
if (instr.getOpcode() == opc_invokespecial) {
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (!refClazz.superClassOf(clazzInfo))
return false;
MethodInfo refMethod
- = clazzInfo.findMethod(ref.getName(), ref.getType());
+ = refClazz.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(ref.getType());
- if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE
+ if ((refMethod.getModifiers() & modifierMask) != 0
|| refType.getParameterTypes().length != params)
return false;
instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
if (refType.getReturnType() == Type.tVoid) {
if (instr.getOpcode() != opc_return)
return false;
@@ -320,45 +385,62 @@ public class SyntheticAnalyzer implements Opcodes {
public boolean checkConstructorAccess() {
ClassInfo clazzInfo = method.getClazzInfo();
BytecodeInfo bytecode = method.getBytecode();
+ String[] paramTypes
+ = TypeSignature.getParameterTypes(method.getType());
Handler[] excHandlers = bytecode.getExceptionHandlers();
if (excHandlers != null && excHandlers.length != 0)
return false;
-
Iterator iter = bytecode.getInstructions().iterator();
- Instruction instr = (Instruction) iter.next();
- if (instr.getOpcode() != opc_aload || instr.getLocalSlot() != 0)
- return false;
- instr = (Instruction) iter.next();
- // slot begins with 2. Slot 1 contains a dummy value, that
- // is used so that the constructor has a different type signature.
- int params = 0, slot = 2;
+ Instruction instr = (Instruction) iter.next();
+ while (instr.getOpcode() == opc_nop && iter.hasNext())
+ instr = (Instruction) iter.next();
+ int params = 0, slot = 0;
while (instr.getOpcode() >= opc_iload
- && instr.getOpcode() <= opc_aload
- && instr.getLocalSlot() == slot) {
+ && instr.getOpcode() <= opc_aload) {
+
+ if (instr.getLocalSlot() > slot
+ && unifyParam == -1 && params > 0
+ && paramTypes[params - 1].charAt(0) == 'L') {
+ unifyParam = params;
+ params++;
+ slot++;
+ }
+ if (instr.getLocalSlot() != slot)
+ return false;
+
params++;
slot += (instr.getOpcode() == opc_lload
|| instr.getOpcode() == opc_dload) ? 2 : 1;
instr = (Instruction) iter.next();
}
- if (instr.getOpcode() == opc_invokespecial) {
+ if (params > 0 && instr.getOpcode() == opc_invokespecial) {
+
+ if (unifyParam == -1 && params <= paramTypes.length
+ && paramTypes[params - 1].charAt(0) == 'L')
+ unifyParam = params++;
+
Reference ref = instr.getReference();
- String refClazz = ref.getClazz().substring(1);
- if (!(refClazz.substring(0, refClazz.length()-1)
- .equals(clazzInfo.getName().replace('.','/'))))
+ ClassInfo refClazz = TypeSignature.getClassInfo(ref.getClazz());
+ if (refClazz != clazzInfo)
return false;
MethodInfo refMethod
- = clazzInfo.findMethod(ref.getName(), ref.getType());
+ = refClazz.findMethod(ref.getName(), ref.getType());
MethodType refType = Type.tMethod(ref.getType());
- if ((refMethod.getModifiers() & modifierMask) != Modifier.PRIVATE
+ if ((refMethod.getModifiers() & modifierMask) != 0
|| !refMethod.getName().equals("")
- || refType.getParameterTypes().length != params)
+ || unifyParam == -1
+ || refType.getParameterTypes().length != params - 2)
return false;
+
instr = (Instruction) iter.next();
if (instr.getOpcode() != opc_return)
return false;
- /* For valid bytecode the types matches automatically */
+ /* We don't check if types matches. No problem since we only
+ * need to make sure, this constructor doesn't do anything
+ * more than relay to the real one.
+ */
reference = ref;
kind = ACCESSCONSTRUCTOR;
return true;
diff --git a/jode/jode/obfuscator/ClassBundle.java.in b/jode/jode/obfuscator/ClassBundle.java.in
index c9ba0f6..0875455 100644
--- a/jode/jode/obfuscator/ClassBundle.java.in
+++ b/jode/jode/obfuscator/ClassBundle.java.in
@@ -38,7 +38,7 @@ import @COLLECTIONS@.TreeMap;
import @COLLECTIONEXTRA@.UnsupportedOperationException;
///#ifdef JDK12
-///import @COLLECTIONS@.WeakHashMap;
+import @COLLECTIONS@.WeakHashMap;
///#endif
public class ClassBundle implements OptionHandler {
@@ -52,8 +52,9 @@ public class ClassBundle implements OptionHandler {
String classPath;
String destDir;
- String tableFile;
- String toTableFile;
+ String inTableFile;
+ String outTableFile;
+ String outRevTableFile;
IdentifierMatcher loading;
IdentifierMatcher preserving;
@@ -74,9 +75,9 @@ public class ClassBundle implements OptionHandler {
}
///#ifdef JDK12
-/// private static final Map aliasesHash = new WeakHashMap();
+ private static final Map aliasesHash = new WeakHashMap();
///#else
- private static final Map aliasesHash = new HashMap();
+/// private static final Map aliasesHash = new HashMap();
///#endif
private static final Map clazzCache = new HashMap();
private static final Map referenceCache = new HashMap();
@@ -103,21 +104,39 @@ public class ClassBundle implements OptionHandler {
return;
}
- if (option.equals("table")) {
+ if (option.equals("verbose")) {
+ if (values.size() != 1)
+ throw new IllegalArgumentException
+ ("Verbose takes one int parameter");
+ GlobalOptions.verboseLevel
+ = ((Integer) values.iterator().next()).intValue();
+ return;
+ }
+
+ if (option.equals("intable") || option.equals("table")) {
+ if (values.size() != 1)
+ throw new IllegalArgumentException
+ ("Only one destination path allowed");
+ inTableFile = (String) values.iterator().next();
+ return;
+ }
+
+ if (option.equals("outtable")) {
if (values.size() != 1)
throw new IllegalArgumentException
("Only one destination path allowed");
- tableFile = (String) values.iterator().next();
+ outTableFile = (String) values.iterator().next();
return;
}
- if (option.equals("revtable")) {
+ if (option.equals("outrevtable") || option.equals("revtable")) {
if (values.size() != 1)
throw new IllegalArgumentException
("Only one destination path allowed");
- toTableFile = (String) values.iterator().next();
+ outRevTableFile = (String) values.iterator().next();
return;
}
+
if (option.equals("strip")) {
next_token:
for (Iterator iter = values.iterator(); iter.hasNext(); ) {
@@ -248,6 +267,13 @@ public class ClassBundle implements OptionHandler {
return alias;
}
+ public String getClassAlias(String className) {
+ ClassIdentifier classIdent = getClassIdentifier(className);
+ if (classIdent == null)
+ return className;
+ return classIdent.getFullAlias();
+ }
+
public String getTypeAlias(String typeSig) {
String alias = (String) aliasesHash.get(typeSig);
if (alias == null) {
@@ -256,7 +282,7 @@ public class ClassBundle implements OptionHandler {
while ((nextindex = typeSig.indexOf('L', index)) != -1) {
newSig.append(typeSig.substring(index, nextindex+1));
index = typeSig.indexOf(';', nextindex);
- String typeAlias = basePackage.findAlias
+ String typeAlias = getClassAlias
(typeSig.substring(nextindex+1, index).replace('/','.'));
newSig.append(typeAlias.replace('.', '/'));
}
@@ -352,25 +378,41 @@ public class ClassBundle implements OptionHandler {
public void readTable() {
try {
TranslationTable table = new TranslationTable();
- InputStream input = new FileInputStream(tableFile);
+ InputStream input = new FileInputStream(inTableFile);
table.load(input);
input.close();
basePackage.readTable(table);
} catch (java.io.IOException ex) {
- GlobalOptions.err.println("Can't read rename table " + tableFile);
+ GlobalOptions.err.println("Can't read rename table "
+ + inTableFile);
ex.printStackTrace(GlobalOptions.err);
}
}
public void writeTable() {
TranslationTable table = new TranslationTable();
- basePackage.writeTable(table);
+ basePackage.writeTable(table, false);
try {
- OutputStream out = new FileOutputStream(toTableFile);
+ OutputStream out = new FileOutputStream(outTableFile);
table.store(out);
out.close();
} catch (java.io.IOException ex) {
- GlobalOptions.err.println("Can't write rename table "+toTableFile);
+ GlobalOptions.err.println("Can't write rename table "
+ + outTableFile);
+ ex.printStackTrace(GlobalOptions.err);
+ }
+ }
+
+ public void writeRevTable() {
+ TranslationTable revtable = new TranslationTable();
+ basePackage.writeTable(revtable, true);
+ try {
+ OutputStream out = new FileOutputStream(outRevTableFile);
+ revtable.store(out);
+ out.close();
+ } catch (java.io.IOException ex) {
+ GlobalOptions.err.println("Can't write rename table "
+ + outRevTableFile);
ex.printStackTrace(GlobalOptions.err);
}
}
@@ -470,11 +512,13 @@ public class ClassBundle implements OptionHandler {
GlobalOptions.err.println("Renaming methods");
time = System.currentTimeMillis();
- if (tableFile != null)
+ if (inTableFile != null)
readTable();
buildTable(renamer);
- if (toTableFile != null)
+ if (outTableFile != null)
writeTable();
+ if (outRevTableFile != null)
+ writeRevTable();
System.err.println("Time used: "+(System.currentTimeMillis() - time));
GlobalOptions.err.println("Transforming the classes");
diff --git a/jode/jode/obfuscator/ClassIdentifier.java.in b/jode/jode/obfuscator/ClassIdentifier.java.in
index 9894f43..f25115d 100644
--- a/jode/jode/obfuscator/ClassIdentifier.java.in
+++ b/jode/jode/obfuscator/ClassIdentifier.java.in
@@ -30,7 +30,6 @@ import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.List;
import @COLLECTIONS@.LinkedList;
import @COLLECTIONS@.Map;
-import @COLLECTIONS@.Random;
import @COLLECTIONEXTRA@.UnsupportedOperationException;
import java.lang.reflect.Modifier;
@@ -374,9 +373,8 @@ public class ClassIdentifier extends Identifier {
FieldInfo[] finfos = info.getFields();
MethodInfo[] minfos = info.getMethods();
if (Main.swapOrder) {
- Random rand = new Random();
- Collections.shuffle(Arrays.asList(finfos), rand);
- Collections.shuffle(Arrays.asList(minfos), rand);
+ Collections.shuffle(Arrays.asList(finfos), Main.rand);
+ Collections.shuffle(Arrays.asList(minfos), Main.rand);
}
fieldIdents = new ArrayList(finfos.length);
methodIdents = new ArrayList(minfos.length);
@@ -401,15 +399,11 @@ public class ClassIdentifier extends Identifier {
ifaceNames = new String[ifaces.length];
for (int i=0; i < ifaces.length; i++) {
ifaceNames[i] = ifaces[i].getName();
- ClassIdentifier ifaceident = Main.getClassBundle()
- .getClassIdentifier(ifaceNames[i]);
initSuperClasses(ifaces[i]);
}
if (info.getSuperclass() != null) {
superName = info.getSuperclass().getName();
- ClassIdentifier superident = Main.getClassBundle()
- .getClassIdentifier(superName);
initSuperClasses(info.getSuperclass());
}
@@ -458,11 +452,10 @@ public class ClassIdentifier extends Identifier {
* @param ancestor The ancestor whose interfaces should be added.
*/
public void addIfaces(Collection result, ClassIdentifier ancestor) {
- String[] ifaces = ancestor.ifaceNames;
ClassInfo[] ifaceInfos = ancestor.info.getInterfaces();
- for (int i=0; i < ifaces.length; i++) {
+ for (int i=0; i < ifaceInfos.length; i++) {
ClassIdentifier ifaceident
- = Main.getClassBundle().getClassIdentifier(ifaces[i]);
+ = Main.getClassBundle().getClassIdentifier(ifaceInfos[i].getName());
if (ifaceident != null && !ifaceident.isReachable())
addIfaces(result, ifaceident);
else
diff --git a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in
index fc5346c..59dcb3c 100644
--- a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in
+++ b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java.in
@@ -215,11 +215,16 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
}
private Interpreter interpreter;
+ private Identifier currentFieldListener;
public ConstantRuntimeEnvironment() {
interpreter = new Interpreter(this);
}
+ public void setFieldListener(Identifier fl) {
+ currentFieldListener = fl;
+ }
+
public static Object getDefaultValue(String typeSig) {
switch(typeSig.charAt(0)) {
case 'Z':
@@ -247,6 +252,8 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
= (FieldIdentifier) Main.getClassBundle().getIdentifier(ref);
if (fi != null && !fi.isNotConstant()) {
Object result = fi.getConstant();
+ if (currentFieldListener != null)
+ fi.addFieldListener(currentFieldListener);
if (result == null)
result = getDefaultValue(ref.getType());
return result;
diff --git a/jode/jode/obfuscator/Identifier.java.in b/jode/jode/obfuscator/Identifier.java.in
index 2260f95..01e9786 100644
--- a/jode/jode/obfuscator/Identifier.java.in
+++ b/jode/jode/obfuscator/Identifier.java.in
@@ -199,7 +199,7 @@ public abstract class Identifier {
((Identifier)i.next()).buildTable(renameRule);
}
- public void writeTable(Map table) {
+ public void writeTable(Map table, boolean reversed) {
if (!isReachable()
&& (Main.stripping & Main.STRIP_UNREACH) != 0)
return;
@@ -212,11 +212,14 @@ public abstract class Identifier {
name = outer.getName() + "." + name;
outer = outer.getParent();
}
- table.put(getFullAlias(), name);
+ if (reversed)
+ table.put(getFullAlias(), name);
+ else
+ table.put(getFullName(), getAlias());
}
for (Iterator i = getChilds(); i.hasNext(); )
- ((Identifier)i.next()).writeTable(table);
+ ((Identifier)i.next()).writeTable(table, reversed);
}
public void readTable(Map table) {
diff --git a/jode/jode/obfuscator/Main.java.in b/jode/jode/obfuscator/Main.java.in
index adcf12e..796ec84 100644
--- a/jode/jode/obfuscator/Main.java.in
+++ b/jode/jode/obfuscator/Main.java.in
@@ -31,6 +31,7 @@ import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import @COLLECTIONS@.Collections;
+import @COLLECTIONS@.Random;
public class Main {
public static boolean swapOrder = false;
@@ -57,7 +58,15 @@ public class Main {
public static final int STRIP_LVT = 0x0004;
public static final int STRIP_LNT = 0x0008;
public static final int STRIP_SOURCE = 0x0010;
- public static int stripping = 0x1;
+ public static int stripping = 0;
+ /**
+ * A random pool used to destroy order of method identifiers and
+ * classes in packages.
+ *
+ * A pseudo random is enough, no need to generate the seed
+ * securely. This makes obfuscating errors reproducable.
+ */
+ public static Random rand = new Random(123456);
private static ClassBundle bundle;
diff --git a/jode/jode/obfuscator/PackageIdentifier.java.in b/jode/jode/obfuscator/PackageIdentifier.java.in
index cf239f3..07b43a7 100644
--- a/jode/jode/obfuscator/PackageIdentifier.java.in
+++ b/jode/jode/obfuscator/PackageIdentifier.java.in
@@ -34,7 +34,6 @@ import @COLLECTIONS@.HashMap;
import @COLLECTIONS@.Iterator;
import @COLLECTIONS@.List;
import @COLLECTIONS@.ArrayList;
-import @COLLECTIONS@.Random;
import @COLLECTIONS@.Arrays;
import @COLLECTIONS@.Collections;
@@ -47,7 +46,6 @@ public class PackageIdentifier extends Identifier {
boolean loadOnDemand;
Map loadedClasses;
List swappedClasses;
- Random rand = new Random();
public PackageIdentifier(ClassBundle bundle,
PackageIdentifier parent,
@@ -310,21 +308,6 @@ public class PackageIdentifier extends Identifier {
return "";
}
- public String findAlias(String className) {
- int index = className.indexOf('.');
- if (index == -1) {
- Identifier ident = getIdentifier(className);
- if (ident != null)
- return ident.getFullAlias();
- } else {
- Identifier pack = getIdentifier(className.substring(0, index));
- if (pack != null)
- return ((PackageIdentifier)pack)
- .findAlias(className.substring(index+1));
- }
- return className;
- }
-
public void buildTable(Renamer renameRule) {
loadOnDemand = false;
super.buildTable(renameRule);
@@ -370,7 +353,7 @@ public class PackageIdentifier extends Identifier {
*/
if (swappedClasses == null) {
swappedClasses = Arrays.asList(loadedClasses.values().toArray());
- Collections.shuffle(swappedClasses, rand);
+ Collections.shuffle(swappedClasses, Main.rand);
}
return swappedClasses.iterator();
}
diff --git a/jode/jode/obfuscator/TranslationTable.java.in b/jode/jode/obfuscator/TranslationTable.java.in
index 40d8574..4247046 100644
--- a/jode/jode/obfuscator/TranslationTable.java.in
+++ b/jode/jode/obfuscator/TranslationTable.java.in
@@ -24,7 +24,7 @@ import @COLLECTIONS@.TreeMap;
import @COLLECTIONS@.Iterator;
///#ifndef JDK12
-import @COLLECTIONS@.Comparator;
+///import @COLLECTIONS@.Comparator;
///#endif
import java.io.InputStream;
@@ -37,17 +37,17 @@ import java.io.IOException;
public class TranslationTable extends TreeMap {
///#ifndef JDK12
- public TranslationTable() {
- super(createStringComparator());
- }
-
- private static Comparator createStringComparator() {
- return new Comparator() {
- public int compare(Object o1, Object o2) {
- return ((String) o1).compareTo((String) o2);
- }
- };
- }
+/// public TranslationTable() {
+/// super(createStringComparator());
+/// }
+///
+/// private static Comparator createStringComparator() {
+/// return new Comparator() {
+/// public int compare(Object o1, Object o2) {
+/// return ((String) o1).compareTo((String) o2);
+/// }
+/// };
+/// }
///#endif
public void load(InputStream in) throws IOException {
diff --git a/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in b/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in
index 237b87d..1943c95 100644
--- a/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in
+++ b/jode/jode/obfuscator/modules/ConstantAnalyzer.java.in
@@ -45,7 +45,8 @@ import @COLLECTIONS@.ListIterator;
*
* @author Jochen Hoenicke
*/
-public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
+public class ConstantAnalyzer extends SimpleAnalyzer
+ implements Opcodes, CodeAnalyzer {
BytecodeInfo bytecode;
@@ -517,74 +518,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
((StackLocalInfo)instr.getTmpInfo()).merge(info);
}
- 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;
- }
-
- public Identifier canonizeReference(Instruction instr) {
- Reference ref = instr.getReference();
- Identifier ident = Main.getClassBundle().getIdentifier(ref);
- String clName = ref.getClazz();
- String realClazzName;
- if (ident != null) {
- ClassIdentifier clazz = (ClassIdentifier)ident.getParent();
- realClazzName = "L" + (clazz.getFullName()
- .replace('.', '/')) + ";";
- } else {
- /* We have to look at the ClassInfo's instead, to
- * point to the right method.
- */
- ClassInfo clazz;
- if (clName.charAt(0) == '[') {
- /* Arrays don't define new methods (well clone(),
- * but that can be ignored).
- */
- clazz = ClassInfo.javaLangObject;
- } else {
- clazz = ClassInfo.forName
- (clName.substring(1, clName.length()-1)
- .replace('/','.'));
- }
- if (instr.getOpcode() == opc_invokeinterface) {
- clazz = canonizeIfaceRef(clazz, ref);
- } else if (instr.getOpcode() >= opc_invokevirtual) {
- while (clazz != null
- && clazz.findMethod(ref.getName(),
- ref.getType()) == null)
- clazz = clazz.getSuperclass();
- } else {
- while (clazz != null
- && clazz.findField(ref.getName(),
- ref.getType()) == null)
- clazz = clazz.getSuperclass();
- }
-
- if (clazz == null) {
- GlobalOptions.err.println("WARNING: Can't find reference: "
- +ref);
- realClazzName = clName;
- } else
- realClazzName = "L" + clazz.getName().replace('.', '/') + ";";
- }
- if (!realClazzName.equals(ref.getClazz())) {
- ref = Reference.getReference(realClazzName,
- ref.getName(), ref.getType());
- instr.setReference(ref);
- }
- return ident;
- }
-
public void handleReference(Reference ref, boolean isVirtual) {
Main.getClassBundle().reachableReference(ref, isVirtual);
}
@@ -602,7 +535,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
public void handleOpcode(StackLocalInfo info, Identifier fieldListener) {
Instruction instr = info.instr;
info.constInfo = unknownConstInfo;
-
+
int opcode = instr.getOpcode();
Handler[] handlers = bytecode.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) {
@@ -734,9 +667,9 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
boolean known = value1.value != ConstValue.VOLATILE
&& value2.value != ConstValue.VOLATILE;
if (known) {
- if ((opcode == opc_idiv
+ if (((opcode == opc_idiv || opcode == opc_irem)
&& ((Integer)value2.value).intValue() == 0)
- || (opcode == opc_ldiv
+ || ((opcode == opc_ldiv || opcode == opc_lrem)
&& ((Long)value2.value).longValue() == 0))
known = false;
}
@@ -1470,12 +1403,14 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
firstInfo.instr = (Instruction) bytecode.getInstructions().get(0);
firstInfo.instr.setTmpInfo(firstInfo);
firstInfo.enqueue();
+ runtime.setFieldListener(methodIdent);
while (modifiedQueue.first != null) {
StackLocalInfo info = modifiedQueue.first;
modifiedQueue.first = info.nextOnQueue;
info.nextOnQueue = null;
handleOpcode(info, methodIdent);
}
+ runtime.setFieldListener(null);
Handler[] handlers = bytecode.getExceptionHandlers();
for (int i=0; i< handlers.length; i++) {
@@ -1522,10 +1457,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
case opc_ineg: case opc_fneg:
iter.set(new Instruction(opc_pop));
break;
- case opc_if_icmpeq: case opc_if_icmpne:
- case opc_if_icmplt: case opc_if_icmpge:
- case opc_if_icmpgt: case opc_if_icmple:
- case opc_if_acmpeq: case opc_if_acmpne:
case opc_lcmp:
case opc_dcmpg: case opc_dcmpl:
case opc_ladd: case opc_dadd:
@@ -1535,7 +1466,12 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
case opc_lrem: case opc_drem:
case opc_land: case opc_lor : case opc_lxor:
iter.set(new Instruction(opc_pop2));
- /* fall through */
+ iter.add(new Instruction(opc_pop2));
+ break;
+ case opc_if_icmpeq: case opc_if_icmpne:
+ case opc_if_icmplt: case opc_if_icmpge:
+ case opc_if_icmpgt: case opc_if_icmple:
+ case opc_if_acmpeq: case opc_if_acmpne:
case opc_fcmpg: case opc_fcmpl:
case opc_l2i: case opc_l2f: case opc_l2d:
case opc_d2i: case opc_d2l: case opc_d2f:
@@ -1574,13 +1510,18 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
case opc_invokevirtual: {
Reference ref = instr.getReference();
String[] pt = TypeSignature.getParameterTypes(ref.getType());
- int arg = 0;
- if (instr.getOpcode() != opc_invokestatic)
- iter.set(new Instruction(opc_pop));
- else if (pt.length > 0) {
- iter.set(new Instruction(TypeSignature.getTypeSize(pt[0])
+ int len = pt.length;
+
+ if (len > 0) {
+ iter.set(new Instruction(TypeSignature.getTypeSize(pt[--len])
+ opc_pop - 1));
- arg++;
+ for (int i = len - 1; i >= 0; i--)
+ iter.add(new Instruction(TypeSignature.getTypeSize(pt[i])
+ + opc_pop - 1));
+ if (instr.getOpcode() != opc_invokestatic)
+ iter.add(new Instruction(opc_pop));
+ } else if (instr.getOpcode() != opc_invokestatic) {
+ iter.set(new Instruction(opc_pop));
} else {
if (replacement == null)
iter.remove();
@@ -1589,9 +1530,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer {
return;
}
- for (int i=arg; i < pt.length; i++)
- iter.add(new Instruction(TypeSignature.getTypeSize(pt[i])
- + opc_pop - 1));
}
}
if (replacement != null)
diff --git a/jode/jode/obfuscator/modules/LocalOptimizer.java.in b/jode/jode/obfuscator/modules/LocalOptimizer.java.in
index cb776fc..bb1b0c0 100644
--- a/jode/jode/obfuscator/modules/LocalOptimizer.java.in
+++ b/jode/jode/obfuscator/modules/LocalOptimizer.java.in
@@ -657,17 +657,12 @@ public class LocalOptimizer implements Opcodes, CodeTransformer {
*/
distributeLocals(locals);
- /* Update the instructions and calculate new maxlocals.
+ /* Update the instructions.
*/
- maxlocals = paramLocals.length;
for (InstrInfo info = firstInfo; info != null; info = info.nextInfo) {
- if (info.local != null) {
- if (info.local.newSlot+info.local.size > maxlocals)
- maxlocals = info.local.newSlot + info.local.size;
+ if (info.local != null)
info.instr.setLocalSlot(info.local.newSlot);
- }
}
- bc.setMaxLocals(maxlocals);
/* Update LocalVariableTable
*/
diff --git a/jode/jode/obfuscator/modules/ModifierMatcher.java.in b/jode/jode/obfuscator/modules/ModifierMatcher.java.in
index 1c8d176..8d36c49 100644
--- a/jode/jode/obfuscator/modules/ModifierMatcher.java.in
+++ b/jode/jode/obfuscator/modules/ModifierMatcher.java.in
@@ -109,7 +109,7 @@ public class ModifierMatcher implements IdentifierMatcher, OptionHandler, Clonea
: str.equals("NATIVE") ? Modifier.NATIVE
: str.equals("STATIC") ? Modifier.STATIC
///#ifdef JDK12
-/// : str.equals("STRICT") ? Modifier.STRICT
+ : str.equals("STRICT") ? Modifier.STRICT
///#endif
: str.equals("SYNCHRONIZED") ? Modifier.SYNCHRONIZED
: str.equals("TRANSIENT") ? Modifier.TRANSIENT
diff --git a/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in b/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in
index ec5459c..ab1b370 100644
--- a/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in
+++ b/jode/jode/obfuscator/modules/SimpleAnalyzer.java.in
@@ -34,6 +34,21 @@ import @COLLECTIONS@.ListIterator;
public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
+ 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;
+ }
+
public Identifier canonizeReference(Instruction instr) {
Reference ref = instr.getReference();
Identifier ident = Main.getClassBundle().getIdentifier(ref);
@@ -58,7 +73,9 @@ public class SimpleAnalyzer implements CodeAnalyzer, Opcodes {
(clName.substring(1, clName.length()-1)
.replace('/','.'));
}
- if (instr.getOpcode() >= opc_invokevirtual) {
+ 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)
diff --git a/jode/jode/swingui/Main.java.in b/jode/jode/swingui/Main.java.in
index 21bcf36..db0707c 100644
--- a/jode/jode/swingui/Main.java.in
+++ b/jode/jode/swingui/Main.java.in
@@ -79,9 +79,11 @@ public class Main
classTree.addTreeSelectionListener(this);
JScrollPane spClassTree = new JScrollPane(classTree);
sourcecodeArea = new JTextArea(20, 80);
+ sourcecodeArea.setEditable(false);
sourcecodeArea.setFont(monospaced);
JScrollPane spText = new JScrollPane(sourcecodeArea);
errorArea = new JTextArea(3, 80);
+ errorArea.setEditable(false);
errorArea.setFont(monospaced);
JScrollPane spError = new JScrollPane(errorArea);
@@ -141,6 +143,7 @@ public class Main
public class AreaWriter extends Writer {
boolean initialized = false;
+ boolean lastCR = false;
private JTextArea area;
public AreaWriter(JTextArea a) {
@@ -153,7 +156,23 @@ public class Main
area.setText("");
initialized = true;
}
- area.append(new String(b, off, len));
+ String str = new String(b, off, len);
+ StringBuffer sb = new StringBuffer(len);
+ while (str != null && str.length() > 0) {
+ if (lastCR && str.charAt(0) == '\n')
+ str = str.substring(1);
+ int crIndex = str.indexOf('\r');
+ if (crIndex >= 0) {
+ sb.append(str.substring(0, crIndex));
+ sb.append("\n");
+ str = str.substring(crIndex+1);
+ lastCR = true;
+ } else {
+ sb.append(str);
+ str = null;
+ }
+ }
+ area.append(sb.toString());
}
public void flush() {
@@ -325,16 +344,42 @@ public class Main
}
}
}
+
+ public static void usage() {
+ System.err.println("Usage: java jode.swingui.Main [CLASSPATH]");
+ System.err.println("The directories in CLASSPATH should be separated by ','.");
+ System.err.println("If no CLASSPATH is given the virtual machine classpath is used.");
+ }
public static void main(String[] params) {
String cp = System.getProperty("java.class.path", "");
cp = cp.replace(File.pathSeparatorChar,
Decompiler.altPathSeparatorChar);
- for (int i=0; i tArray( y.intersection(x) )
- * obj , tArray(x) ->
+ /*
+ * tArray(y), tArray(x) -> tArray( y.intersection(x) )
+ * obj , tArray(x) ->
* iff tArray extends and implements obj
- */
+ */
if (bottom.getTypeCode() == TC_ARRAY)
return tArray(elementType.intersection
(((ArrayType)bottom).elementType));
@@ -93,19 +99,20 @@ public class ArrayType extends ReferenceType {
* @return the common sub type.
*/
public Type getSpecializedType(Type type) {
- /*
- * tArray(x), object -> tArray(x) iff tArray implements object
+ /*
+ * tArray(x), iface -> tArray(x) iff tArray implements iface
* tArray(x), tArray(y) -> tArray(x.intersection(y))
- * tArray(x), other -> tError
- */
+ * tArray(x), other -> tError
+ */
if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getBottom();
}
if (type == tNull)
return this;
if (type.getTypeCode() == TC_ARRAY) {
- return tArray(elementType.intersection
- (((ArrayType)type).elementType));
+ Type elType = elementType.intersection
+ (((ArrayType)type).elementType);
+ return elType != tError ? tArray(elType) : tError;
}
if (type.getTypeCode() == TC_CLASS) {
ClassInterfacesType other = (ClassInterfacesType) type;
@@ -122,19 +129,23 @@ public class ArrayType extends ReferenceType {
* @return the common super type.
*/
public Type getGeneralizedType(Type type) {
- /* tArray(x), tNull -> tArray(x)
- * tArray(x), tClass(y) -> common ifaces of tArray and tClass
- * tArray(x), tArray(y) -> tArray(x.intersection(y))
- * tArray(x), other -> tError
+ /* tArray(x), tNull -> tArray(x)
+ * tArray(x), tClass(y) -> common ifaces of tArray and tClass
+ * tArray(x), tArray(y) -> tArray(x.intersection(y)) or tObject
+ * tArray(x), other -> tError
*/
if (type.getTypeCode() == TC_RANGE) {
type = ((RangeType) type).getTop();
}
if (type == tNull)
return this;
- if (type.getTypeCode() == TC_ARRAY)
- return tArray(elementType.intersection
- (((ArrayType)type).elementType));
+ if (type.getTypeCode() == TC_ARRAY) {
+ Type elType = elementType.intersection
+ (((ArrayType)type).elementType);
+ if (elType != tError)
+ return tArray(elType);
+ return ClassInterfacesType.create(null, arrayIfaces);
+ }
if (type.getTypeCode() == TC_CLASS) {
ClassInterfacesType other = (ClassInterfacesType) type;
if (implementsAllIfaces(other.clazz, other.ifaces, arrayIfaces))
diff --git a/jode/jode/type/ClassInterfacesType.java b/jode/jode/type/ClassInterfacesType.java
index 0f00bfe..930a0a5 100644
--- a/jode/jode/type/ClassInterfacesType.java
+++ b/jode/jode/type/ClassInterfacesType.java
@@ -509,34 +509,57 @@ public class ClassInterfacesType extends ReferenceType {
private final static Hashtable keywords = new Hashtable();
static {
+ keywords.put("abstract", Boolean.TRUE);
+ keywords.put("default", Boolean.TRUE);
keywords.put("if", Boolean.TRUE);
- keywords.put("else", Boolean.TRUE);
- keywords.put("for", Boolean.TRUE);
- keywords.put("while", Boolean.TRUE);
+ keywords.put("private", Boolean.TRUE);
keywords.put("throw", Boolean.TRUE);
- keywords.put("return", Boolean.TRUE);
- keywords.put("class", Boolean.TRUE);
- keywords.put("interface", Boolean.TRUE);
+ keywords.put("boolean", Boolean.TRUE);
+ keywords.put("do", Boolean.TRUE);
keywords.put("implements", Boolean.TRUE);
- keywords.put("extends", Boolean.TRUE);
+ keywords.put("protected", Boolean.TRUE);
+ keywords.put("throws", Boolean.TRUE);
+ keywords.put("break", Boolean.TRUE);
+ keywords.put("double", Boolean.TRUE);
+ keywords.put("import", Boolean.TRUE);
+ keywords.put("public", Boolean.TRUE);
+ keywords.put("transient", Boolean.TRUE);
+ keywords.put("byte", Boolean.TRUE);
+ keywords.put("else", Boolean.TRUE);
keywords.put("instanceof", Boolean.TRUE);
- keywords.put("new", Boolean.TRUE);
+ keywords.put("return", Boolean.TRUE);
+ keywords.put("try", Boolean.TRUE);
+ keywords.put("case", Boolean.TRUE);
+ keywords.put("extends", Boolean.TRUE);
keywords.put("int", Boolean.TRUE);
- keywords.put("boolean", Boolean.TRUE);
- keywords.put("long", Boolean.TRUE);
- keywords.put("float", Boolean.TRUE);
- keywords.put("double", Boolean.TRUE);
keywords.put("short", Boolean.TRUE);
- keywords.put("public", Boolean.TRUE);
- keywords.put("protected", Boolean.TRUE);
- keywords.put("private", Boolean.TRUE);
+ keywords.put("void", Boolean.TRUE);
+ keywords.put("catch", Boolean.TRUE);
+ keywords.put("final", Boolean.TRUE);
+ keywords.put("interface", Boolean.TRUE);
keywords.put("static", Boolean.TRUE);
- keywords.put("synchronized", Boolean.TRUE);
- keywords.put("strict", Boolean.TRUE);
- keywords.put("transient", Boolean.TRUE);
- keywords.put("abstract", Boolean.TRUE);
keywords.put("volatile", Boolean.TRUE);
- keywords.put("final", Boolean.TRUE);
+ keywords.put("char", Boolean.TRUE);
+ keywords.put("finally", Boolean.TRUE);
+ keywords.put("long", Boolean.TRUE);
+ keywords.put("super", Boolean.TRUE);
+ keywords.put("while", Boolean.TRUE);
+ keywords.put("class", Boolean.TRUE);
+ keywords.put("float", Boolean.TRUE);
+ keywords.put("native", Boolean.TRUE);
+ keywords.put("switch", Boolean.TRUE);
+ keywords.put("const", Boolean.TRUE);
+ keywords.put("for", Boolean.TRUE);
+ keywords.put("new", Boolean.TRUE);
+ keywords.put("synchronized", Boolean.TRUE);
+ keywords.put("continue", Boolean.TRUE);
+ keywords.put("goto", Boolean.TRUE);
+ keywords.put("package", Boolean.TRUE);
+ keywords.put("this", Boolean.TRUE);
+ keywords.put("strictfp", Boolean.TRUE);
+ keywords.put("null", Boolean.TRUE);
+ keywords.put("true", Boolean.TRUE);
+ keywords.put("false", Boolean.TRUE);
}
/**
diff --git a/jode/jode/type/NullType.java b/jode/jode/type/NullType.java
index 1521b66..6fe8804 100644
--- a/jode/jode/type/NullType.java
+++ b/jode/jode/type/NullType.java
@@ -71,15 +71,4 @@ public class NullType extends ReferenceType {
public String toString() {
return "tNull";
}
-
- /**
- * Intersect this type with another type and return the new type.
- * @param type the other type.
- * @return the intersection, or tError, if a type conflict happens.
- */
- public Type intersection(Type type) {
- if (type == this)
- return type;
- return tError;
- }
}
diff --git a/jode/jode/type/RangeType.java b/jode/jode/type/RangeType.java
index c2eedb2..c8549be 100644
--- a/jode/jode/type/RangeType.java
+++ b/jode/jode/type/RangeType.java
@@ -107,13 +107,18 @@ public class RangeType extends Type {
/**
* Returns the hint type of this range type set. This returns the
* singleton set containing only the first top type, except if it
- * is null and there is a unique bottom type, in which case it returns
- * the bottom type.
+ * is null and there is a unique bottom type, in which case it
+ * returns the bottom type.
* @return the hint type.
*/
public Type getHint() {
- return topType == tNull && bottomType.equals(bottomType.getHint())
- ? bottomType.getHint(): topType.getHint();
+ Type bottomHint = bottomType.getHint();
+ Type topHint = topType.getHint();
+
+ if (topType == tNull && bottomType.equals(bottomHint))
+ return bottomHint;
+
+ return topHint;
}
/**
diff --git a/jode/jode/util/UnifyHash.java.in b/jode/jode/util/UnifyHash.java.in
index 8c5104c..a5661ce 100644
--- a/jode/jode/util/UnifyHash.java.in
+++ b/jode/jode/util/UnifyHash.java.in
@@ -1,4 +1,4 @@
-/* UnifyHash Copyright (C) 1999 Jochen Hoenicke.
+/* UnifyHash 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
@@ -19,8 +19,8 @@
package jode.util;
///#ifdef JDK12
-///import java.lang.ref.WeakReference;
-///import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
///#endif
import @COLLECTIONS@.Comparator;
@@ -40,28 +40,28 @@ public class UnifyHash extends AbstractCollection {
private static final float DEFAULT_LOAD_FACTOR = 0.75F;
///#ifdef JDK12
-/// private ReferenceQueue queue = new ReferenceQueue();
+ private ReferenceQueue queue = new ReferenceQueue();
///#endif
static class Bucket
///#ifdef JDK12
-/// extends WeakReference
+ extends WeakReference
///#endif
{
///#ifdef JDK12
-/// public Bucket(Object o, ReferenceQueue q) {
-/// super(o, q);
-/// }
-///#else
- public Bucket(Object o) {
- this.obj = o;
- }
-
- Object obj;
-
- public Object get() {
- return obj;
+ public Bucket(Object o, ReferenceQueue q) {
+ super(o, q);
}
+///#else
+/// public Bucket(Object o) {
+/// this.obj = o;
+/// }
+///
+/// Object obj;
+///
+/// public Object get() {
+/// return obj;
+/// }
///#endif
int hash;
@@ -107,21 +107,21 @@ public class UnifyHash extends AbstractCollection {
}
///#ifdef JDK12
-/// public final void cleanUp() {
-/// Bucket died;
-/// while ((died = (Bucket)queue.poll()) != null) {
-/// int diedSlot = Math.abs(died.hash % buckets.length);
-/// if (buckets[diedSlot] == died)
-/// buckets[diedSlot] = died.next;
-/// else {
-/// Bucket b = buckets[diedSlot];
-/// while (b.next != died)
-/// b = b.next;
-/// b.next = died.next;
-/// }
-/// size--;
-/// }
-/// }
+ public final void cleanUp() {
+ Bucket died;
+ while ((died = (Bucket)queue.poll()) != null) {
+ int diedSlot = Math.abs(died.hash % buckets.length);
+ if (buckets[diedSlot] == died)
+ buckets[diedSlot] = died.next;
+ else {
+ Bucket b = buckets[diedSlot];
+ while (b.next != died)
+ b = b.next;
+ b.next = died.next;
+ }
+ size--;
+ }
+ }
///#endif
@@ -131,7 +131,7 @@ public class UnifyHash extends AbstractCollection {
public Iterator iterator() {
///#ifdef JDK12
-/// cleanUp();
+ cleanUp();
///#endif
return new Iterator() {
@@ -182,6 +182,9 @@ public class UnifyHash extends AbstractCollection {
}
public Iterator iterateHashCode(final int hash) {
+///#ifdef JDK12
+ cleanUp();
+///#endif
return new Iterator() {
private int known = modCount;
private Bucket nextBucket
@@ -232,9 +235,9 @@ public class UnifyHash extends AbstractCollection {
int slot = Math.abs(hash % buckets.length);
///#ifdef JDK12
-/// Bucket b = new Bucket(o, queue);
+ Bucket b = new Bucket(o, queue);
///#else
- Bucket b = new Bucket(o);
+/// Bucket b = new Bucket(o);
///#endif
b.hash = hash;
b.next = buckets[slot];
@@ -243,7 +246,7 @@ public class UnifyHash extends AbstractCollection {
public Object unify(Object o, int hash, Comparator comparator) {
///#ifdef JDK12
-/// cleanUp();
+ cleanUp();
///#endif
int slot = Math.abs(hash % buckets.length);
for (Bucket b = buckets[slot]; b != null; b = b.next) {
|