[proguard] 01/02: Imported Upstream version 3.4
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Thu Apr 10 09:00:33 UTC 2014
This is an automated email from the git hooks/post-receive script.
ebourg-guest pushed a commit to annotated tag debian/3.4-1
in repository proguard.
commit e24c573b1b744f4ef568b25399b83b69a2552861
Author: Sam Clegg <samo at debian.org>
Date: Thu Apr 10 10:41:53 2014 +0200
Imported Upstream version 3.4
---
docs/FAQ.html | 4 +-
docs/GPL_exception.html | 13 +-
docs/acknowledgements.html | 2 +-
docs/alternatives.html | 18 +-
docs/downloads.html | 21 +-
docs/feedback.html | 2 +-
docs/license.html | 4 +-
docs/manual/ant.html | 12 +-
docs/manual/examples.html | 87 +-
docs/manual/gui.html | 3 +-
docs/manual/introduction.html | 4 +-
docs/manual/limitations.html | 13 +-
docs/manual/retrace/usage.html | 5 +-
docs/manual/sections.html | 138 +-
docs/manual/troubleshooting.html | 15 +
docs/manual/usage.html | 12 +-
docs/quality.html | 9 +-
docs/sections.html | 116 +-
docs/style.css | 145 +-
docs/title.html | 10 +-
lib/proguard.jar | Bin 394065 -> 0 bytes
lib/proguardgui.jar | Bin 123885 -> 0 bytes
lib/retrace.jar | Bin 5110 -> 0 bytes
src/proguard/ClassSpecificationVisitorFactory.java | 4 +-
src/proguard/ConfigurationParser.java | 69 +-
src/proguard/ConfigurationWriter.java | 41 +-
src/proguard/GPL.java | 6 +-
src/proguard/ProGuard.java | 514 +-----
src/proguard/classfile/ClassFile.java | 12 +-
.../classfile/editor/CodeAttrInfoEditor.java | 3 +-
.../classfile/editor/MethodInvocationFixer.java | 6 +-
src/proguard/classfile/editor/VariableEditor.java | 16 +-
.../classfile/instruction/Instruction.java | 30 +-
.../classfile/instruction/InstructionCounter.java | 106 ++
.../ClassFileClassForNameReferenceInitializer.java | 66 +-
.../visitor/ClassCounter.java} | 36 +-
.../classfile/visitor/ClassFileNameFilter.java | 8 +-
.../visitor/MemberCounter.java} | 63 +-
.../gui/ClassMemberSpecificationDialog.java | 56 +-
.../gui/ClassMemberSpecificationsPanel.java | 18 +-
src/proguard/gui/GUIResources.properties | 4 +-
src/proguard/gui/ProGuardGUI.java | 67 +-
src/proguard/gui/boilerplate.pro | 131 +-
src/proguard/gui/default.pro | 131 +-
src/proguard/io/DirectoryPump.java | 7 +-
src/proguard/obfuscate/NameMarker.java | 8 +-
src/proguard/obfuscate/Obfuscator.java | 278 +++
src/proguard/optimize/KeepMarker.java | 8 +-
src/proguard/optimize/NonPrivateMethodMarker.java | 2 +-
src/proguard/optimize/Optimizer.java | 293 ++++
src/proguard/optimize/ParameterShrinker.java | 87 +-
...odPrivatizer.java => WriteOnlyFieldFilter.java} | 43 +-
src/proguard/optimize/WriteOnlyFieldMarker.java | 4 +-
...ialEvaluator.java => EvaluationSimplifier.java} | 1797 +++++++++----------
.../optimize/evaluation/PartialEvaluator.java | 1825 +++-----------------
src/proguard/optimize/evaluation/TracedStack.java | 409 +++--
.../optimize/evaluation/TracedVariables.java | 60 +-
.../evaluation/UnusedParameterCleaner.java | 10 +-
.../optimize/evaluation/value/Category1Value.java | 4 +-
.../optimize/evaluation/value/Category2Value.java | 4 +-
.../optimize/peephole/BranchTargetFinder.java | 196 ++-
.../optimize/peephole/ClassFileFinalizer.java | 43 +-
.../optimize/peephole/GetterSetterInliner.java | 40 +-
.../optimize/peephole/GotoCommonCodeReplacer.java | 232 +++
...toReturnReplacer.java => GotoGotoReplacer.java} | 71 +-
.../optimize/peephole/GotoReturnReplacer.java | 31 +-
.../optimize/peephole/LoadStoreRemover.java | 46 +-
.../optimize/peephole/MethodPrivatizer.java | 35 +-
src/proguard/optimize/peephole/NopRemover.java | 30 +-
src/proguard/optimize/peephole/PushPopRemover.java | 52 +-
.../peephole/SingleImplementationMarker.java | 29 +-
.../optimize/peephole/StoreLoadReplacer.java | 44 +-
src/proguard/shrink/Shrinker.java | 135 ++
73 files changed, 3992 insertions(+), 3851 deletions(-)
diff --git a/docs/FAQ.html b/docs/FAQ.html
index 1947507..2574a68 100644
--- a/docs/FAQ.html
+++ b/docs/FAQ.html
@@ -166,7 +166,9 @@ is ever incorporated, I'll provide a tool to decrypt the strings as well.
No. Control obfuscation injects additional branches into the bytecode, in an
attempt to fool decompilers. This operation may confuse JIT compilers as well,
adversely affecting performance. It may be added as an option in the future,
-but it doesn't have the highest priority.
+but it doesn't have the highest priority. I will focus mostly on the
+optimization step, which may already restructure the code to the point where
+decompilers get confused.
<a name="incremental"> </a>
<h3>Does <b>ProGuard</b> support incremental obfuscation?</h3>
diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html
index dc43dd1..4c53f2a 100644
--- a/docs/GPL_exception.html
+++ b/docs/GPL_exception.html
@@ -32,12 +32,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
<P>
In addition, as a special exception, Eric Lafortune gives permission to link
the code of this program with the following stand-alone applications: Apache
-Ant, the Sun J2ME Wireless Toolkit, the Eclipse IDE, and the Javaground Tools,
-and distribute linked combinations including the two. You must obey the GNU
-General Public License in all respects for all of the code used other than
-these programs. If you modify this file, you may extend this exception to your
-version of the file, but you are not obligated to do so. If you do not wish to
-do so, delete this exception statement from your version.
+Ant, the Eclipse IDE, the Sun NetBeans IDE, the Sun J2ME Wireless Toolkit, and
+the Javaground Tools, and distribute linked combinations including the two.
+You must obey the GNU General Public License in all respects for all of the
+code used other than these programs. If you modify this file, you may extend
+this exception to your version of the file, but you are not obligated to do
+so. If you do not wish to do so, delete this exception statement from your
+version.
</P>
</BODY>
diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html
index a65a440..f89f418 100644
--- a/docs/acknowledgements.html
+++ b/docs/acknowledgements.html
@@ -12,7 +12,7 @@
<b>ProGuard</b> grew out of <b>RetroGuard</b>, which its author Mark Welsh has
kindly made available under the GNU Lesser General Public License.
-<b>RetroGuard</b> is a very nice piece of code, but it only performs
+<b>RetroGuard</b> is a very nice piece of code, but it only performed
obfuscation. I started from the class file parsing code and wrote my own
shrinker, optimizer, and obfuscator. At this point, both programs have little
code in common.
diff --git a/docs/alternatives.html b/docs/alternatives.html
index 8ca2bf9..379b211 100644
--- a/docs/alternatives.html
+++ b/docs/alternatives.html
@@ -132,15 +132,6 @@ incorrect.
</tr>
<tr>
-<td><a target="other" rel="nofollow" href="http://www.retrologic.com/">RetroLogic</a></td>
-<td><a target="other" rel="nofollow" href="http://www.retrologic.com/">RetroGuard</a></td>
-<td align="center"><br></td>
-<td align="center"><br></td>
-<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
-<td>Free (LGPL)</td>
-</tr>
-
-<tr>
<td><a target="other" rel="nofollow" href="http://sourceforge.net/users/glurk/">Thorsten Heit</a></td>
<td><a target="other" rel="nofollow" href="http://sourceforge.net/projects/javaguard/">JavaGuard</a></td>
<td align="center"><br></td>
@@ -213,6 +204,15 @@ incorrect.
</tr>
<tr>
+<td><a target="other" rel="nofollow" href="http://www.retrologic.com/">RetroLogic</a></td>
+<td><a target="other" rel="nofollow" href="http://www.retrologic.com/">RetroGuard</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td>Commercial</td>
+</tr>
+
+<tr>
<td><a target="other" rel="nofollow" href="http://www.codingart.com/">CodingArt</a></td>
<td><a target="other" rel="nofollow" href="http://www.codingart.com/codeshield.html">CodeShield</a></td>
<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
diff --git a/docs/downloads.html b/docs/downloads.html
index 06c1eea..b548248 100644
--- a/docs/downloads.html
+++ b/docs/downloads.html
@@ -34,6 +34,22 @@ updates with sub-minor version numbers. These versions are typically released
shortly after their parent versions, for applying emergency fixes.
<p>
+<h3>Version 3.4</h3>
+<ul>
+<li>Extended optimizations: removing duplicate code within methods.
+<li>Extended regular expressions for class names to comma-separated lists.
+<li>Now automatically keeping arguments of specified methods.
+<li>Added verbose statistics for optimizations.
+<li>Added boilerplate Number optimizations in GUI.
+<li>Fixed <code>Class.forName</code> detection.
+<li>Fixed incremental obfuscation bug.
+<li>Fixed optimization bug causing stack verification errors.
+<li>Fixed optimization bugs related to removal of unused parameters.
+<li>Fixed exception when optimizing code with many local variables.
+<li>Fixed exception when saving configuration with initializers in GUI.
+<li>Updated documentation and examples.
+</ul>
+
<h3>Version 3.3</h3>
<ul>
<li>Extended optimizations: making methods private and static when possible,
@@ -43,7 +59,7 @@ shortly after their parent versions, for applying emergency fixes.
<li>Added <code>-whyareyoukeeping</code> option to get details on why given
classes and class members are being kept.
<li>Added warnings for misplaced class files.
-<li>Improved printing of notes for Class.forName constructs.
+<li>Improved printing of notes for <code>Class.forName</code> constructs.
<li>Implemented '<code>assumenosideeffects</code>' nested element in Ant task.
<li>Improved processing of annotations.
<li>Fixed reading and writing of parameter annotations.
@@ -180,7 +196,8 @@ shortly after their parent versions, for applying emergency fixes.
<li>Added option to obfuscate using lower-case class names only.
<li>Added better option for obfuscating native methods.
<li>Added option not to ignore non-public library classes.
-<li>Added automatic .class detection for classes compiled with Jikes.
+<li>Added automatic <code>.class</code> detection for classes compiled with
+ Jikes.
<li>Updated documentation and examples.
</ul>
diff --git a/docs/feedback.html b/docs/feedback.html
index eddbca6..d8b1a5f 100644
--- a/docs/feedback.html
+++ b/docs/feedback.html
@@ -39,7 +39,7 @@ ahead:
target="other">SourceForge</a>).
<p>
-<li>Likewise, you can submit and consult feature requests on the <a
+<li>Similarly, you can submit and consult feature requests on the <a
href="http://sourceforge.net/tracker/?atid=474707&group_id=54750&func=browse"
target="other">feature request page</a> (at <a
diff --git a/docs/license.html b/docs/license.html
index 917d7f6..141704b 100644
--- a/docs/license.html
+++ b/docs/license.html
@@ -29,8 +29,8 @@ under the GPL. I am granting a <a href="GPL_exception.html">special
exception</a> to the latter clause (in wording suggested by the <a
href="http://www.gnu.org/copyleft/gpl-faq.html#GPLIncompatibleLibs"
target="other">FSF</a>), for combinations with the following stand-alone
-applications: Apache Ant, the Sun J2ME Wireless Toolkit, the Eclipse IDE, and
-the Javaground Tools.
+applications: Apache Ant, the Eclipse IDE, the Sun NetBeans IDE, the Sun J2ME
+Wireless Toolkit, and the Javaground Tools.
<p>
The <b>ProGuard user documentation</b> represents an important part of this
diff --git a/docs/manual/ant.html b/docs/manual/ant.html
index 676ee64..21ea393 100644
--- a/docs/manual/ant.html
+++ b/docs/manual/ant.html
@@ -29,7 +29,7 @@ Please make sure the class path is set correctly for your system.
<p>
There are three ways to configure the ProGuard task: using an external
-configuration file, using embedded ProGuard configuration options, and using
+configuration file, using embedded ProGuard configuration options, or using
the equivalent XML configuration tags. These three ways can be combined,
depending on practical circumstances and personal preference.
<p>
@@ -98,10 +98,12 @@ have to be encoded as <code><</code> and <code>></code>.
<h3>3. XML configuration tags</h3>
If you really prefer a full-blown XML configuration, you can replace the
-ProGuard configuration options by the equivalent XML configuration tags. The
-remainder of this page presents the supported tags. For a more extensive
-discussion of their meaning, please refer to the traditional <a
-href="usage.html">Usage</a> section.
+ProGuard configuration options by XML configuration tags. The resulting
+configuration will be equivalent, but much more verbose and difficult to read,
+as XML goes. The remainder of this page presents the supported tags. For a
+more extensive discussion of their meaning, please refer to the traditional <a
+href="usage.html">Usage</a> section. You can find some sample configuration
+files in the <code>examples/ant</code> directory of the ProGuard distribution.
<p>
<a name="proguard"> </a>
diff --git a/docs/manual/examples.html b/docs/manual/examples.html
index 69c800f..7a57cd1 100644
--- a/docs/manual/examples.html
+++ b/docs/manual/examples.html
@@ -23,7 +23,6 @@ Some typical useful configurations:
<li><a href="#native">Processing native methods</a>
<li><a href="#serializable">Processing serializable classes</a>
<li><a href="#beans">Processing bean classes</a>
-<li><a href="#enumerations">Processing enumeration classes</a>
<li><a href="#annotations">Processing annotations</a>
<li><a href="#rmi">Processing RMI code</a>
<li><a href="#stacktrace">Producing useful obfuscated stack traces</a>
@@ -33,6 +32,9 @@ Some typical useful configurations:
<li><a href="#structure">Printing out the internal structure of class files</a>
</ol>
+You can find some sample configuration files in the <code>examples</code>
+directory of the ProGuard distribution.
+
<a name="application"> </a>
<h3>A typical application</h3>
To shrink, optimize, and obfuscate the ProGuard application itself, one would
@@ -79,10 +81,10 @@ for de-obfuscating any stack traces later on, or for incremental obfuscation of
extensions.
<p>
In general, you might need a few additional directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>. For processing 'simple' applications like ProGuard, that is not
-required.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>. For processing 'simple' applications like
+ProGuard, that is not required.
<a name="applet"> </a>
<h3>A typical applet</h3>
@@ -101,9 +103,9 @@ The typical applet methods will be preserved automatically, since
class in the library <code>rt.jar</code>.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<a name="midlet"> </a>
<h3>A typical midlet</h3>
@@ -131,9 +133,9 @@ The typical midlet methods will be preserved automatically, since
class in the library <code>midpapi20.jar</code>.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<p>
You must preverify your midlets <i>after</i> having processed them, using
J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
@@ -167,9 +169,9 @@ The <code>-printseeds</code> option prints out which classes exactly will
be preserved, so we know for sure we're getting what we want.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<a name="applets"> </a>
<h3>All possible applets in the input jars</h3>
@@ -190,9 +192,9 @@ Again, the <code>-printseeds</code> option prints out which applets exactly
will be preserved.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<a name="midlets"> </a>
<h3>All possible midlets in the input jars</h3>
@@ -217,9 +219,9 @@ And again, the <code>-printseeds</code> option prints out which midlets exactly
will be preserved.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<p>
You must preverify your midlets <i>after</i> having processed them, using
J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
@@ -253,9 +255,9 @@ And again, the <code>-printseeds</code> option prints out which servlets
exactly will be preserved.
<p>
If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+href="#native">native methods</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, or <a
+href="#annotations">annotations</a>.
<a name="library"> </a>
<h3>Processing a library</h3>
@@ -285,10 +287,6 @@ serialization code:
native <methods>;
}
--keepclassmembers class * extends java.lang.Enum {
- public **[] values();
-}
-
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private void writeObject(java.io.ObjectOutputStream);
@@ -296,6 +294,10 @@ serialization code:
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
+
+-keepclassmembers class * extends java.lang.Enum {
+ public **[] values();
+}
</pre>
<p>
This configuration should preserve everything we'll ever want to access in the
@@ -314,10 +316,9 @@ these other obfuscators are ever used for further obfuscation of the library.
<p>
We've added some directives for keeping the "Deprecated" attribute, for <a
href="#stacktrace">useful stack traces</a>, for <a href="#native">native
-methods</a>, for <a href="#enumerations">enumerations</a>, for <a
-href="#serializable">serializable classes</a>, and for <a
-href="#annotations">annotations</a>, which are all discussed in their
-respective examples.
+methods</a>, <a href="#serializable">serializable classes</a>, and for <a
+href="#annotations">annotations</a> (possibly with enumerations), which are
+all discussed in their respective examples.
<p>
The "InnerClasses" attribute (or more precisely, its source name part) has to
be preserved as well, for any inner classes that can be referenced from
@@ -347,21 +348,6 @@ ProGuard doesn't have your native code, so it can't automatically preserve the
classes or class members that are invoked by the native code. These are entry
points, which you'll have to specify explicitly.
-<a name="enumerations"> </a>
-<h3>Processing enumeration classes</h3>
-If your application, applet, servlet, library, etc., contains enumeration
-classes, you'll have to preserve a special method. Enumerations were
-introduced in JDK 5.0. The <code>javac</code> compiler will translate
-enumerations into classes with a special structure. The classes contain
-implementations of a method that the run-time environment accesses by
-introspection (Isn't that just grand? Introspection is the self-modifying code
-of a new generation). You'll therefore have to specify it explicitly:
-<pre>
--keepclassmembers class * extends java.lang.Enum {
- public **[] values();
-}
-</pre>
-
<a name="serializable"> </a>
<h3>Processing serializable classes</h3>
More complex applications, applets, servlets, libraries, etc., may contain
@@ -513,6 +499,10 @@ treats annotation attributes as optional, and removes them in the obfuscation
step. If they are required, you'll have to specify this explicitly:
<pre>
-keepattributes *Annotation*
+
+-keepclassmembers class * extends java.lang.Enum {
+ public static **[] values();
+}
</pre>
<p>
For brevity, we're specifying a wildcarded attribute name, which will match
@@ -524,6 +514,11 @@ For brevity, we're specifying a wildcarded attribute name, which will match
code, you could refine this selection, for instance not keeping the run-time
invisible annotations (which are only used at compile-time).
<p>
+If your annotations contain enumeration classes, you'll have to preserve the
+<code>values()</code> method in these classes. The run-time environment
+accesses this method by introspection (Isn't that just grand? Introspection is
+the self-modifying code of a new generation).
+<p>
Some code may make further use of introspection to figure out the enclosing
methods of anonymous inner classes. In that case, the corresponding attribute
will have to be preserved as well:
diff --git a/docs/manual/gui.html b/docs/manual/gui.html
index ff5e13e..2d918c8 100644
--- a/docs/manual/gui.html
+++ b/docs/manual/gui.html
@@ -10,7 +10,8 @@
<h2>Graphical User Interface</h2>
-To run the ProGuard graphical user interface, just type:
+You can find the ProGuard GUI jar in the <code>lib</code> directory of the
+ProGuard distribution. To run the ProGuard graphical user interface, just type:
<p class="code">
<code><b>java -jar proguardgui.jar</b> [-nosplash] </code>[<i>configuration_file</i>]
</p>
diff --git a/docs/manual/introduction.html b/docs/manual/introduction.html
index 8855676..0a71f9b 100644
--- a/docs/manual/introduction.html
+++ b/docs/manual/introduction.html
@@ -17,8 +17,8 @@ methods. The obfuscation step renames the remaining classes, fields, and
methods using short meaningless names. The resulting jars are smaller and
harder to reverse-engineer.
<p>
-ProGuard can also be used to list unused fields and methods in an application,
-and to print out the internal structure of class files.
+ProGuard can also be used to list unused classes, fields, and methods in an
+application, and to print out the internal structure of class files.
<p>
<table class="diagram" align="center">
diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html
index 3b36cae..2d0f8fd 100644
--- a/docs/manual/limitations.html
+++ b/docs/manual/limitations.html
@@ -34,8 +34,9 @@ easily avoided or resolved:
library classes</b> while reading library jars. If any of them are
extended by public library classes, and then extended again by input
classes, ProGuard will complain it can't find them. In that case, you'll
- have to use the <code>-dontskipnonpubliclibraryclasses</code> option. The
- graphical user interface has a checkbox for this setting.
+ have to use the <code>-dontskipnonpubliclibraryclasses</code> option, and
+ maybe even the <code>-dontskipnonpubliclibraryclassmembers</code> option.
+ The graphical user interface has checkboxes for these settings.
<p>
<li>For best results, ProGuard's optimization algorithms assume that the
@@ -75,6 +76,14 @@ easily avoided or resolved:
marker interface.
<p>
+<li>When obfuscating, ProGuard will write out class files named
+ "<code>a.class</code>", "<code>b.class</code>", etc. If there is a large
+ numbers of classes in the same package, it may also write out
+ <b>"<code>aux.class</code>"</b>. Windows doesn't allow creating files with
+ this reserved name (among a few other names), so it's generally better to
+ write the output to a jar, in order to avoid such problems.
+ <p>
+
<li>ProGuard's obfuscation process currently doesn't follow the <b>naming
rule</b> specifying that internal classes must be named as
<code>ExternalClass$InternalClass</code>, for instance (cfr. <a href=
diff --git a/docs/manual/retrace/usage.html b/docs/manual/retrace/usage.html
index 55bdd6d..c67eba1 100644
--- a/docs/manual/retrace/usage.html
+++ b/docs/manual/retrace/usage.html
@@ -10,13 +10,14 @@
<h2>Usage</h2>
-To run ReTrace, just type:
+You can find the ReTrace jar in the <code>lib</code> directory of the
+ProGuard distribution. To run ReTrace, just type:
<p>
<p class="code">
<code><b>java -jar retrace.jar [-verbose] </b></code><i>mapping_file</i>
[<i>stacktrace_file</i>]
</p>
-where the arguments have the following meaning:
+The arguments have the following meaning:
<p>
<table cellspacing="10">
<tr>
diff --git a/docs/manual/sections.html b/docs/manual/sections.html
index fab98e2..51ae384 100644
--- a/docs/manual/sections.html
+++ b/docs/manual/sections.html
@@ -6,131 +6,55 @@
<meta http-equiv="content-style-type" content="text/css">
<link rel="stylesheet" type="text/css" href="../style.css">
<title>Sections</title>
-
-<script type="text/javascript" language="JavaScript">
-<!--
-function defineSection(aName, aTarget, aURL) {
- document.write("<tr><th class=\"sections\"");
- document.write(document.body != null ?
- (" onMouseOver=\"lightUp(this)\" onMouseOut=\"lightDown(this)\" onClick=\"goTo(this,'"+aTarget+"','"+aURL+"')\" onAfterUpdate=\"lightUp(this)\">"+aName) :
- ("><a target=\""+aTarget+"\" href=\""+aURL+"\">"+aName+"</a>"));
- document.write("</th></tr>");
-}
-
-function lightUp(aTH) {
- if (aTH != fTH)
- aTH.bgColor = "#eeeeee";
-}
-function lightDown(aTH) {
- if (aTH != fTH)
- aTH.bgColor = "";
-}
-function goTo(aTH, aTarget, aURL) {
- if (fTH != null)
- fTH.bgColor = "";
-
- fTH = aTH;
- fTH.bgColor = "white";
- f = findFrameByName(parent.frames, aTarget);
- if (f != null)
- f.document.location.href=aURL;
-}
-function findFrameByName(aFrames, aFrameName) {
- for (i = 0; i < aFrames.length; i++)
- if (aFrames[i].name == aFrameName)
- return aFrames[i];
- return null;
-}
-//-->
-</script>
-
</head>
-<body class="title">
+<body class="navigation">
+
+<ul class="navigation">
+<li><a href="../sections.html"><< Main menu</a></li>
+
+<li class="title">ProGuard Manual</li>
+<li><a target="main" href="introduction.html">Introduction</a></li>
+<li><a target="main" href="usage.html">Usage</a></li>
+<li><a target="main" href="limitations.html">Limitations</a></li>
+<li><a target="main" href="examples.html">Examples</a></li>
+<li><a target="main" href="troubleshooting.html">Troubleshooting</a></li>
+<li><a target="main" href="refcard.html">Ref Card</a></li>
+<li><a target="main" href="gui.html">GUI</a></li>
+<li><a target="main" href="ant.html">Ant Task</a></li>
+<li><a target="main" href="wtk.html">J2ME WTK</a></li>
+
+<li class="title">ReTrace Manual</li>
+<li><a target="main" href="retrace/introduction.html">Introduction</a></li>
+<li><a target="main" href="retrace/usage.html">Usage</a></li>
+<li><a target="main" href="retrace/examples.html">Examples</a></li>
+</ul>
-<script type="text/javascript" language="JavaScript">
-<!--
-var fTH = null;
-//-->
-</script>
+<p>
+<center>
+<small>With support of</small>
+<p>
+
+<a href="http://sourceforge.net/projects/proguard/" target="other">
<script type="text/javascript" language="JavaScript">
<!--
-document.write("<table class=\"sections\"");
-defineSection("<< Main Menu","sections","../sections.html");
-document.write("<tr><th class=\"sections\" bgcolor=\"white\">ProGuard<br>Manual</th></tr>");
-defineSection("Introduction", "main","introduction.html");
-defineSection("Usage", "main","usage.html");
-defineSection("Limitations", "main","limitations.html");
-defineSection("Examples", "main","examples.html");
-defineSection("Troubleshooting","main","troubleshooting.html");
-defineSection("Reference Card", "main","refcard.html");
-defineSection("GUI", "main","gui.html");
-defineSection("Ant Task", "main","ant.html");
-defineSection("J2ME WTK", "main","wtk.html");
-document.write("<tr><th class=\"sections\" bgcolor=\"white\">ReTrace<br>Manual</th></tr>");
-defineSection("Introduction", "main","retrace/introduction.html");
-defineSection("Usage", "main","retrace/usage.html");
-defineSection("Examples", "main","retrace/examples.html");
-document.write("</table>");
-document.write("<p>");
-document.write("<center>");
-document.write("<small>With support of</small>");
-document.write("<p>");
-document.write("<a href=\"http://sourceforge.net/projects/proguard/\" target=\"other\">");
document.write("<img src=\"");
document.write(document.location.hostname == "proguard.sourceforge.net" ?
"http://sourceforge.net/sflogo.php?group_id=54750&type=1" :
"../sflogo.png");
-document.write("\" width=\"88\" height=\"31\" border=\"0\" alt=\"SourceForge\">");
-document.write("</a>");
-document.write("<p>");
-document.write("<a href=\"http://www.luciad.com/\" target=\"other\">");
-document.write("<img src=\"../luciadlogo.png\" width=\"88\" height=\"24\" border=\"0\" alt=\"Luciad\">");
-document.write("</a>");
-document.write("</center>");
+document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
//-->
</script>
-
<noscript>
-<table
- width="120"
- border="1"
- style="border-style:outset"
- cellspacing="0"
- cellpadding="0"
- background="../steel.gif"
- bgcolor="#cccccc">
-
-<tr><th height="40" valign="middle"><a href="../sections.html"><< Main menu</a></th></tr>
-
-<tr><th class="sections" bgcolor="white">ProGuard<br>Manual</th></tr>
-<tr><th class="sections"><a target="main" href="introduction.html">Introduction</a></th></tr>
-<tr><th class="sections"><a target="main" href="usage.html">Usage</a></th></tr>
-<tr><th class="sections"><a target="main" href="limitations.html">Limitations</a></th></tr>
-<tr><th class="sections"><a target="main" href="examples.html">Examples</a></th></tr>
-<tr><th class="sections"><a target="main" href="troubleshooting.html">Troubleshooting</a></th></tr>
-<tr><th class="sections"><a target="main" href="refcard.html">Reference Card</a></th></tr>
-<tr><th class="sections"><a target="main" href="gui.html">GUI</a></th></tr>
-<tr><th class="sections"><a target="main" href="ant.html">Ant Task</a></th></tr>
-<tr><th class="sections"><a target="main" href="wtk.html">J2ME WTK</a></th></tr>
+<img src="../sflogo.png" width="88" height="31" alt="SourceForge">
+</noscript>
-<tr><th class="sections" bgcolor="white">ReTrace<br>Manual</th></tr>
-<tr><th class="sections"><a target="main" href="retrace/introduction.html">Introduction</a></th></tr>
-<tr><th class="sections"><a target="main" href="retrace/usage.html">Usage</a></th></tr>
-<tr><th class="sections"><a target="main" href="retrace/examples.html">Examples</a></th></tr>
+</a>
-</table>
-<p>
-<center>
-<small>With support of</small>
-<p>
-<a href="http://sourceforge.net/projects/proguard/" target="other">
-<img src="../sflogo.png" width="88" height="31" alt="SourceForge"></a>
<p>
<a href="http://www.luciad.com/" target="other">
<img src="../luciadlogo.png" width="88" height="24" alt="Luciad"></a>
</center>
-</noscript>
</body>
</html>
diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html
index 28542c1..634dc84 100644
--- a/docs/manual/troubleshooting.html
+++ b/docs/manual/troubleshooting.html
@@ -138,6 +138,14 @@ Should ProGuard crash while processing your application:
packages and classes. Remember that only classes or interfaces that are
extended or implemented by classes in your input jars are required.</dd>
+<dt><a name="stackoverflowerror"><b>StackOverflowError</b></a></dt>
+
+<dd>You can try increasing the stack size of the Java virtual machine (with
+ the usual <code>-Xss</code> option). This error also seems to occur
+ occasionally when optimizing complex methods on Windows (surprisingly, not
+ on Linux). You can then work around it by using ProGuard's
+ <code>-dontoptimize</code> option.</dd>
+
<dt><a name="otherwise"><b>Otherwise...</b></a></dt>
<dd>Maybe your class files are corrupt. See if recompiling them and trying
@@ -264,6 +272,13 @@ might be several reasons:
<dd>You may have forgotten to sign your program jar <i>after</i> having
processed it with ProGuard.</dd>
+<dt><a name="arraystoreexception"><b>ArrayStoreException: sun.reflect.annotation.EnumConstantNotPresentExceptionProxy</b></a></dt>
+
+<dd>You are probably processing annotations involving enumerations. You should
+ then preserve the <code>values()</code> method of the enumeration type, as
+ shown in the examples.</dd>
+
+
<dt><a name="nosuchfieldormethod"><b>Error: No Such Field or Method</b>,
<b>Error verifying method</b> (in J2ME emulator)</a></dt>
diff --git a/docs/manual/usage.html b/docs/manual/usage.html
index cfa8ecb..09e91ae 100644
--- a/docs/manual/usage.html
+++ b/docs/manual/usage.html
@@ -14,9 +14,10 @@ To run ProGuard, just type:
<p class="code">
<code><b>java -jar proguard.jar </b></code><i>options</i> ...
</p>
-Options can also be put in one or more configuration files. Typically, you'll
-put most options in a configuration file (say, <code>myconfig.pro</code>), and
-just call:
+You can find the ProGuard jar in the <code>lib</code> directory of the
+ProGuard distribution. Options can also be put in one or more configuration
+files. Typically, you'll put most options in a configuration file (say,
+<code>myconfig.pro</code>), and just call:
<p class="code">
<code><b>java -jar proguard.jar @myconfig.pro</b></code>
</p>
@@ -780,6 +781,11 @@ files.
</table>
+ For additional flexibility, class names can actually be comma-separated
+ lists of class names, with optional <code><b>!</b></code> negators, just
+ like file name filters. This notation doesn't look very Java-like, so it
+ should be used with moderation.
+ <p>
For convenience and for backward compatibility, the class name
<code><b>*</b></code> refers to any class, irrespective of its package.
<p>
diff --git a/docs/quality.html b/docs/quality.html
index 1214b26..2ba1dd6 100644
--- a/docs/quality.html
+++ b/docs/quality.html
@@ -18,7 +18,8 @@ documentation, spell checks, compilation results, an output jar, dead code
analysis, a shrunk and obfuscated jar (using ProGuard itself!), test runs with
memory and performance analyses, etc. Most analyses are produced using freely
available tools. The results are poured into a convenient set of web pages
-using bash/sed/awk scripts. You're welcome to have a look at them:
+using bash/sed/awk scripts. You're welcome to have a look at an uploaded
+snapshot of one of these runs:
<p>
<center><a href="http://proguard.sourceforge.net/quality/"
target="other">Automated Code Analysis and Testing Pages</a> (at <a
@@ -27,6 +28,12 @@ target="other">SourceForge</a>)</center>
<p>
The pages will appear in a new window, which you probably want to view at
full-screen size.
+<p>
+
+In addition, <b>ProGuard</b> is tested against a constantly growing test suite
+(over 160 tests at this time of writing). These small programs contain a wide
+range of common and uncommon constructs, in order to detect any problems as
+soon as possible.
<hr>
<address>
diff --git a/docs/sections.html b/docs/sections.html
index 39a1866..3227667 100644
--- a/docs/sections.html
+++ b/docs/sections.html
@@ -6,116 +6,50 @@
<meta http-equiv="content-style-type" content="text/css">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Sections</title>
-
-<script type="text/javascript" language="JavaScript">
-<!--
-function defineSection(aName, aTarget, aURL) {
- document.write("<tr><th class=\"sections\"");
- document.write(document.body != null ?
- (" onMouseOver=\"lightUp(this)\" onMouseOut=\"lightDown(this)\" onClick=\"goTo(this,'"+aTarget+"','"+aURL+"')\" onAfterUpdate=\"lightUp(this)\">"+aName) :
- ("><a target=\""+aTarget+"\" href=\""+aURL+"\">"+aName+"</a>"));
- document.write("</th></tr>");
-}
-
-function lightUp(aTH) {
- if (aTH != fTH)
- aTH.bgColor = "#eeeeee";
-}
-function lightDown(aTH) {
- if (aTH != fTH)
- aTH.bgColor = "";
-}
-function goTo(aTH, aTarget, aURL) {
- if (fTH != null)
- fTH.bgColor = "";
-
- fTH = aTH;
- fTH.bgColor = "white";
- f = findFrameByName(parent.frames, aTarget);
- if (f != null)
- f.document.location.href=aURL;
-}
-function findFrameByName(aFrames, aFrameName) {
- for (i = 0; i < aFrames.length; i++)
- if (aFrames[i].name == aFrameName)
- return aFrames[i];
- return null;
-}
-//-->
-</script>
-
</head>
-<body class="title">
+<body class="navigation">
+
+<ul class="navigation">
+<li><a target="main" href="main.html">Main</a></li>
+<li><a target="main" href="results.html">Results</a></li>
+<li><a target="main" href="FAQ.html">FAQ</a></li>
+<li><a href="manual/sections.html">Manual</a></li>
+<li><a target="main" href="quality.html">Quality</a></li>
+<li><a target="main" href="screenshots.html">Screenshots</a></li>
+<li><a target="main" href="testimonials.html">Testimonials</a></li>
+<li><a target="main" href="license.html">License</a></li>
+<li><a target="main" href="downloads.html">Downloads</a></li>
+<li><a target="main" href="feedback.html">Feedback</a></li>
+<li><a target="main" href="acknowledgements.html">Ack'ments</a></li>
+<li><a target="main" href="alternatives.html">Alternatives</a></li>
+</ul>
-<script type="text/javascript" language="JavaScript">
-<!--
-var fTH = null;
-//-->
-</script>
+<p>
+<center>
+<small>With support of</small>
+<p>
+
+<a href="http://sourceforge.net/projects/proguard/" target="other">
<script type="text/javascript" language="JavaScript">
<!--
-document.write("<table class=\"sections\" width=\"120\">");
-defineSection("Main", "main", "main.html");
-defineSection("Results", "main", "results.html");
-defineSection("FAQ", "main", "FAQ.html");
-defineSection("Manual", "sections","manual/sections.html");
-defineSection("Quality", "main", "quality.html");
-defineSection("Screenshots", "main", "screenshots.html");
-defineSection("Testimonials","main", "testimonials.html");
-defineSection("License", "main", "license.html");
-defineSection("Downloads", "main", "downloads.html");
-defineSection("Feedback", "main", "feedback.html");
-defineSection("Ack'ments", "main", "acknowledgements.html");
-defineSection("Alternatives","main", "alternatives.html");
-document.write("</table>");
-document.write("<p>");
-document.write("<center>");
-document.write("<small>With support of</small>");
-document.write("<p>");
-document.write("<a href=\"http://sourceforge.net/projects/proguard/\" target=\"other\">");
document.write("<img src=\"");
document.write(document.location.hostname == "proguard.sourceforge.net" ?
"http://sourceforge.net/sflogo.php?group_id=54750&type=1" :
"sflogo.png");
document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
-document.write("</a>");
-document.write("<p>");
-document.write("<a href=\"http://www.luciad.com/\" target=\"other\">");
-document.write("<img src=\"luciadlogo.png\" width=\"88\" height=\"24\" alt=\"Luciad\">");
-document.write("</a>");
-document.write("</center>");
//-->
</script>
-
<noscript>
-<table class="sections">
+<img src="sflogo.png" width="88" height="31" alt="SourceForge">
+</noscript>
-<tr><th class="sections"><a target="main" href="main.html">Main</a></th></tr>
-<tr><th class="sections"><a target="main" href="results.html">Results</a></th></tr>
-<tr><th class="sections"><a target="main" href="FAQ.html">FAQ</a></th></tr>
-<tr><th class="sections"><a href="manual/sections.html">Manual</a></th></tr>
-<tr><th class="sections"><a target="main" href="quality.html">Quality</a></th></tr>
-<tr><th class="sections"><a target="main" href="screenshots.html">Screenshots</a></th></tr>
-<tr><th class="sections"><a target="main" href="testimonials.html">Testimonials</a></th></tr>
-<tr><th class="sections"><a target="main" href="license.html">License</a></th></tr>
-<tr><th class="sections"><a target="main" href="downloads.html">Downloads</a></th></tr>
-<tr><th class="sections"><a target="main" href="feedback.html">Feedback</a></th></tr>
-<tr><th class="sections"><a target="main" href="acknowledgements.html">Ack'ments</a></th></tr>
-<tr><th class="sections"><a target="main" href="alternatives.html">Alternatives</a></th></tr>
+</a>
-</table>
-<p>
-<center>
-<small>With support of</small>
-<p>
-<a href="http://sourceforge.net/projects/proguard/" target="other">
-<img src="sflogo.png" width="88" height="31" alt="SourceForge"></a>
<p>
<a href="http://www.luciad.com/" target="other">
<img src="luciadlogo.png" width="88" height="24" alt="Luciad"></a>
</center>
-</noscript>
</body>
</html>
diff --git a/docs/style.css b/docs/style.css
index b763813..40a12d0 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -3,96 +3,157 @@
/* Global settings. */
-body {
+body
+{
background: #FFFFFF;
}
-h1 {
+h1
+{
text-align: center;
}
-h2 {
+h2
+{
text-align: center;
}
-h3 {
+h3
+{
background: #EEEEFF;
padding: 10px;
}
-table {
+table
+{
width: 100%;
}
-th {
+th
+{
padding: 4px;
}
-td {
+td
+{
background: #EEEEFF;
padding: 8px;
}
-img {
+img
+{
border: none;
}
/* Settings for the introductory paragraph. */
-p.intro {
+p.intro
+{
background: #EEEEFF;
padding: 10px;
border: solid #000000 1px
}
-/* Settings for the title and section frames. */
+/* Settings for the title frame. */
-body.title {
+body.title
+{
margin: 0px;
- background: #FFFFFF;
+ padding: 0px;
+ background: #C0C0C0;
}
-table.title {
- width: 100%;
- height: 50px;
- background: url("steel.gif");
- border: outset #FFFFFF 1px;
- border-spacing: 0px;
+div.title
+{
+ height: 48px;
+ margin: 0px;
+ padding: 0px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #FFFFFF #808080 #808080 #FFFFFF;
+ background: url("steel.gif");
}
-th.title, td.title {
- background: transparent;
- border: none;
- border-spacing: 0px;
+div.title h1
+{
+ margin: 0px;
+ padding: 0px;
+ padding-top: 8px;
+ padding-left: 40%;
+ float: left;
}
-.title a:link { color: #000000; text-decoration: none; }
-.title a:visited { color: #000000; text-decoration: none; }
-.title a:hover { color: #222222; text-decoration: none; }
-.title a:active { color: #FFFFFF; text-decoration: none; }
+div.title div
+{
+ margin: 0px;
+ padding: 0px;
+ padding-top: 12px;
+ padding-right: 20px;
+ float: right;
+}
-table.sections {
- width: 100%;
- height: 50px;
+/* Settings for the section frames. */
+
+body.navigation
+{
+ margin: 0px;
+ padding: 0px;
+}
+
+ul.navigation
+{
+ margin: 0px;
+ padding: 0px;
+ list-style: none;
+ text-align: center;
background: url("steel.gif");
- border: 0px;
- border-spacing: 0px;
}
-th.sections, td.sections {
- height: 40px;
- border: outset #FFFFFF 1px;
+ul.navigation li
+{
+ margin: 0px;
+ padding: 0px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #FFFFFF #808080 #808080 #FFFFFF;
+ color: #000000;
+ font-weight: bold;
+}
+
+ul.navigation li.title
+{
+ margin: 0px;
+ padding: 4px 10px;
+ background: #E0E0E0;
+}
+
+ul.navigation li a
+{
+ margin: 0px;
+ padding: 6px 0px;
+ background: transparent;
+ color: #000000;
+ text-decoration: none;
+ display: block;
+}
+
+ul.navigation li a:hover,
+ul.navigation li a:focus
+{
+ background: #FFFFFF;
}
/* Settings for the yellow note tables. */
-table.note {
+table.note
+{
width: 408px;
border: none;
border-spacing: 0px;
}
-td.shadow8 {
+td.shadow8
+{
width: 8px;
padding: 0px;
margin: 0px;
@@ -100,7 +161,8 @@ td.shadow8 {
background: transparent;
}
-td.shadow400 {
+td.shadow400
+{
width: 400px;
padding: 0px;
margin: 0px;
@@ -108,20 +170,23 @@ td.shadow400 {
background: transparent;
}
-td.note {
+td.note
+{
width: 380px;
background: #FFFFC0;
padding: 0px;
margin: 0px;
}
-p.note {
+p.note
+{
padding: 0px;
margin: 0px 10px;
text-align: center;
}
-p.author {
+p.author
+{
padding: 0px;
margin: 0px 10px;
text-align: right;
diff --git a/docs/title.html b/docs/title.html
index 6087887..9e5751a 100644
--- a/docs/title.html
+++ b/docs/title.html
@@ -8,12 +8,10 @@
</head>
<body class="title">
-<table class="title">
-<tr>
-<th class="title"><h1><img src="title.gif" width="154" height="29" alt="ProGuard"></h1></th>
-<td class="title" width="100">Version 3.3.2</td>
-</tr>
-</table>
+<div class="title">
+<h1><img src="title.gif" width="154" height="29" alt="ProGuard"></h1>
+<div>Version 3.4</div>
+</div>
</body>
</html>
diff --git a/lib/proguard.jar b/lib/proguard.jar
deleted file mode 100644
index e48c6f7..0000000
Binary files a/lib/proguard.jar and /dev/null differ
diff --git a/lib/proguardgui.jar b/lib/proguardgui.jar
deleted file mode 100644
index b77d428..0000000
Binary files a/lib/proguardgui.jar and /dev/null differ
diff --git a/lib/retrace.jar b/lib/retrace.jar
deleted file mode 100644
index 1dca452..0000000
Binary files a/lib/retrace.jar and /dev/null differ
diff --git a/src/proguard/ClassSpecificationVisitorFactory.java b/src/proguard/ClassSpecificationVisitorFactory.java
index 5094a99..0b9e4b7 100644
--- a/src/proguard/ClassSpecificationVisitorFactory.java
+++ b/src/proguard/ClassSpecificationVisitorFactory.java
@@ -1,4 +1,4 @@
-/* $Id: ClassSpecificationVisitorFactory.java,v 1.5 2004/12/11 16:35:23 eric Exp $
+/* $Id: ClassSpecificationVisitorFactory.java,v 1.6 2005/08/21 20:25:33 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -31,7 +31,7 @@ import java.util.*;
*
* @author Eric Lafortune
*/
-class ClassSpecificationVisitorFactory
+public class ClassSpecificationVisitorFactory
{
/**
* Creates a new ClassPoolVisitor to efficiently travel to the specified
diff --git a/src/proguard/ConfigurationParser.java b/src/proguard/ConfigurationParser.java
index 523b40a..1584fc1 100644
--- a/src/proguard/ConfigurationParser.java
+++ b/src/proguard/ConfigurationParser.java
@@ -1,4 +1,4 @@
-/* $Id: ConfigurationParser.java,v 1.24 2005/06/11 14:50:16 eric Exp $
+/* $Id: ConfigurationParser.java,v 1.25 2005/08/21 20:25:33 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -220,7 +220,9 @@ public class ConfigurationParser
do
{
// Read the filter.
- filters[counter++] = ListUtil.commaSeparatedString(parseCommaSeparatedList(false, true));
+ filters[counter++] =
+ ListUtil.commaSeparatedString(
+ parseCommaSeparatedList("filter", true, false, true));
}
while (counter < filters.length &&
ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord));
@@ -465,18 +467,18 @@ public class ConfigurationParser
}
}
- readNextWord("class name or interface name");
- checkJavaIdentifier("class name or interface name");
+ // Parse the class name part.
+ String externalClassName =
+ ListUtil.commaSeparatedString(
+ parseCommaSeparatedList("class name or interface name",
+ false, true, false));
- // Parse the class name part. For backward compatibility, allow a
- // single "*" wildcard to match any class.
- String externalClassName = nextWord;
+ // For backward compatibility, allow a single "*" wildcard to match any
+ // class.
String className = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalClassName) ?
null :
ClassUtil.internalClassName(externalClassName);
- readNextWord();
-
String extendsClassName = null;
if (!configurationEnd())
@@ -485,11 +487,11 @@ public class ConfigurationParser
if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) ||
ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord))
{
- readNextWord("class name or interface name");
- checkJavaIdentifier("class name or interface name");
- extendsClassName = ClassUtil.internalClassName(nextWord);
-
- readNextWord();
+ extendsClassName =
+ ClassUtil.internalClassName(
+ ListUtil.commaSeparatedString(
+ parseCommaSeparatedList("class name or interface name",
+ false, true, false)));
}
}
@@ -649,7 +651,7 @@ public class ConfigurationParser
else
{
// Make sure we have a proper type.
- checkJavaIdentifier("class member type");
+ checkJavaIdentifier("java type");
String type = nextWord;
readNextWord("class member name");
@@ -711,7 +713,7 @@ public class ConfigurationParser
// Parse the method arguments.
String descriptor =
ClassUtil.internalMethodDescriptor(type,
- parseCommaSeparatedList(true, false));
+ parseCommaSeparatedList("argument", true, true, false));
if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord))
{
@@ -750,10 +752,13 @@ public class ConfigurationParser
/**
- * Reads a comma-separated list of java identifiers or of file names,
- * optionally ending after the closing parenthesis.
+ * Reads a comma-separated list of java identifiers or of file names. If an
+ * empty list is allowed, the reading will end after a closing parenthesis
+ * or semi-colon.
*/
- private List parseCommaSeparatedList(boolean checkJavaIdentifiers,
+ private List parseCommaSeparatedList(String expectedDescription,
+ boolean allowEmptyList,
+ boolean checkJavaIdentifiers,
boolean replaceSystemProperties)
throws ParseException, IOException
{
@@ -762,9 +767,10 @@ public class ConfigurationParser
while (true)
{
// Read an argument.
- readNextWord("argument");
+ readNextWord(expectedDescription);
- if (arguments.size() == 0 &&
+ if (allowEmptyList &&
+ arguments.size() == 0 &&
(ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord) ||
ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)))
{
@@ -773,7 +779,7 @@ public class ConfigurationParser
if (checkJavaIdentifiers)
{
- checkJavaIdentifier("argument type");
+ checkJavaIdentifier("java type");
}
if (replaceSystemProperties)
@@ -783,11 +789,19 @@ public class ConfigurationParser
arguments.add(nextWord);
- // Read a comma (or a closing parenthesis, or a different word).
- readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
- "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD +
- "'");
-
+ if (allowEmptyList)
+ {
+ // Read a comma (or a closing parenthesis, or a different word).
+ readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+ "' or closing '" + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD +
+ "'");
+ }
+ else
+ {
+ // Read a comma (or a different word).
+ readNextWord();
+ }
+
if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord))
{
break;
@@ -940,6 +954,7 @@ public class ConfigurationParser
c == ']' ||
c == '<' ||
c == '>' ||
+ c == '!' ||
c == '*' ||
c == '?' ||
c == '%'))
diff --git a/src/proguard/ConfigurationWriter.java b/src/proguard/ConfigurationWriter.java
index f3f581c..168ed39 100644
--- a/src/proguard/ConfigurationWriter.java
+++ b/src/proguard/ConfigurationWriter.java
@@ -1,4 +1,4 @@
-/* $Id: ConfigurationWriter.java,v 1.17 2005/06/25 22:09:48 eric Exp $
+/* $Id: ConfigurationWriter.java,v 1.18 2005/08/21 19:28:48 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -307,7 +307,7 @@ public class ConfigurationWriter
// Write out the class name.
writer.print(classSpecification.className != null ?
ClassUtil.externalClassName(classSpecification.className) :
- "*");
+ ConfigurationConstants.ANY_CLASS_KEYWORD);
// Write out the extends template, if any.
if (classSpecification.extendsClassName != null)
@@ -322,8 +322,7 @@ public class ConfigurationWriter
writer.println(" {");
writeFieldSpecification( classSpecification.fieldSpecifications);
- writeMethodSpecification(classSpecification.methodSpecifications,
- classSpecification.className);
+ writeMethodSpecification(classSpecification.methodSpecifications);
writer.println("}");
}
else
@@ -374,11 +373,18 @@ public class ConfigurationWriter
writer.print(ClassUtil.externalFieldAccessFlags(classMemberSpecification.requiredSetAccessFlags));
// Write out the field name and descriptor.
- writer.print(classMemberSpecification.name != null ||
- classMemberSpecification.descriptor != null ?
+ String name = classMemberSpecification.name;
+ String descriptor = classMemberSpecification.descriptor;
+
+ if (name == null)
+ {
+ name = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD;
+ }
+
+ writer.print(descriptor != null ?
ClassUtil.externalFullFieldDescription(0,
- classMemberSpecification.name,
- classMemberSpecification.descriptor) :
+ name,
+ descriptor) :
ConfigurationConstants.ANY_FIELD_KEYWORD);
writer.println(";");
@@ -387,7 +393,7 @@ public class ConfigurationWriter
}
- private void writeMethodSpecification(List classMemberSpecifications, String className)
+ private void writeMethodSpecification(List classMemberSpecifications)
{
if (classMemberSpecifications != null)
{
@@ -405,12 +411,19 @@ public class ConfigurationWriter
writer.print(ClassUtil.externalMethodAccessFlags(classMemberSpecification.requiredSetAccessFlags));
// Write out the method name and descriptor.
- writer.print(classMemberSpecification.name != null ||
- classMemberSpecification.descriptor != null ?
- ClassUtil.externalFullMethodDescription(className,
+ String name = classMemberSpecification.name;
+ String descriptor = classMemberSpecification.descriptor;
+
+ if (name == null)
+ {
+ name = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD;
+ }
+
+ writer.print(descriptor != null ?
+ ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT,
0,
- classMemberSpecification.name,
- classMemberSpecification.descriptor) :
+ name,
+ descriptor) :
ConfigurationConstants.ANY_METHOD_KEYWORD);
writer.println(";");
diff --git a/src/proguard/GPL.java b/src/proguard/GPL.java
index 007a42a..e8dd9ff 100644
--- a/src/proguard/GPL.java
+++ b/src/proguard/GPL.java
@@ -1,4 +1,4 @@
-/* $Id: GPL.java,v 1.3 2005/06/25 22:04:40 eric Exp $
+/* $Id: GPL.java,v 1.5 2005/09/11 22:14:13 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -162,8 +162,10 @@ public class GPL
packageName.startsWith("proguard") ||
packageName.startsWith("org.apache.tools.ant") ||
packageName.startsWith("org.eclipse") ||
+ packageName.startsWith("org.netbeans") ||
packageName.startsWith("com.sun.kvem") ||
- packageName.startsWith("jg.j2me.builder");
+ packageName.startsWith("jg.j2me") ||
+ packageName.startsWith("jg.common");
}
diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java
index 5fec9d9..e874887 100644
--- a/src/proguard/ProGuard.java
+++ b/src/proguard/ProGuard.java
@@ -1,4 +1,4 @@
-/* $Id: ProGuard.java,v 1.97 2005/06/26 16:20:23 eric Exp $
+/* $Id: ProGuard.java,v 1.101 2005/10/22 11:53:39 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -21,24 +21,21 @@
*/
package proguard;
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.editor.*;
-import proguard.classfile.instruction.*;
+import proguard.classfile.ClassPool;
+import proguard.classfile.attribute.AllAttrInfoVisitor;
+import proguard.classfile.editor.ConstantPoolSorter;
+import proguard.classfile.instruction.AllInstructionVisitor;
import proguard.classfile.util.*;
import proguard.classfile.visitor.*;
import proguard.io.*;
-import proguard.obfuscate.*;
-import proguard.optimize.*;
-import proguard.optimize.evaluation.PartialEvaluator;
-import proguard.optimize.peephole.*;
-import proguard.shrink.*;
-import proguard.util.*;
+import proguard.obfuscate.Obfuscator;
+import proguard.optimize.Optimizer;
+import proguard.shrink.Shrinker;
+import proguard.util.ClassNameListMatcher;
import java.io.*;
import java.util.*;
-
/**
* Tool for shrinking, optimizing, and obfuscating Java class files.
*
@@ -46,7 +43,7 @@ import java.util.*;
*/
public class ProGuard
{
- public static final String VERSION = "ProGuard, version 3.3.2";
+ public static final String VERSION = "ProGuard, version 3.4";
private Configuration configuration;
private ClassPool programClassPool = new ClassPool();
@@ -74,6 +71,12 @@ public class ProGuard
readInput();
+ // The defaultPackage option implies the allowAccessModification option.
+ if (configuration.defaultPackage != null)
+ {
+ configuration.allowAccessModification = true;
+ }
+
if (configuration.shrink ||
configuration.optimize ||
configuration.obfuscate)
@@ -164,12 +167,6 @@ public class ProGuard
configuration.libraryJars,
createDataEntryClassPoolFiller(true));
}
-
- // The defaultPackage option implies the allowAccessModification option.
- if (configuration.defaultPackage != null)
- {
- configuration.allowAccessModification = true;
- }
}
@@ -369,7 +366,7 @@ public class ProGuard
// Discard unused library classes.
if (configuration.verbose)
{
- System.out.println("Removing unused library classes...");
+ System.out.println("Removed unused library classes...");
System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize);
System.out.println(" Final number of library classes: " + libraryClassPool.size());
}
@@ -415,6 +412,7 @@ public class ProGuard
return null;
}
+
/**
* Prints out classes and class members that are used as seeds in the
* shrinking and obfuscation steps.
@@ -463,110 +461,47 @@ public class ProGuard
if (configuration.verbose)
{
System.out.println("Shrinking...");
- }
- // Check if we have at least some keep commands.
- if (configuration.keep == null)
- {
- throw new IOException("You have to specify '-keep' options for the shrinking step.");
- }
-
- int originalProgramClassPoolSize = programClassPool.size();
-
- // Clean up any old visitor info.
- programClassPool.classFilesAccept(new ClassFileCleaner());
- libraryClassPool.classFilesAccept(new ClassFileCleaner());
-
- // Create a visitor for marking the seeds.
- UsageMarker usageMarker = configuration.whyAreYouKeeping == null ?
- new UsageMarker() :
- new ShortestUsageMarker();
-
- ClassPoolVisitor classPoolvisitor =
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
- usageMarker,
- usageMarker);
- // Mark the seeds.
- programClassPool.accept(classPoolvisitor);
- libraryClassPool.accept(classPoolvisitor);
-
- // Mark interfaces that have to be kept.
- programClassPool.classFilesAccept(new InterfaceUsageMarker(usageMarker));
-
- // Mark the inner class information that has to be kept.
- programClassPool.classFilesAccept(new InnerUsageMarker(usageMarker));
-
- if (configuration.whyAreYouKeeping != null)
- {
- if (configuration.verbose)
+ // We'll print out some explanation, if requested.
+ if (configuration.whyAreYouKeeping != null)
{
System.out.println("Explaining why classes and class members are being kept...");
}
- System.out.println();
-
- // Create a visitor for explaining classes and class members.
- ShortestUsagePrinter shortestUsagePrinter =
- new ShortestUsagePrinter((ShortestUsageMarker)usageMarker,
- configuration.verbose);
-
- ClassPoolVisitor whyClassPoolvisitor =
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping,
- shortestUsagePrinter,
- shortestUsagePrinter);
-
- // Mark the seeds.
- programClassPool.accept(whyClassPoolvisitor);
- libraryClassPool.accept(whyClassPoolvisitor);
- }
-
- if (configuration.printUsage != null)
- {
- if (configuration.verbose)
+ // We'll print out the usage, if requested.
+ if (configuration.printUsage != null)
{
System.out.println("Printing usage" +
(isFile(configuration.printUsage) ?
" to [" + configuration.printUsage.getAbsolutePath() + "]" :
"..."));
}
-
- PrintStream ps = isFile(configuration.printUsage) ?
- new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printUsage))) :
- System.out;
-
- // Print out items that will be removed.
- programClassPool.classFilesAcceptAlphabetically(
- new UsagePrinter(usageMarker, true, ps));
-
- if (ps != System.out)
- {
- ps.close();
- }
}
- // Discard unused program classes.
- ClassPool newProgramClassPool = new ClassPool();
- programClassPool.classFilesAccept(
- new UsedClassFileFilter(usageMarker,
- new MultiClassFileVisitor(
- new ClassFileVisitor[] {
- new ClassFileShrinker(usageMarker, 1024),
- new ClassPoolFiller(newProgramClassPool, false)
- })));
- programClassPool = newProgramClassPool;
-
- if (configuration.verbose)
+ // Check if we have at least some keep commands.
+ if (configuration.keep == null)
{
- System.out.println("Removing unused program classes and class elements...");
- System.out.println(" Original number of program classes: " + originalProgramClassPoolSize);
- System.out.println(" Final number of program classes: " + programClassPool.size());
+ throw new IOException("You have to specify '-keep' options for the shrinking step.");
}
+ int originalProgramClassPoolSize = programClassPool.size();
+
+ // Perform the actual shrinking.
+ programClassPool = new Shrinker(configuration).execute(programClassPool, libraryClassPool);
+
// Check if we have at least some output class files.
- if (programClassPool.size() == 0)
+ int newProgramClassPoolSize = programClassPool.size();
+ if (newProgramClassPoolSize == 0)
{
throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
}
+
+ if (configuration.verbose)
+ {
+ System.out.println("Removed unused program classes and class elements...");
+ System.out.println(" Original number of program classes: " + originalProgramClassPoolSize);
+ System.out.println(" Final number of program classes: " + newProgramClassPoolSize);
+ }
}
@@ -580,14 +515,6 @@ public class ProGuard
System.out.println("Optimizing...");
}
- // Clean up any old visitor info.
- programClassPool.classFilesAccept(new ClassFileCleaner());
- libraryClassPool.classFilesAccept(new ClassFileCleaner());
-
- // Link all methods that should get the same optimization info.
- programClassPool.classFilesAccept(new BottomClassFileFilter(
- new MethodInfoLinker()));
-
// Check if we have at least some keep commands.
if (configuration.keep == null &&
configuration.keepNames == null &&
@@ -597,138 +524,8 @@ public class ProGuard
throw new IOException("You have to specify '-keep' options for the optimization step.");
}
- // Create a visitor for marking the seeds.
- KeepMarker keepMarker = new KeepMarker();
- ClassPoolVisitor classPoolvisitor =
- new MultiClassPoolVisitor(new ClassPoolVisitor[]
- {
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
- keepMarker,
- keepMarker),
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
- keepMarker,
- keepMarker)
- });
-
- // Mark the seeds.
- programClassPool.accept(classPoolvisitor);
- libraryClassPool.accept(classPoolvisitor);
-
- // Attach some optimization info to all methods, so it can be filled
- // out later.
- programClassPool.classFilesAccept(new AllMethodVisitor(
- new MethodOptimizationInfoSetter()));
- libraryClassPool.classFilesAccept(new AllMethodVisitor(
- new MethodOptimizationInfoSetter()));
-
- // Mark all interfaces that have single implementations.
- programClassPool.classFilesAccept(new SingleImplementationMarker(configuration.allowAccessModification));
-
- // Make class files and methods final, as far as possible.
- programClassPool.classFilesAccept(new ClassFileFinalizer());
-
- // Mark all fields that are write-only, and mark the used local variables.
- programClassPool.classFilesAccept(new AllMethodVisitor(
- new AllAttrInfoVisitor(
- new AllInstructionVisitor(
- new MultiInstructionVisitor(
- new InstructionVisitor[]
- {
- new WriteOnlyFieldMarker(),
- new VariableUsageMarker(),
- })))));
-
- // Mark all methods that can not be made private.
- programClassPool.classFilesAccept(new NonPrivateMethodMarker());
- libraryClassPool.classFilesAccept(new NonPrivateMethodMarker());
-
- // Make all non-private and unmarked methods in final classes private.
- programClassPool.classFilesAccept(new ClassFileAccessFilter(ClassConstants.INTERNAL_ACC_FINAL, 0,
- new AllMethodVisitor(
- new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
- new MethodPrivatizer()))));
-
- // Mark all used parameters, including the 'this' parameters.
- programClassPool.classFilesAccept(new AllMethodVisitor(
- new ParameterUsageMarker()));
- libraryClassPool.classFilesAccept(new AllMethodVisitor(
- new ParameterUsageMarker()));
-
- if (configuration.assumeNoSideEffects != null)
- {
- // Create a visitor for marking methods that don't have any side effects.
- NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
- ClassPoolVisitor noClassPoolvisitor =
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects,
- null,
- noSideEffectMethodMarker);
-
- // Mark the seeds.
- programClassPool.accept(noClassPoolvisitor);
- libraryClassPool.accept(noClassPoolvisitor);
- }
-
- // Mark all methods that have side effects.
- programClassPool.accept(new SideEffectMethodMarker());
-
- // Perform partial evaluation.
- programClassPool.classFilesAccept(new AllMethodVisitor(
- new PartialEvaluator()));
-
- // Inline interfaces with single implementations.
- programClassPool.classFilesAccept(new SingleImplementationInliner());
-
- // Restore the interface references from these single implementations.
- programClassPool.classFilesAccept(new SingleImplementationFixer());
-
- // Shrink the method parameters and make methods static.
- programClassPool.classFilesAccept(new AllMethodVisitor(
- new ParameterShrinker(1024, 64)));
-
- // Fix all references to class files.
- programClassPool.classFilesAccept(new ClassFileReferenceFixer());
-
- // Fix all references to class members.
- programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
-
- // Create a branch target marker and a code attribute editor that can
- // be reused for all code attributes.
- BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
- CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(1024);
-
- // Visit all code attributes.
- // First let the branch marker mark all branch targets.
- // Then perform peephole optimisations on the instructions:
- // - Remove push/pop instruction pairs.
- // - Remove load/store instruction pairs.
- // - Replace store/load instruction pairs by dup/store instructions.
- // - Replace branches to return instructions by return instructions.
- // - Remove nop instructions.
- // - Fix invocations of methods that have become private, static,...
- // - Inline simple getters and setters.
- // Finally apply all changes to the code.
- programClassPool.classFilesAccept(
- new AllMethodVisitor(
- new AllAttrInfoVisitor(
- new MultiAttrInfoVisitor(
- new AttrInfoVisitor[]
- {
- branchTargetFinder,
- new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
- new AllInstructionVisitor(
- new MultiInstructionVisitor(
- new InstructionVisitor[]
- {
- new PushPopRemover(branchTargetFinder, codeAttrInfoEditor),
- new LoadStoreRemover(branchTargetFinder, codeAttrInfoEditor),
- new StoreLoadReplacer(branchTargetFinder, codeAttrInfoEditor),
- new GotoReturnReplacer(codeAttrInfoEditor),
- new NopRemover(codeAttrInfoEditor),
- new MethodInvocationFixer(codeAttrInfoEditor),
- new GetterSetterInliner(codeAttrInfoEditor, configuration.allowAccessModification),
- })),
- codeAttrInfoEditor
- }))));
+ // Perform the actual optimization.
+ new Optimizer(configuration).execute(programClassPool, libraryClassPool);
}
@@ -740,235 +537,25 @@ public class ProGuard
if (configuration.verbose)
{
System.out.println("Obfuscating...");
- }
- // Check if we have at least some keep commands.
- if (configuration.keep == null &&
- configuration.keepNames == null &&
- configuration.applyMapping == null &&
- configuration.printMapping == null)
- {
- throw new IOException("You have to specify '-keep' options for the obfuscation step.");
- }
-
- // Clean up any old visitor info.
- programClassPool.classFilesAccept(new ClassFileCleaner());
- libraryClassPool.classFilesAccept(new ClassFileCleaner());
-
- // Link all methods that should get the same names.
- programClassPool.classFilesAccept(new BottomClassFileFilter(
- new MethodInfoLinker()));
-
- // Create a visitor for marking the seeds.
- NameMarker nameMarker = new NameMarker();
- ClassPoolVisitor classPoolvisitor =
- new MultiClassPoolVisitor(new ClassPoolVisitor[]
- {
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
- nameMarker,
- nameMarker),
- ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
- nameMarker,
- nameMarker)
- });
-
- // Mark the seeds.
- programClassPool.accept(classPoolvisitor);
- libraryClassPool.accept(classPoolvisitor);
-
- // All library classes and library class members keep their names.
- libraryClassPool.classFilesAccept(nameMarker);
- libraryClassPool.classFilesAccept(new AllMemberInfoVisitor(nameMarker));
-
- // Apply the mapping, if one has been specified. The mapping can
- // override the names of library classes and of library class members.
- if (configuration.applyMapping != null)
- {
- if (configuration.verbose)
+ // We'll apply a mapping, if requested.
+ if (configuration.applyMapping != null)
{
System.out.println("Applying mapping [" + configuration.applyMapping.getAbsolutePath() + "]");
}
- MappingReader reader = new MappingReader(configuration.applyMapping);
- MappingProcessor keeper =
- new MultiMappingProcessor(new MappingProcessor[]
- {
- new MappingKeeper(programClassPool),
- new MappingKeeper(libraryClassPool),
- });
-
- reader.pump(keeper);
- }
-
- // Mark attributes that have to be kept.
- AttributeUsageMarker attributeUsageMarker = new AttributeUsageMarker();
- if (configuration.keepAttributes != null)
- {
- if (configuration.keepAttributes.size() != 0)
- {
- attributeUsageMarker.setKeepAttributes(configuration.keepAttributes);
- }
- else
- {
- attributeUsageMarker.setKeepAllAttributes();
- }
- }
- programClassPool.classFilesAccept(attributeUsageMarker);
-
- // Remove the attributes that can be discarded.
- programClassPool.classFilesAccept(new AttributeShrinker());
-
- if (configuration.verbose)
- {
- System.out.println("Renaming program classes and class elements...");
- }
-
- // Come up with new names for all class files.
- programClassPool.classFilesAccept(new ClassFileObfuscator(programClassPool,
- configuration.defaultPackage,
- configuration.useMixedCaseClassNames));
-
- NameFactory nameFactory = new SimpleNameFactory();
-
- if (configuration.obfuscationDictionary != null)
- {
- nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, nameFactory);
- }
-
- Map descriptorMap = new HashMap();
-
- // Come up with new names for all non-private class members.
- programClassPool.classFilesAccept(
- new BottomClassFileFilter(
- new MultiClassFileVisitor(new ClassFileVisitor[]
- {
- // Collect all non-private member names in this name space.
- new ClassFileHierarchyTraveler(true, true, true, false,
- new AllMemberInfoVisitor(
- new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
- new MemberInfoNameCollector(configuration.overloadAggressively,
- descriptorMap)))),
-
- // Assign new names to all non-private members in this name space.
- new ClassFileHierarchyTraveler(true, true, true, false,
- new AllMemberInfoVisitor(
- new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
- new MemberInfoObfuscator(configuration.overloadAggressively,
- nameFactory,
- descriptorMap)))),
-
- // Clear the collected names.
- new MapCleaner(descriptorMap)
- })));
-
- // Come up with new names for all private class members.
- programClassPool.classFilesAccept(
- new MultiClassFileVisitor(new ClassFileVisitor[]
- {
- // Collect all member names in this class.
- new AllMemberInfoVisitor(
- new MemberInfoNameCollector(configuration.overloadAggressively,
- descriptorMap)),
-
- // Collect all non-private member names higher up the hierarchy.
- new ClassFileHierarchyTraveler(false, true, true, false,
- new AllMemberInfoVisitor(
- new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
- new MemberInfoNameCollector(configuration.overloadAggressively,
- descriptorMap)))),
-
- // Assign new names to all private members in this class.
- new AllMemberInfoVisitor(
- new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
- new MemberInfoObfuscator(configuration.overloadAggressively,
- nameFactory,
- descriptorMap))),
-
- // Clear the collected names.
- new MapCleaner(descriptorMap)
- }));
-
- // Some class members may have ended up with conflicting names.
- // Collect all special member names.
- programClassPool.classFilesAccept(
- new AllMemberInfoVisitor(
- new MemberInfoSpecialNameFilter(
- new MemberInfoNameCollector(configuration.overloadAggressively,
- descriptorMap))));
- libraryClassPool.classFilesAccept(
- new AllMemberInfoVisitor(
- new MemberInfoSpecialNameFilter(
- new MemberInfoNameCollector(configuration.overloadAggressively,
- descriptorMap))));
-
- // Replace the conflicting member names with special, globally unique names.
- programClassPool.classFilesAccept(
- new AllMemberInfoVisitor(
- new MemberInfoNameConflictFilter(
- new MultiMemberInfoVisitor(new MemberInfoVisitor[]
- {
- new MemberInfoNameCleaner(),
- new MemberInfoObfuscator(configuration.overloadAggressively,
- new SpecialNameFactory(new SimpleNameFactory()),
- descriptorMap),
- }))));
-
- descriptorMap.clear();
-
- // Print out the mapping, if requested.
- if (configuration.printMapping != null)
- {
- if (configuration.verbose)
+ // We'll print out the mapping, if requested.
+ if (configuration.printMapping != null)
{
System.out.println("Printing mapping" +
(isFile(configuration.printMapping) ?
" to [" + configuration.printMapping.getAbsolutePath() + "]" :
"..."));
}
-
- PrintStream ps = isFile(configuration.printMapping) ?
- new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printMapping))) :
- System.out;
-
- // Print out items that will be removed.
- programClassPool.classFilesAcceptAlphabetically(new MappingPrinter(ps));
-
- if (ps != System.out)
- {
- ps.close();
- }
- }
-
- // Actually apply the new names.
- programClassPool.classFilesAccept(new ClassFileRenamer());
- libraryClassPool.classFilesAccept(new ClassFileRenamer());
-
- // Update all references to these new names.
- programClassPool.classFilesAccept(new ClassFileReferenceFixer());
- libraryClassPool.classFilesAccept(new ClassFileReferenceFixer());
- programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
-
- // Make package visible elements public, if necessary.
- if (configuration.defaultPackage != null)
- {
- programClassPool.classFilesAccept(new ClassFileOpener());
}
- // Rename the source file attributes, if requested.
- if (configuration.newSourceFileAttribute != null)
- {
- programClassPool.classFilesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
- }
-
- // Mark NameAndType constant pool entries that have to be kept
- // and remove the other ones.
- programClassPool.classFilesAccept(new NameAndTypeUsageMarker());
- programClassPool.classFilesAccept(new NameAndTypeShrinker(1024));
-
- // Mark Utf8 constant pool entries that have to be kept
- // and remove the other ones.
- programClassPool.classFilesAccept(new Utf8UsageMarker());
- programClassPool.classFilesAccept(new Utf8Shrinker(1024));
+ // Perform the actual obfuscation.
+ new Obfuscator(configuration).execute(programClassPool, libraryClassPool);
}
@@ -1143,7 +730,8 @@ public class ProGuard
* Returns whether the given file is actually a file, or just a placeholder
* for the standard output.
*/
- private boolean isFile(File file){
+ private boolean isFile(File file)
+ {
return file.getPath().length() > 0;
}
@@ -1155,6 +743,7 @@ public class ProGuard
{
if (args.length == 0)
{
+ System.out.println(VERSION);
System.out.println("Usage: java proguard.ProGuard [options ...]");
System.exit(1);
}
@@ -1172,8 +761,7 @@ public class ProGuard
parser.parse(configuration);
// Execute ProGuard with these options.
- ProGuard proGuard = new ProGuard(configuration);
- proGuard.execute();
+ new ProGuard(configuration).execute();
}
finally
{
diff --git a/src/proguard/classfile/ClassFile.java b/src/proguard/classfile/ClassFile.java
index b2a7d38..8cb7e7c 100644
--- a/src/proguard/classfile/ClassFile.java
+++ b/src/proguard/classfile/ClassFile.java
@@ -228,13 +228,13 @@ public interface ClassFile extends VisitorAccepter
* Lets the given member info visitor visit all concrete implementations of
* the specified method in the class hierarchy.
* @param name the method name.
- * @param type the method descriptor.
+ * @param descriptor the method descriptor.
* @param visitThisMethod specifies whether to visit the method in
* this class.
+ * @param visitSpecialMethods specifies whether to visit the special
+ * initializer methods.
* @param visitSuperMethods specifies whether to visit the method in
* the super classes.
- * @param visitInterfaceMethods specifies whether to visit the method in
- * the interfaces.
* @param visitOverridingMethods specifies whether to visit the method in
* the subclasses.
* @param visitSpecialMethods specifies whether to visit special methods.
@@ -252,14 +252,14 @@ public interface ClassFile extends VisitorAccepter
* Lets the given member info visitor visit all concrete implementations of
* the specified method in the class hierarchy.
* @param name the method name.
- * @param type the method descriptor.
+ * @param descriptor the method descriptor.
* @param methodInfo the method itself, if present.
* @param visitThisMethod specifies whether to visit the method in
* this class.
+ * @param visitSpecialMethods specifies whether to visit the method in
+ * the interfaces.
* @param visitSuperMethods specifies whether to visit the method in
* the super classes.
- * @param visitInterfaceMethods specifies whether to visit the method in
- * the interfaces.
* @param visitOverridingMethods specifies whether to visit the method in
* the subclasses.
* @param visitSpecialMethods specifies whether to visit special methods.
diff --git a/src/proguard/classfile/editor/CodeAttrInfoEditor.java b/src/proguard/classfile/editor/CodeAttrInfoEditor.java
index 5f78a41..b06f481 100644
--- a/src/proguard/classfile/editor/CodeAttrInfoEditor.java
+++ b/src/proguard/classfile/editor/CodeAttrInfoEditor.java
@@ -1,4 +1,4 @@
-/* $Id: CodeAttrInfoEditor.java,v 1.13 2005/06/11 13:21:35 eric Exp $
+/* $Id: CodeAttrInfoEditor.java,v 1.14 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -195,7 +195,6 @@ public class CodeAttrInfoEditor
replacements[instructionOffset] != null ||
postInsertions[instructionOffset] != null ||
deleted[instructionOffset];
-
}
diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java
index 579d71e..6de23d6 100644
--- a/src/proguard/classfile/editor/MethodInvocationFixer.java
+++ b/src/proguard/classfile/editor/MethodInvocationFixer.java
@@ -1,4 +1,4 @@
-/* $Id: MethodInvocationFixer.java,v 1.3 2005/06/11 13:21:35 eric Exp $
+/* $Id: MethodInvocationFixer.java,v 1.4 2005/08/13 21:01:04 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -85,10 +85,6 @@ implements InstructionVisitor,
// Is it a method invocation?
if (isMethodInvocation)
{
- if (DEBUG)
- {
- }
-
// Do we need to update the opcode?
byte opcode = cpInstruction.opcode;
diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java
index d66b996..023009a 100644
--- a/src/proguard/classfile/editor/VariableEditor.java
+++ b/src/proguard/classfile/editor/VariableEditor.java
@@ -1,4 +1,4 @@
-/* $Id: VariableEditor.java,v 1.4 2005/06/11 13:21:35 eric Exp $
+/* $Id: VariableEditor.java,v 1.5 2005/09/11 22:15:22 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -56,11 +56,6 @@ public class VariableEditor
variableRemapper = new VariableRemapper(codeLength);
deleted = new boolean[maxLocals];
variableMap = new int[maxLocals];
-
- for (int index = 0; index < maxLocals; index++)
- {
- variableMap[index] = -1;
- }
}
@@ -80,8 +75,7 @@ public class VariableEditor
{
for (int index = 0; index < maxLocals; index++)
{
- deleted[index] = false;
- variableMap[index] = -1;
+ deleted[index] = false;
}
}
@@ -154,8 +148,7 @@ public class VariableEditor
int oldMaxLocals = codeAttrInfo.u2maxLocals;
// Make sure there is a sufficiently large variable map.
- if (variableMap == null ||
- variableMap.length < oldMaxLocals)
+ if (variableMap.length < oldMaxLocals)
{
variableMap = new int[oldMaxLocals];
}
@@ -164,7 +157,8 @@ public class VariableEditor
int newVariableIndex = 0;
for (int oldVariableIndex = 0; oldVariableIndex < oldMaxLocals; oldVariableIndex++)
{
- if (!deleted[oldVariableIndex])
+ if (oldVariableIndex >= deleted.length ||
+ !deleted[oldVariableIndex])
{
variableMap[oldVariableIndex] = newVariableIndex++;
}
diff --git a/src/proguard/classfile/instruction/Instruction.java b/src/proguard/classfile/instruction/Instruction.java
index 9eafaca..57bbda9 100644
--- a/src/proguard/classfile/instruction/Instruction.java
+++ b/src/proguard/classfile/instruction/Instruction.java
@@ -1,4 +1,4 @@
-/* $Id: Instruction.java,v 1.23 2005/06/11 13:13:15 eric Exp $
+/* $Id: Instruction.java,v 1.24 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -331,13 +331,13 @@ public abstract class Instruction
3, // sastore
1, // pop
2, // pop2
- 0, // dup
- 0, // dup_x1
- 0, // dup_x2
- 0, // dup2
- 0, // dup2_x1
- 0, // dup2_x2
- 0, // swap
+ 1, // dup
+ 2, // dup_x1
+ 3, // dup_x2
+ 2, // dup2
+ 3, // dup2_x1
+ 4, // dup2_x2
+ 2, // swap
2, // iadd
4, // ladd
2, // fadd
@@ -540,13 +540,13 @@ public abstract class Instruction
0, // sastore
0, // pop
0, // pop2
- 1, // dup
- 1, // dup_x1
- 1, // dup_x2
- 2, // dup2
- 2, // dup2_x1
- 2, // dup2_x2
- 0, // swap
+ 2, // dup
+ 3, // dup_x1
+ 4, // dup_x2
+ 4, // dup2
+ 5, // dup2_x1
+ 6, // dup2_x2
+ 2, // swap
1, // iadd
2, // ladd
1, // fadd
diff --git a/src/proguard/classfile/instruction/InstructionCounter.java b/src/proguard/classfile/instruction/InstructionCounter.java
new file mode 100644
index 0000000..2671f9b
--- /dev/null
+++ b/src/proguard/classfile/instruction/InstructionCounter.java
@@ -0,0 +1,106 @@
+/* $Id: InstructionCounter.java,v 1.1 2005/07/31 18:50:05 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2005 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttrInfo;
+import proguard.classfile.instruction.*;
+
+/**
+ * This InstructionVisitor counts the number of instructions that has been visited.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionCounter implements InstructionVisitor
+{
+ private int count;
+
+
+ /**
+ * Returns the number of instructions that has been visited so far.
+ */
+ public int getCount()
+ {
+ return count++;
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitBranchInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ BranchInstruction branchInstruction)
+ {
+ count++;
+ }
+
+
+ public void visitCpInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ CpInstruction cpInstruction)
+ {
+ count++;
+ }
+
+
+ public void visitLookUpSwitchInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ LookUpSwitchInstruction lookUpSwitchInstruction)
+ {
+ count++;
+ }
+
+
+ public void visitSimpleInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ SimpleInstruction simpleInstruction)
+ {
+ count++;
+ }
+
+
+ public void visitTableSwitchInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ TableSwitchInstruction tableSwitchInstruction)
+ {
+ count++;
+ }
+
+
+ public void visitVariableInstruction(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ int offset,
+ VariableInstruction variableInstruction)
+ {
+ count++;
+ }
+}
diff --git a/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java b/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
index d926107..d0f327b 100644
--- a/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
+++ b/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
@@ -1,4 +1,4 @@
-/* $Id: ClassFileClassForNameReferenceInitializer.java,v 1.16 2005/06/25 22:07:51 eric Exp $
+/* $Id: ClassFileClassForNameReferenceInitializer.java,v 1.17 2005/07/17 14:47:15 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -104,14 +104,37 @@ public class ClassFileClassForNameReferenceInitializer
// Implementations for InstructionVisitor.
- public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
- public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
- public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
- public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
+ public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+ {
+ // Nothing interesting; just forget any stored indices.
+ clearConstantPoolIndices();
+ }
+
+
+ public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+ {
+ // Nothing interesting; just forget any stored indices.
+ clearConstantPoolIndices();
+ }
+
+
+ public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+ {
+ // Nothing interesting; just forget any stored indices.
+ clearConstantPoolIndices();
+ }
+
+
+ public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+ {
+ // Nothing interesting; just forget any stored indices.
+ clearConstantPoolIndices();
+ }
+
+
public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
{
+
// Just ignore generic instructions and reset the constant pool indices.
switch (variableInstruction.opcode)
{
@@ -148,6 +171,11 @@ public class ClassFileClassForNameReferenceInitializer
// class$(String, boolean).
ldcStringCpIndex = currentCpIndex;
}
+ else
+ {
+ ldcStringCpIndex = -1;
+ }
+
invokestaticMethodRefCpIndex = -1;
invokevirtualMethodRefCpIndex = -1;
break;
@@ -210,16 +238,13 @@ public class ClassFileClassForNameReferenceInitializer
}
}
- ldcStringCpIndex = -1;
- invokestaticMethodRefCpIndex = -1;
- invokevirtualMethodRefCpIndex = -1;
+ // We've handled the special case; forget about the indices.
+ clearConstantPoolIndices();
break;
default:
- // Nothing interesting; just forget about previous indices.
- ldcStringCpIndex = -1;
- invokestaticMethodRefCpIndex = -1;
- invokevirtualMethodRefCpIndex = -1;
+ // Nothing interesting; just forget any stored indices.
+ clearConstantPoolIndices();
break;
}
}
@@ -270,6 +295,19 @@ public class ClassFileClassForNameReferenceInitializer
}
+ // Small utility methods.
+
+ /**
+ * Clears all references to the constant pool.
+ */
+ private void clearConstantPoolIndices()
+ {
+ ldcStringCpIndex = -1;
+ invokestaticMethodRefCpIndex = -1;
+ invokevirtualMethodRefCpIndex = -1;
+ }
+
+
/**
* Returns the class with the given name, either for the program class pool
* or from the library class pool, or <code>null</code> if it can't be found.
diff --git a/src/proguard/optimize/evaluation/value/Category2Value.java b/src/proguard/classfile/visitor/ClassCounter.java
similarity index 56%
copy from src/proguard/optimize/evaluation/value/Category2Value.java
copy to src/proguard/classfile/visitor/ClassCounter.java
index 6ebd773..32d9469 100644
--- a/src/proguard/optimize/evaluation/value/Category2Value.java
+++ b/src/proguard/classfile/visitor/ClassCounter.java
@@ -1,4 +1,4 @@
-/* $Id: Category2Value.java,v 1.3 2005/06/11 13:13:16 eric Exp $
+/* $Id: ClassCounter.java,v 1.1 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -18,24 +18,40 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-package proguard.optimize.evaluation.value;
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
/**
- * This abstract class represents a partially evaluated Category 2 value.
- *
+ * This ClassFileVisitor counts the number of classes that has been visited.
+ *
* @author Eric Lafortune
*/
-abstract class Category2Value extends Value
+public class ClassCounter implements ClassFileVisitor
{
- // Implementations for Value.
+ private int count;
+
+
+ /**
+ * Returns the number of classes that has been visited so far.
+ */
+ public int getCount()
+ {
+ return count++;
+ }
+
+
+ // Implementations for ClassFileVisitor.
- public final Category2Value category2Value()
+ public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
{
- return this;
+ count++;
}
- public final boolean isCategory2()
+
+ public void visitProgramClassFile(ProgramClassFile programClassFile)
{
- return true;
+ count++;
}
}
diff --git a/src/proguard/classfile/visitor/ClassFileNameFilter.java b/src/proguard/classfile/visitor/ClassFileNameFilter.java
index cb1a026..142e306 100644
--- a/src/proguard/classfile/visitor/ClassFileNameFilter.java
+++ b/src/proguard/classfile/visitor/ClassFileNameFilter.java
@@ -1,4 +1,4 @@
-/* $Id: ClassFileNameFilter.java,v 1.9 2005/06/11 13:13:15 eric Exp $
+/* $Id: ClassFileNameFilter.java,v 1.10 2005/08/21 20:25:33 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -33,8 +33,8 @@ import proguard.util.*;
*/
public class ClassFileNameFilter implements ClassFileVisitor
{
- private ClassFileVisitor classFileVisitor;
- private StringMatcher regularExpressionMatcher;
+ private ClassFileVisitor classFileVisitor;
+ private StringMatcher regularExpressionMatcher;
/**
@@ -48,7 +48,7 @@ public class ClassFileNameFilter implements ClassFileVisitor
String regularExpression)
{
this.classFileVisitor = classFileVisitor;
- this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
+ this.regularExpressionMatcher = new ClassNameListMatcher(regularExpression);
}
diff --git a/src/proguard/optimize/peephole/MethodPrivatizer.java b/src/proguard/classfile/visitor/MemberCounter.java
similarity index 53%
copy from src/proguard/optimize/peephole/MethodPrivatizer.java
copy to src/proguard/classfile/visitor/MemberCounter.java
index 0578abd..49e4d2e 100644
--- a/src/proguard/optimize/peephole/MethodPrivatizer.java
+++ b/src/proguard/classfile/visitor/MemberCounter.java
@@ -1,4 +1,4 @@
-/* $Id: MethodPrivatizer.java,v 1.5 2005/06/11 13:21:35 eric Exp $
+/* $Id: MemberCounter.java,v 1.1 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -18,41 +18,56 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-package proguard.optimize.peephole;
+package proguard.classfile.visitor;
import proguard.classfile.*;
-import proguard.classfile.util.AccessUtil;
-import proguard.classfile.visitor.MemberInfoVisitor;
-import proguard.optimize.NonPrivateMethodMarker;
+import proguard.classfile.visitor.*;
/**
- * This MemberInfoVisitor makes all final methods that it visits private,
- * unless they have been marked by a NonPrivateMethodMarker.
- *
- * @see NonPrivateMethodMarker
+ * This MemberInfoVisitor counts the number of class members that has been visited.
+ *
* @author Eric Lafortune
*/
-public class MethodPrivatizer
- implements MemberInfoVisitor
+public class MemberCounter implements MemberInfoVisitor
{
+ private int count;
+
+
+ /**
+ * Returns the number of class members that has been visited so far.
+ */
+ public int getCount()
+ {
+ return count++;
+ }
+
+
// Implementations for MemberInfoVisitor.
- public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+ public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile,
+ LibraryFieldInfo libraryFieldInfo)
+ {
+ count++;
+ }
- public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+
+ public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile,
+ LibraryMethodInfo libraryMethodInfo)
{
- int accessFlags = programMethodInfo.getAccessFlags();
+ count++;
+ }
- // Is the method unmarked?
- if (NonPrivateMethodMarker.canBeMadePrivate(programMethodInfo))
- {
- // Make the method private.
- programMethodInfo.u2accessFlags =
- AccessUtil.replaceAccessFlags(accessFlags,
- ClassConstants.INTERNAL_ACC_PRIVATE);
- }
+
+ public void visitProgramFieldInfo(ProgramClassFile programClassFile,
+ ProgramFieldInfo programFieldInfo)
+ {
+ count++;
}
- public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
- public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+
+ public void visitProgramMethodInfo(ProgramClassFile programClassFile,
+ ProgramMethodInfo programMethodInfo)
+ {
+ count++;
+ }
}
diff --git a/src/proguard/gui/ClassMemberSpecificationDialog.java b/src/proguard/gui/ClassMemberSpecificationDialog.java
index 25a4bb2..3c121b5 100644
--- a/src/proguard/gui/ClassMemberSpecificationDialog.java
+++ b/src/proguard/gui/ClassMemberSpecificationDialog.java
@@ -1,4 +1,4 @@
-/* $Id: ClassMemberSpecificationDialog.java,v 1.4 2005/06/11 13:13:15 eric Exp $
+/* $Id: ClassMemberSpecificationDialog.java,v 1.5 2005/08/21 19:28:04 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -177,7 +177,9 @@ class ClassMemberSpecificationDialog extends JDialog
// Create the type panel.
JPanel typePanel = new JPanel(layout);
typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
- GUIResources.getMessage("returnType")));
+ GUIResources.getMessage(isField ?
+ "type" :
+ "returnType")));
typePanel.add(typeTextField, constraintsLastStretch);
@@ -324,19 +326,43 @@ class ClassMemberSpecificationDialog extends JDialog
String type = typeTextField.getText();
String arguments = argumentsTextField.getText();
- boolean fullWildcard = name.equals("") ||
- type.equals("");
-
- ClassMemberSpecification classMemberSpecification = fullWildcard ?
- new ClassMemberSpecification(0,
- 0,
- null,
- null) :
- new ClassMemberSpecification(0,
- 0,
- name,
- isField ? ClassUtil.internalType(type) :
- ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments)));
+ if (name.equals("") ||
+ name.equals("*"))
+ {
+ name = null;
+ }
+
+ if (type.equals("") ||
+ type.equals("*"))
+ {
+ type = null;
+ }
+
+ if (name != null ||
+ type != null)
+ {
+ if (isField)
+ {
+ if (type == null)
+ {
+ type = ClassConstants.EXTERNAL_TYPE_INT;
+ }
+
+ type = ClassUtil.internalType(type);
+ }
+ else
+ {
+ if (type == null)
+ {
+ type = ClassConstants.EXTERNAL_TYPE_VOID;
+ }
+
+ type = ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments));
+ }
+ }
+
+ ClassMemberSpecification classMemberSpecification =
+ new ClassMemberSpecification(0, 0, name, type);
// Also get the access radio button settings.
getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC, publicRadioButtons);
diff --git a/src/proguard/gui/ClassMemberSpecificationsPanel.java b/src/proguard/gui/ClassMemberSpecificationsPanel.java
index 3964b42..90154a4 100644
--- a/src/proguard/gui/ClassMemberSpecificationsPanel.java
+++ b/src/proguard/gui/ClassMemberSpecificationsPanel.java
@@ -1,4 +1,4 @@
-/* $Id: ClassMemberSpecificationsPanel.java,v 1.6 2005/06/11 13:13:15 eric Exp $
+/* $Id: ClassMemberSpecificationsPanel.java,v 1.7 2005/08/21 19:28:04 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -22,6 +22,7 @@ package proguard.gui;
import proguard.*;
import proguard.classfile.util.ClassUtil;
+import proguard.classfile.ClassConstants;
import java.awt.Component;
import java.awt.event.*;
@@ -226,10 +227,17 @@ class ClassMemberSpecificationsPanel extends ListPanel
MyClassMemberSpecificationWrapper wrapper = (MyClassMemberSpecificationWrapper)value;
ClassMemberSpecification option = wrapper.classMemberSpecification;
- String name = option.name;
+ String name = option.name;
+ String descriptor = option.descriptor;
+
+ if (name == null)
+ {
+ name = "*";
+ }
+
label.setText(wrapper.isField ?
- (name == null ? "<fields>" : ClassUtil.externalFullFieldDescription(0, option.name, option.descriptor)) :
- (name == null ? "<methods>" : ClassUtil.externalFullMethodDescription("<init>", 0, option.name, option.descriptor)));
+ (descriptor == null ? "<fields>" : ClassUtil.externalFullFieldDescription(0, name, descriptor)) :
+ (descriptor == null ? "<methods>" : ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT, 0, name, descriptor)));
if (isSelected)
{
@@ -259,7 +267,7 @@ class ClassMemberSpecificationsPanel extends ListPanel
public boolean isField;
public MyClassMemberSpecificationWrapper(ClassMemberSpecification classMemberSpecification,
- boolean isField)
+ boolean isField)
{
this.classMemberSpecification = classMemberSpecification;
this.isField = isField;
diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties
index 9132b6f..d8880f3 100644
--- a/src/proguard/gui/GUIResources.properties
+++ b/src/proguard/gui/GUIResources.properties
@@ -24,7 +24,7 @@ obfuscation = Obfuscation
#
# Panel titles.
#
-welcome = Welcome to ProGuard, version 3.3.2
+welcome = Welcome to ProGuard, version 3.4
options = Options
keepAdditional = Keep additional classes and class members
keepNamesAdditional = Keep additional class names and class member names
@@ -134,6 +134,7 @@ boilerplate__class_method_names = .class method names
boilerplate_remove = Remove
boilerplate_system_method_calls = System method calls without side effects
boilerplate_math_method_calls = Math method calls without side effects
+boilerplate_number_method_calls = Number method calls without side effects
boilerplate_string_method_calls = String method calls without side effects
boilerplate_stringbuffer_method_calls = StringBuffer method calls without side effects
boilerplate_stringbuilder_method_calls = StringBuilder method calls without side effects
@@ -168,6 +169,7 @@ classMembers = Class members
extensionsOf = Extensions of
specificationNumber = Specification #
+type = Type
returnType = Return type
name = Name
arguments = Arguments
diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java
index 7c7465f..ecabbaa 100644
--- a/src/proguard/gui/ProGuardGUI.java
+++ b/src/proguard/gui/ProGuardGUI.java
@@ -1,4 +1,4 @@
-/* $Id: ProGuardGUI.java,v 1.34 2005/06/25 22:08:57 eric Exp $
+/* $Id: ProGuardGUI.java,v 1.35 2005/08/21 20:25:33 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -891,9 +891,8 @@ public class ProGuardGUI extends JFrame
{
if (boilerplateKeepCheckBoxes[index].isSelected())
{
- addClassSpecifications(keep,
- boilerplateKeep[index],
- boilerplateKeepTextFields[index].getText());
+ keep.add(classSpecification(boilerplateKeep[index],
+ boilerplateKeepTextFields[index].getText()));
}
}
@@ -918,9 +917,8 @@ public class ProGuardGUI extends JFrame
{
if (boilerplateKeepNamesCheckBoxes[index].isSelected())
{
- addClassSpecifications(keepNames,
- boilerplateKeepNames[index],
- boilerplateKeepNamesTextFields[index].getText());
+ keepNames.add(classSpecification(boilerplateKeepNames[index],
+ boilerplateKeepNamesTextFields[index].getText()));
}
}
@@ -997,9 +995,9 @@ public class ProGuardGUI extends JFrame
/**
- * Looks in the given list for a ProGuard option that is identical to the
- * given template. Returns true if it found, and removes the matching option
- * as a side effect.
+ * Looks in the given list for a class specification that is identical to
+ * the given template. Returns true if it is found, and removes the matching
+ * class specification as a side effect.
*/
private boolean findClassSpecification(ClassSpecification classSpecificationTemplate,
List classSpecifications)
@@ -1025,9 +1023,10 @@ public class ProGuardGUI extends JFrame
/**
- * Looks in the given list for ProGuard options that match the given template.
- * Returns a comma-separated string of class file names from matching options,
- * and removes the matching options as a side effect.
+ * Looks in the given list for class specifications that match the given
+ * template. Returns a comma-separated string of class file names from
+ * matching class specifications, and removes the matching class
+ * specifications as a side effect.
*/
private String findMatchingClassSpecifications(ClassSpecification classSpecificationTemplate,
List classSpecifications)
@@ -1067,8 +1066,10 @@ public class ProGuardGUI extends JFrame
/**
- * Adds ProGuard options to the given list, based on the given option
- * template and the comma-separated list of class names to be filled in.
+ * Adds class specifications to the given list, based on the given template
+ * and the comma-separated list of class names to be filled in.
+ * @deprecated Add a single class specification using
+ * #classSpecification(ClassSpecification,String).
*/
private void addClassSpecifications(List classSpecifications,
ClassSpecification classSpecificationTemplate,
@@ -1080,20 +1081,32 @@ public class ProGuardGUI extends JFrame
{
String className = (String)classNames.get(index);
- // Create a copy of the template.
- ClassSpecification classSpecification =
- (ClassSpecification)classSpecificationTemplate.clone();
+ // Add a modified copy of the template to the list.
+ classSpecifications.add(classSpecification(classSpecificationTemplate, className));
+ }
+ }
- // Set the class name in the copy.
- classSpecification.className =
- className.equals("") ||
- className.equals("*") ?
- null :
- ClassUtil.internalClassName(className);
- // Add the copy to the list.
- classSpecifications.add(classSpecification);
- }
+ /**
+ * Returns a class specification, based on the given template and the class
+ * name to be filled in.
+ */
+ private ClassSpecification classSpecification(ClassSpecification classSpecificationTemplate,
+ String className)
+ {
+ // Create a copy of the template.
+ ClassSpecification classSpecification =
+ (ClassSpecification)classSpecificationTemplate.clone();
+
+ // Set the class name in the copy.
+ classSpecification.className =
+ className.equals("") ||
+ className.equals("*") ?
+ null :
+ ClassUtil.internalClassName(className);
+
+ // Return the modified copy.
+ return classSpecification;
}
diff --git a/src/proguard/gui/boilerplate.pro b/src/proguard/gui/boilerplate.pro
index c905e24..ee270c7 100644
--- a/src/proguard/gui/boilerplate.pro
+++ b/src/proguard/gui/boilerplate.pro
@@ -83,14 +83,14 @@
# Remove - System method calls. Remove all invocations of System
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.System {
- public static native long currentTimeMillis();
+ public static long currentTimeMillis();
static java.lang.Class getCallerClass();
- public static native int identityHashCode(java.lang.Object);
+ public static int identityHashCode(java.lang.Object);
public static java.lang.SecurityManager getSecurityManager();
public static java.util.Properties getProperties();
public static java.lang.String getProperty(java.lang.String);
public static java.lang.String getenv(java.lang.String);
- public static native java.lang.String mapLibraryName(java.lang.String);
+ public static java.lang.String mapLibraryName(java.lang.String);
public static java.lang.String getProperty(java.lang.String,java.lang.String);
}
@@ -143,6 +143,130 @@
public static double log1p(double);
}
+# Remove - Number method calls. Remove all invocations of Number
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.* extends java.lang.Number {
+ public static java.lang.String toString(byte);
+ public static java.lang.Byte valueOf(byte);
+ public static byte parseByte(java.lang.String);
+ public static byte parseByte(java.lang.String,int);
+ public static java.lang.Byte valueOf(java.lang.String,int);
+ public static java.lang.Byte valueOf(java.lang.String);
+ public static java.lang.Byte decode(java.lang.String);
+ public int compareTo(java.lang.Byte);
+
+ public static java.lang.String toString(short);
+ public static short parseShort(java.lang.String);
+ public static short parseShort(java.lang.String,int);
+ public static java.lang.Short valueOf(java.lang.String,int);
+ public static java.lang.Short valueOf(java.lang.String);
+ public static java.lang.Short valueOf(short);
+ public static java.lang.Short decode(java.lang.String);
+ public static short reverseBytes(short);
+ public int compareTo(java.lang.Short);
+
+ public static java.lang.String toString(int,int);
+ public static java.lang.String toHexString(int);
+ public static java.lang.String toOctalString(int);
+ public static java.lang.String toBinaryString(int);
+ public static java.lang.String toString(int);
+ public static int parseInt(java.lang.String,int);
+ public static int parseInt(java.lang.String);
+ public static java.lang.Integer valueOf(java.lang.String,int);
+ public static java.lang.Integer valueOf(java.lang.String);
+ public static java.lang.Integer valueOf(int);
+ public static java.lang.Integer getInteger(java.lang.String);
+ public static java.lang.Integer getInteger(java.lang.String,int);
+ public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer);
+ public static java.lang.Integer decode(java.lang.String);
+ public static int highestOneBit(int);
+ public static int lowestOneBit(int);
+ public static int numberOfLeadingZeros(int);
+ public static int numberOfTrailingZeros(int);
+ public static int bitCount(int);
+ public static int rotateLeft(int,int);
+ public static int rotateRight(int,int);
+ public static int reverse(int);
+ public static int signum(int);
+ public static int reverseBytes(int);
+ public int compareTo(java.lang.Integer);
+
+ public static java.lang.String toString(long,int);
+ public static java.lang.String toHexString(long);
+ public static java.lang.String toOctalString(long);
+ public static java.lang.String toBinaryString(long);
+ public static java.lang.String toString(long);
+ public static long parseLong(java.lang.String,int);
+ public static long parseLong(java.lang.String);
+ public static java.lang.Long valueOf(java.lang.String,int);
+ public static java.lang.Long valueOf(java.lang.String);
+ public static java.lang.Long valueOf(long);
+ public static java.lang.Long decode(java.lang.String);
+ public static java.lang.Long getLong(java.lang.String);
+ public static java.lang.Long getLong(java.lang.String,long);
+ public static java.lang.Long getLong(java.lang.String,java.lang.Long);
+ public static long highestOneBit(long);
+ public static long lowestOneBit(long);
+ public static int numberOfLeadingZeros(long);
+ public static int numberOfTrailingZeros(long);
+ public static int bitCount(long);
+ public static long rotateLeft(long,int);
+ public static long rotateRight(long,int);
+ public static long reverse(long);
+ public static int signum(long);
+ public static long reverseBytes(long);
+ public int compareTo(java.lang.Long);
+
+ public static java.lang.String toString(float);
+ public static java.lang.String toHexString(float);
+ public static java.lang.Float valueOf(java.lang.String);
+ public static java.lang.Float valueOf(float);
+ public static float parseFloat(java.lang.String);
+ public static boolean isNaN(float);
+ public static boolean isInfinite(float);
+ public static int floatToIntBits(float);
+ public static int floatToRawIntBits(float);
+ public static float intBitsToFloat(int);
+ public static int compare(float,float);
+ public boolean isNaN();
+ public boolean isInfinite();
+ public int compareTo(java.lang.Float);
+
+ public static java.lang.String toString(double);
+ public static java.lang.String toHexString(double);
+ public static java.lang.Double valueOf(java.lang.String);
+ public static java.lang.Double valueOf(double);
+ public static double parseDouble(java.lang.String);
+ public static boolean isNaN(double);
+ public static boolean isInfinite(double);
+ public static long doubleToLongBits(double);
+ public static long doubleToRawLongBits(double);
+ public static double longBitsToDouble(long);
+ public static int compare(double,double);
+ public boolean isNaN();
+ public boolean isInfinite();
+ public int compareTo(java.lang.Double);
+
+ public <init>(byte);
+ public <init>(short);
+ public <init>(int);
+ public <init>(long);
+ public <init>(float);
+ public <init>(double);
+ public <init>(java.lang.String);
+ public byte byteValue();
+ public short shortValue();
+ public int intValue();
+ public long longValue();
+ public float floatValue();
+ public double doubleValue();
+
+ public int compareTo(java.lang.Object);
+ public boolean equals(java.lang.Object);
+ public int hashCode();
+ public java.lang.String toString();
+}
+
# Remove - String method calls. Remove all invocations of String
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.String {
@@ -211,7 +335,6 @@
public java.lang.String trim();
}
-
# Remove - StringBuffer method calls. Remove all invocations of
# StringBuffer methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.StringBuffer {
diff --git a/src/proguard/gui/default.pro b/src/proguard/gui/default.pro
index a8cf902..540e6b7 100644
--- a/src/proguard/gui/default.pro
+++ b/src/proguard/gui/default.pro
@@ -23,14 +23,14 @@
# Remove - System method calls. Remove all invocations of System
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.System {
- public static native long currentTimeMillis();
+ public static long currentTimeMillis();
static java.lang.Class getCallerClass();
- public static native int identityHashCode(java.lang.Object);
+ public static int identityHashCode(java.lang.Object);
public static java.lang.SecurityManager getSecurityManager();
public static java.util.Properties getProperties();
public static java.lang.String getProperty(java.lang.String);
public static java.lang.String getenv(java.lang.String);
- public static native java.lang.String mapLibraryName(java.lang.String);
+ public static java.lang.String mapLibraryName(java.lang.String);
public static java.lang.String getProperty(java.lang.String,java.lang.String);
}
@@ -83,6 +83,130 @@
public static double log1p(double);
}
+# Remove - Number method calls. Remove all invocations of Number
+# methods without side effects whose return values are not used.
+-assumenosideeffects public class java.lang.* extends java.lang.Number {
+ public static java.lang.String toString(byte);
+ public static java.lang.Byte valueOf(byte);
+ public static byte parseByte(java.lang.String);
+ public static byte parseByte(java.lang.String,int);
+ public static java.lang.Byte valueOf(java.lang.String,int);
+ public static java.lang.Byte valueOf(java.lang.String);
+ public static java.lang.Byte decode(java.lang.String);
+ public int compareTo(java.lang.Byte);
+
+ public static java.lang.String toString(short);
+ public static short parseShort(java.lang.String);
+ public static short parseShort(java.lang.String,int);
+ public static java.lang.Short valueOf(java.lang.String,int);
+ public static java.lang.Short valueOf(java.lang.String);
+ public static java.lang.Short valueOf(short);
+ public static java.lang.Short decode(java.lang.String);
+ public static short reverseBytes(short);
+ public int compareTo(java.lang.Short);
+
+ public static java.lang.String toString(int,int);
+ public static java.lang.String toHexString(int);
+ public static java.lang.String toOctalString(int);
+ public static java.lang.String toBinaryString(int);
+ public static java.lang.String toString(int);
+ public static int parseInt(java.lang.String,int);
+ public static int parseInt(java.lang.String);
+ public static java.lang.Integer valueOf(java.lang.String,int);
+ public static java.lang.Integer valueOf(java.lang.String);
+ public static java.lang.Integer valueOf(int);
+ public static java.lang.Integer getInteger(java.lang.String);
+ public static java.lang.Integer getInteger(java.lang.String,int);
+ public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer);
+ public static java.lang.Integer decode(java.lang.String);
+ public static int highestOneBit(int);
+ public static int lowestOneBit(int);
+ public static int numberOfLeadingZeros(int);
+ public static int numberOfTrailingZeros(int);
+ public static int bitCount(int);
+ public static int rotateLeft(int,int);
+ public static int rotateRight(int,int);
+ public static int reverse(int);
+ public static int signum(int);
+ public static int reverseBytes(int);
+ public int compareTo(java.lang.Integer);
+
+ public static java.lang.String toString(long,int);
+ public static java.lang.String toHexString(long);
+ public static java.lang.String toOctalString(long);
+ public static java.lang.String toBinaryString(long);
+ public static java.lang.String toString(long);
+ public static long parseLong(java.lang.String,int);
+ public static long parseLong(java.lang.String);
+ public static java.lang.Long valueOf(java.lang.String,int);
+ public static java.lang.Long valueOf(java.lang.String);
+ public static java.lang.Long valueOf(long);
+ public static java.lang.Long decode(java.lang.String);
+ public static java.lang.Long getLong(java.lang.String);
+ public static java.lang.Long getLong(java.lang.String,long);
+ public static java.lang.Long getLong(java.lang.String,java.lang.Long);
+ public static long highestOneBit(long);
+ public static long lowestOneBit(long);
+ public static int numberOfLeadingZeros(long);
+ public static int numberOfTrailingZeros(long);
+ public static int bitCount(long);
+ public static long rotateLeft(long,int);
+ public static long rotateRight(long,int);
+ public static long reverse(long);
+ public static int signum(long);
+ public static long reverseBytes(long);
+ public int compareTo(java.lang.Long);
+
+ public static java.lang.String toString(float);
+ public static java.lang.String toHexString(float);
+ public static java.lang.Float valueOf(java.lang.String);
+ public static java.lang.Float valueOf(float);
+ public static float parseFloat(java.lang.String);
+ public static boolean isNaN(float);
+ public static boolean isInfinite(float);
+ public static int floatToIntBits(float);
+ public static int floatToRawIntBits(float);
+ public static float intBitsToFloat(int);
+ public static int compare(float,float);
+ public boolean isNaN();
+ public boolean isInfinite();
+ public int compareTo(java.lang.Float);
+
+ public static java.lang.String toString(double);
+ public static java.lang.String toHexString(double);
+ public static java.lang.Double valueOf(java.lang.String);
+ public static java.lang.Double valueOf(double);
+ public static double parseDouble(java.lang.String);
+ public static boolean isNaN(double);
+ public static boolean isInfinite(double);
+ public static long doubleToLongBits(double);
+ public static long doubleToRawLongBits(double);
+ public static double longBitsToDouble(long);
+ public static int compare(double,double);
+ public boolean isNaN();
+ public boolean isInfinite();
+ public int compareTo(java.lang.Double);
+
+ public <init>(byte);
+ public <init>(short);
+ public <init>(int);
+ public <init>(long);
+ public <init>(float);
+ public <init>(double);
+ public <init>(java.lang.String);
+ public byte byteValue();
+ public short shortValue();
+ public int intValue();
+ public long longValue();
+ public float floatValue();
+ public double doubleValue();
+
+ public int compareTo(java.lang.Object);
+ public boolean equals(java.lang.Object);
+ public int hashCode();
+ public java.lang.String toString();
+}
+
# Remove - String method calls. Remove all invocations of String
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.String {
@@ -151,7 +275,6 @@
public java.lang.String trim();
}
-
# Remove - StringBuffer method calls. Remove all invocations of
# StringBuffer methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.StringBuffer {
diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java
index f57eb1a..c3076b5 100644
--- a/src/proguard/io/DirectoryPump.java
+++ b/src/proguard/io/DirectoryPump.java
@@ -1,4 +1,4 @@
-/* $Id: DirectoryPump.java,v 1.3 2005/06/11 13:13:15 eric Exp $
+/* $Id: DirectoryPump.java,v 1.4 2005/08/21 20:24:12 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -45,6 +45,11 @@ public class DirectoryPump implements DataEntryPump
public void pumpDataEntries(DataEntryReader dataEntryReader)
throws IOException
{
+ if (!directory.exists())
+ {
+ throw new IOException("No such file or directory");
+ }
+
readFiles(directory, dataEntryReader);
}
diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java
index 0059dba..86fb925 100644
--- a/src/proguard/obfuscate/NameMarker.java
+++ b/src/proguard/obfuscate/NameMarker.java
@@ -1,4 +1,4 @@
-/* $Id: NameMarker.java,v 1.16 2005/06/11 13:13:16 eric Exp $
+/* $Id: NameMarker.java,v 1.17 2005/08/13 20:57:55 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -57,12 +57,18 @@ public class NameMarker
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
{
keepFieldName(programClassFile, programFieldInfo);
+
+ // Mark the class names referenced in the descriptor string.
+ programFieldInfo.referencedClassesAccept(this);
}
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
{
keepMethodName(programClassFile, programMethodInfo);
+
+ // Mark the class names referenced in the descriptor string.
+ programMethodInfo.referencedClassesAccept(this);
}
diff --git a/src/proguard/obfuscate/Obfuscator.java b/src/proguard/obfuscate/Obfuscator.java
new file mode 100644
index 0000000..fd20eff
--- /dev/null
+++ b/src/proguard/obfuscate/Obfuscator.java
@@ -0,0 +1,278 @@
+/* $Id: Obfuscator.java,v 1.2 2005/07/30 12:14:27 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2005 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.*;
+import proguard.classfile.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.util.MethodInfoLinker;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This class can perform obfuscation of class pools according to a given
+ * specification.
+ *
+ * @author Eric Lafortune
+ */
+public class Obfuscator
+{
+ private Configuration configuration;
+
+
+ /**
+ * Creates a new Obfuscator.
+ */
+ public Obfuscator(Configuration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+
+ /**
+ * Performs obfuscation of the given program class pool.
+ */
+ public void execute(ClassPool programClassPool,
+ ClassPool libraryClassPool) throws IOException
+ {
+ // Check if we have at least some keep commands.
+ if (configuration.keep == null &&
+ configuration.keepNames == null &&
+ configuration.applyMapping == null &&
+ configuration.printMapping == null)
+ {
+ throw new IOException("You have to specify '-keep' options for the obfuscation step.");
+ }
+
+ // Clean up any old visitor info.
+ programClassPool.classFilesAccept(new ClassFileCleaner());
+ libraryClassPool.classFilesAccept(new ClassFileCleaner());
+
+ // Link all methods that should get the same names.
+ programClassPool.classFilesAccept(new BottomClassFileFilter(
+ new MethodInfoLinker()));
+
+ // Create a visitor for marking the seeds.
+ NameMarker nameMarker = new NameMarker();
+ ClassPoolVisitor classPoolvisitor =
+ new MultiClassPoolVisitor(new ClassPoolVisitor[]
+ {
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+ nameMarker,
+ nameMarker),
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
+ nameMarker,
+ nameMarker)
+ });
+
+ // Mark the seeds.
+ programClassPool.accept(classPoolvisitor);
+ libraryClassPool.accept(classPoolvisitor);
+
+ // All library classes and library class members keep their names.
+ libraryClassPool.classFilesAccept(nameMarker);
+ libraryClassPool.classFilesAccept(new AllMemberInfoVisitor(nameMarker));
+
+ // Apply the mapping, if one has been specified. The mapping can
+ // override the names of library classes and of library class members.
+ if (configuration.applyMapping != null)
+ {
+ MappingReader reader = new MappingReader(configuration.applyMapping);
+ MappingProcessor keeper =
+ new MultiMappingProcessor(new MappingProcessor[]
+ {
+ new MappingKeeper(programClassPool),
+ new MappingKeeper(libraryClassPool),
+ });
+
+ reader.pump(keeper);
+ }
+
+ // Mark attributes that have to be kept.
+ AttributeUsageMarker attributeUsageMarker = new AttributeUsageMarker();
+ if (configuration.keepAttributes != null)
+ {
+ if (configuration.keepAttributes.size() != 0)
+ {
+ attributeUsageMarker.setKeepAttributes(configuration.keepAttributes);
+ }
+ else
+ {
+ attributeUsageMarker.setKeepAllAttributes();
+ }
+ }
+ programClassPool.classFilesAccept(attributeUsageMarker);
+
+ // Remove the attributes that can be discarded.
+ programClassPool.classFilesAccept(new AttributeShrinker());
+
+ // Come up with new names for all class files.
+ programClassPool.classFilesAccept(new ClassFileObfuscator(programClassPool,
+ configuration.defaultPackage,
+ configuration.useMixedCaseClassNames));
+
+ NameFactory nameFactory = new SimpleNameFactory();
+
+ if (configuration.obfuscationDictionary != null)
+ {
+ nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, nameFactory);
+ }
+
+ Map descriptorMap = new HashMap();
+
+ // Come up with new names for all non-private class members.
+ programClassPool.classFilesAccept(
+ new BottomClassFileFilter(
+ new MultiClassFileVisitor(new ClassFileVisitor[]
+ {
+ // Collect all member names in this name space.
+ new ClassFileHierarchyTraveler(true, true, true, false,
+ new AllMemberInfoVisitor(
+ new MemberInfoNameCollector(configuration.overloadAggressively,
+ descriptorMap))),
+
+ // Assign new names to all non-private members in this name space.
+ new ClassFileHierarchyTraveler(true, true, true, false,
+ new AllMemberInfoVisitor(
+ new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+ new MemberInfoObfuscator(configuration.overloadAggressively,
+ nameFactory,
+ descriptorMap)))),
+
+ // Clear the collected names.
+ new MapCleaner(descriptorMap)
+ })));
+
+ // Come up with new names for all private class members.
+ programClassPool.classFilesAccept(
+ new MultiClassFileVisitor(new ClassFileVisitor[]
+ {
+ // Collect all member names in this class.
+ new AllMemberInfoVisitor(
+ new MemberInfoNameCollector(configuration.overloadAggressively,
+ descriptorMap)),
+
+ // Collect all non-private member names higher up the hierarchy.
+ new ClassFileHierarchyTraveler(false, true, true, false,
+ new AllMemberInfoVisitor(
+ new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+ new MemberInfoNameCollector(configuration.overloadAggressively,
+ descriptorMap)))),
+
+ // Assign new names to all private members in this class.
+ new AllMemberInfoVisitor(
+ new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
+ new MemberInfoObfuscator(configuration.overloadAggressively,
+ nameFactory,
+ descriptorMap))),
+
+ // Clear the collected names.
+ new MapCleaner(descriptorMap)
+ }));
+
+ // Some class members may have ended up with conflicting names.
+ // Collect all special member names.
+ programClassPool.classFilesAccept(
+ new AllMemberInfoVisitor(
+ new MemberInfoSpecialNameFilter(
+ new MemberInfoNameCollector(configuration.overloadAggressively,
+ descriptorMap))));
+ libraryClassPool.classFilesAccept(
+ new AllMemberInfoVisitor(
+ new MemberInfoSpecialNameFilter(
+ new MemberInfoNameCollector(configuration.overloadAggressively,
+ descriptorMap))));
+
+ // Replace the conflicting member names with special, globally unique names.
+ programClassPool.classFilesAccept(
+ new AllMemberInfoVisitor(
+ new MemberInfoNameConflictFilter(
+ new MultiMemberInfoVisitor(new MemberInfoVisitor[]
+ {
+ new MemberInfoNameCleaner(),
+ new MemberInfoObfuscator(configuration.overloadAggressively,
+ new SpecialNameFactory(new SimpleNameFactory()),
+ descriptorMap),
+ }))));
+
+ descriptorMap.clear();
+
+ // Print out the mapping, if requested.
+ if (configuration.printMapping != null)
+ {
+ PrintStream ps = isFile(configuration.printMapping) ?
+ new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printMapping))) :
+ System.out;
+
+ // Print out items that will be removed.
+ programClassPool.classFilesAcceptAlphabetically(new MappingPrinter(ps));
+
+ if (ps != System.out)
+ {
+ ps.close();
+ }
+ }
+
+ // Actually apply the new names.
+ programClassPool.classFilesAccept(new ClassFileRenamer());
+ libraryClassPool.classFilesAccept(new ClassFileRenamer());
+
+ // Update all references to these new names.
+ programClassPool.classFilesAccept(new ClassFileReferenceFixer());
+ libraryClassPool.classFilesAccept(new ClassFileReferenceFixer());
+ programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
+
+ // Make package visible elements public, if necessary.
+ if (configuration.defaultPackage != null)
+ {
+ programClassPool.classFilesAccept(new ClassFileOpener());
+ }
+
+ // Rename the source file attributes, if requested.
+ if (configuration.newSourceFileAttribute != null)
+ {
+ programClassPool.classFilesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
+ }
+
+ // Mark NameAndType constant pool entries that have to be kept
+ // and remove the other ones.
+ programClassPool.classFilesAccept(new NameAndTypeUsageMarker());
+ programClassPool.classFilesAccept(new NameAndTypeShrinker(1024));
+
+ // Mark Utf8 constant pool entries that have to be kept
+ // and remove the other ones.
+ programClassPool.classFilesAccept(new Utf8UsageMarker());
+ programClassPool.classFilesAccept(new Utf8Shrinker(1024));
+ }
+
+
+ /**
+ * Returns whether the given file is actually a file, or just a placeholder
+ * for the standard output.
+ */
+ private boolean isFile(File file)
+ {
+ return file.getPath().length() > 0;
+ }
+}
diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java
index e54b5e7..6024d1c 100644
--- a/src/proguard/optimize/KeepMarker.java
+++ b/src/proguard/optimize/KeepMarker.java
@@ -1,4 +1,4 @@
-/* $Id: KeepMarker.java,v 1.6 2005/06/05 15:54:09 eric Exp $
+/* $Id: KeepMarker.java,v 1.7 2005/08/13 20:57:55 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -56,12 +56,18 @@ public class KeepMarker
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
{
markAsKept(programFieldInfo);
+
+ // Mark the classes referenced in the descriptor string.
+ programFieldInfo.referencedClassesAccept(this);
}
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
{
markAsKept(MethodInfoLinker.lastMethodInfo(programMethodInfo));
+
+ // Mark the classes referenced in the descriptor string.
+ programMethodInfo.referencedClassesAccept(this);
}
diff --git a/src/proguard/optimize/NonPrivateMethodMarker.java b/src/proguard/optimize/NonPrivateMethodMarker.java
index f66511f..74fbb36 100644
--- a/src/proguard/optimize/NonPrivateMethodMarker.java
+++ b/src/proguard/optimize/NonPrivateMethodMarker.java
@@ -1,4 +1,4 @@
-/* $Id: NonPrivateMethodMarker.java,v 1.6 2005/06/11 13:21:35 eric Exp $
+/* $Id: NonPrivateMethodMarker.java,v 1.7 2005/07/10 11:08:57 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java
new file mode 100644
index 0000000..bae56f6
--- /dev/null
+++ b/src/proguard/optimize/Optimizer.java
@@ -0,0 +1,293 @@
+/* $Id: Optimizer.java,v 1.9 2005/10/22 11:55:29 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2005 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.*;
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.MethodInfoLinker;
+import proguard.classfile.visitor.*;
+import proguard.optimize.evaluation.EvaluationSimplifier;
+import proguard.optimize.peephole.*;
+
+import java.io.IOException;
+
+/**
+ * This class optimizes class pools according to a given configuration.
+ *
+ * @author Eric Lafortune
+ */
+public class Optimizer
+{
+ private Configuration configuration;
+
+
+ /**
+ * Creates a new Optimizer.
+ */
+ public Optimizer(Configuration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+
+ /**
+ * Performs optimization of the given program class pool.
+ */
+ public void execute(ClassPool programClassPool,
+ ClassPool libraryClassPool) throws IOException
+ {
+ // Create counters to count the numbers of optimizations, if required.
+ ClassCounter singleImplementationCounter = null;
+ ClassCounter finalClassCounter = null;
+ MemberCounter writeOnlyFieldCounter = null;
+ MemberCounter finalMethodCounter = null;
+ MemberCounter privateMethodCounter = null;
+ MemberCounter staticMethodCounter = null;
+ MemberCounter parameterShrinkCounter = null;
+ InstructionCounter getterSetterCounter = null;
+ InstructionCounter commonCodeCounter = null;
+ InstructionCounter pushCounter = null;
+ InstructionCounter branchCounter = null;
+ InstructionCounter deletedCounter = null;
+// InstructionCounter nopCounter = null;
+ InstructionCounter pushPopCounter = null;
+ InstructionCounter loadStoreCounter = null;
+ InstructionCounter storeLoadCounter = null;
+ InstructionCounter gotoGotoCounter = null;
+ InstructionCounter gotoReturnCounter = null;
+
+ if (configuration.verbose)
+ {
+ singleImplementationCounter = new ClassCounter();
+ finalClassCounter = new ClassCounter();
+ writeOnlyFieldCounter = new MemberCounter();
+ finalMethodCounter = new MemberCounter();
+ privateMethodCounter = new MemberCounter();
+ staticMethodCounter = new MemberCounter();
+ parameterShrinkCounter = new MemberCounter();
+ getterSetterCounter = new InstructionCounter();
+ commonCodeCounter = new InstructionCounter();
+ pushCounter = new InstructionCounter();
+ branchCounter = new InstructionCounter();
+ deletedCounter = new InstructionCounter();
+// nopCounter = new InstructionCounter();
+ pushPopCounter = new InstructionCounter();
+ loadStoreCounter = new InstructionCounter();
+ storeLoadCounter = new InstructionCounter();
+ gotoGotoCounter = new InstructionCounter();
+ gotoReturnCounter = new InstructionCounter();
+ }
+
+ // Clean up any old visitor info.
+ programClassPool.classFilesAccept(new ClassFileCleaner());
+ libraryClassPool.classFilesAccept(new ClassFileCleaner());
+
+ // Link all methods that should get the same optimization info.
+ programClassPool.classFilesAccept(new BottomClassFileFilter(
+ new MethodInfoLinker()));
+
+ // Create a visitor for marking the seeds.
+ KeepMarker keepMarker = new KeepMarker();
+ ClassPoolVisitor classPoolvisitor =
+ new MultiClassPoolVisitor(new ClassPoolVisitor[]
+ {
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+ keepMarker,
+ keepMarker),
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
+ keepMarker,
+ keepMarker)
+ });
+
+ // Mark the seeds.
+ programClassPool.accept(classPoolvisitor);
+ libraryClassPool.accept(classPoolvisitor);
+
+ // Attach some optimization info to all methods, so it can be filled
+ // out later.
+ programClassPool.classFilesAccept(new AllMethodVisitor(
+ new MethodOptimizationInfoSetter()));
+ libraryClassPool.classFilesAccept(new AllMethodVisitor(
+ new MethodOptimizationInfoSetter()));
+
+ // Mark all interfaces that have single implementations.
+ programClassPool.classFilesAccept(new SingleImplementationMarker(configuration.allowAccessModification, singleImplementationCounter));
+
+ // Make class files and methods final, as far as possible.
+ programClassPool.classFilesAccept(new ClassFileFinalizer(finalClassCounter, finalMethodCounter));
+
+ // Mark all fields that are write-only, and mark the used local variables.
+ programClassPool.classFilesAccept(new AllMethodVisitor(
+ new AllAttrInfoVisitor(
+ new AllInstructionVisitor(
+ new MultiInstructionVisitor(
+ new InstructionVisitor[]
+ {
+ new WriteOnlyFieldMarker(),
+ new VariableUsageMarker(),
+ })))));
+
+ // Mark all fields that are write-only, and mark the used local variables.
+ if (configuration.verbose)
+ {
+ programClassPool.classFilesAccept(new AllFieldVisitor(
+ new WriteOnlyFieldFilter(
+ writeOnlyFieldCounter)));
+ }
+
+ // Mark all methods that can not be made private.
+ programClassPool.classFilesAccept(new NonPrivateMethodMarker());
+ libraryClassPool.classFilesAccept(new NonPrivateMethodMarker());
+
+ // Make all non-private and unmarked methods in final classes private.
+ programClassPool.classFilesAccept(new ClassFileAccessFilter(ClassConstants.INTERNAL_ACC_FINAL, 0,
+ new AllMethodVisitor(
+ new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+ new MethodPrivatizer(privateMethodCounter)))));
+
+ // Mark all used parameters, including the 'this' parameters.
+ programClassPool.classFilesAccept(new AllMethodVisitor(
+ new ParameterUsageMarker()));
+ libraryClassPool.classFilesAccept(new AllMethodVisitor(
+ new ParameterUsageMarker()));
+
+ if (configuration.assumeNoSideEffects != null)
+ {
+ // Create a visitor for marking methods that don't have any side effects.
+ NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
+ ClassPoolVisitor noClassPoolvisitor =
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.assumeNoSideEffects,
+ null,
+ noSideEffectMethodMarker);
+
+ // Mark the seeds.
+ programClassPool.accept(noClassPoolvisitor);
+ libraryClassPool.accept(noClassPoolvisitor);
+ }
+
+ // Mark all methods that have side effects.
+ programClassPool.accept(new SideEffectMethodMarker());
+
+ // Perform partial evaluation.
+ programClassPool.classFilesAccept(new AllMethodVisitor(
+ new EvaluationSimplifier(pushCounter, branchCounter, deletedCounter)));
+
+ // Inline interfaces with single implementations.
+ programClassPool.classFilesAccept(new SingleImplementationInliner());
+
+ // Restore the interface references from these single implementations.
+ programClassPool.classFilesAccept(new SingleImplementationFixer());
+
+ // Shrink the method parameters and make methods static.
+ programClassPool.classFilesAccept(new AllMethodVisitor(
+ new ParameterShrinker(1024, 64, parameterShrinkCounter, staticMethodCounter)));
+
+ // Fix all references to class files.
+ programClassPool.classFilesAccept(new ClassFileReferenceFixer());
+
+ // Fix all references to class members.
+ programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
+
+ // Create a branch target marker and a code attribute editor that can
+ // be reused for all code attributes.
+ BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
+ CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(1024);
+
+ // Visit all code attributes.
+ // First let the branch marker mark all branch targets.
+ // Then share common blocks of code at branches.
+ // Finally apply all changes to the code.
+ programClassPool.classFilesAccept(
+ new AllMethodVisitor(
+ new AllAttrInfoVisitor(
+ new MultiAttrInfoVisitor(
+ new AttrInfoVisitor[]
+ {
+ branchTargetFinder,
+ new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
+ new AllInstructionVisitor(
+ new GotoCommonCodeReplacer(branchTargetFinder, codeAttrInfoEditor, commonCodeCounter)),
+ codeAttrInfoEditor
+ }))));
+
+ // Visit all code attributes.
+ // First let the branch marker mark all branch targets.
+ // Then perform peephole optimisations on the instructions:
+ // - Fix invocations of methods that have become private, static,...
+ // - Remove nop instructions.
+ // - Remove push/pop instruction pairs.
+ // - Remove load/store instruction pairs.
+ // - Replace store/load instruction pairs by dup/store instructions.
+ // - Simplify branches to branch instructions.
+ // - Replace branches to return instructions by return instructions.
+ // - Inline simple getters and setters.
+ // Finally apply all changes to the code.
+ programClassPool.classFilesAccept(
+ new AllMethodVisitor(
+ new AllAttrInfoVisitor(
+ new MultiAttrInfoVisitor(
+ new AttrInfoVisitor[]
+ {
+ branchTargetFinder,
+ new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
+ new AllInstructionVisitor(
+ new MultiInstructionVisitor(
+ new InstructionVisitor[]
+ {
+ new MethodInvocationFixer( codeAttrInfoEditor),
+// new NopRemover( codeAttrInfoEditor, nopCounter),
+ new PushPopRemover( branchTargetFinder, codeAttrInfoEditor, pushPopCounter),
+ new LoadStoreRemover( branchTargetFinder, codeAttrInfoEditor, loadStoreCounter),
+ new StoreLoadReplacer( branchTargetFinder, codeAttrInfoEditor, storeLoadCounter),
+ new GotoGotoReplacer( codeAttrInfoEditor, gotoGotoCounter),
+ new GotoReturnReplacer( codeAttrInfoEditor, gotoReturnCounter),
+ new GetterSetterInliner(configuration.allowAccessModification, codeAttrInfoEditor, getterSetterCounter),
+ })),
+ codeAttrInfoEditor
+ }))));
+
+ if (configuration.verbose)
+ {
+ System.out.println(" Number of inlined interfaces: "+singleImplementationCounter.getCount());
+ System.out.println(" Number of finalized classes: "+finalClassCounter .getCount());
+ System.out.println(" Number of removed write-only fields: "+writeOnlyFieldCounter .getCount());
+ System.out.println(" Number of finalized methods: "+finalMethodCounter .getCount());
+ System.out.println(" Number of privatized methods: "+privateMethodCounter .getCount());
+ System.out.println(" Number of staticized methods: "+staticMethodCounter .getCount());
+ System.out.println(" Number of simplified method declarations: "+parameterShrinkCounter .getCount());
+ System.out.println(" Number of inlined getters/setter calls: "+getterSetterCounter .getCount());
+ System.out.println(" Number of merged code blocks: "+commonCodeCounter .getCount());
+ System.out.println(" Number of simplified push instructions: "+pushCounter .getCount());
+ System.out.println(" Number of simplified branches: "+branchCounter .getCount());
+ System.out.println(" Number of removed instructions: "+deletedCounter .getCount());
+// System.out.println(" Number of removed nop instructions: "+nopCounter .getCount());
+ System.out.println(" Number of removed push/pop pairs: "+pushPopCounter .getCount());
+ System.out.println(" Number of removed load/store pairs: "+loadStoreCounter .getCount());
+ System.out.println(" Number of simplified store/load pairs: "+storeLoadCounter .getCount());
+ System.out.println(" Number of simplified goto/goto pairs: "+gotoGotoCounter .getCount());
+ System.out.println(" Number of simplified goto/return pairs: "+gotoReturnCounter .getCount());
+ }
+ }
+}
diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java
index ca1f752..97aa151 100644
--- a/src/proguard/optimize/ParameterShrinker.java
+++ b/src/proguard/optimize/ParameterShrinker.java
@@ -1,4 +1,4 @@
-/* $Id: ParameterShrinker.java,v 1.4 2005/06/25 22:04:04 eric Exp $
+/* $Id: ParameterShrinker.java,v 1.6 2005/08/06 10:27:43 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -42,8 +42,11 @@ public class ParameterShrinker
private static final boolean DEBUG = false;
- private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+ private MemberInfoVisitor extraParameterMemberInfoVisitor;
+ private MemberInfoVisitor extraStaticMemberInfoVisitor;
+
private VariableEditor variableEditor;
+ private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
// A parameter for the parameter annotation visiting methods.
private MethodInfo methodInfo;
@@ -58,7 +61,30 @@ public class ParameterShrinker
*/
public ParameterShrinker(int codeLength, int maxLocals)
{
- variableEditor = new VariableEditor(codeLength, maxLocals);
+ this(codeLength, maxLocals, null, null);
+ }
+
+
+ /**
+ * Creates a new ParameterShrinker.
+ * @param codeLength an estimate of the maximum length of all the code
+ * that will be edited.
+ * @param maxLocals an estimate of the maximum length of all the local
+ * variable frames that will be edited.
+ * @param extraParameterMemberInfoVisitor an optional extra visitor for all
+ * methods whose parameters have been
+ * simplified.
+ * @param extraStaticMemberInfoVisitor an optional extra visitor for all
+ * methods that have been made static.
+ */
+ public ParameterShrinker(int codeLength, int maxLocals,
+ MemberInfoVisitor extraParameterMemberInfoVisitor,
+ MemberInfoVisitor extraStaticMemberInfoVisitor)
+ {
+ this.variableEditor = new VariableEditor(codeLength, maxLocals);
+
+ this.extraParameterMemberInfoVisitor = extraParameterMemberInfoVisitor;
+ this.extraStaticMemberInfoVisitor = extraStaticMemberInfoVisitor;
}
@@ -107,6 +133,12 @@ public class ParameterShrinker
// Update the descriptor.
programMethodInfo.u2descriptorIndex =
constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
+
+ // Visit the method, if required.
+ if (extraParameterMemberInfoVisitor != null)
+ {
+ extraParameterMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+ }
}
// Delete unused variables from the local variable frame.
@@ -141,6 +173,12 @@ public class ParameterShrinker
programMethodInfo.u2accessFlags =
(accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL) |
ClassConstants.INTERNAL_ACC_STATIC;
+
+ // Visit the method, if required.
+ if (extraStaticMemberInfoVisitor != null)
+ {
+ extraStaticMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+ }
}
}
@@ -211,16 +249,13 @@ public class ParameterShrinker
while (internalTypeEnumeration.hasMoreTypes())
{
String type = internalTypeEnumeration.nextType();
- if (ClassUtil.isInternalClassType(type))
+ if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
{
- if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
- {
- annotations[newAnnotationIndex++] = annotations[annotationIndex];
- }
-
- annotationIndex++;
+ annotations[newAnnotationIndex++] = annotations[annotationIndex];
}
+ annotationIndex++;
+
parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
}
@@ -286,18 +321,23 @@ public class ParameterShrinker
int parameterIndex =
(methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
0 : 1;
-
+
int referencedClassFileIndex = 0;
int newReferencedClassFileIndex = 0;
-
+
// Go over the parameters.
String descriptor = methodInfo.getDescriptor(classFile);
InternalTypeEnumeration internalTypeEnumeration =
new InternalTypeEnumeration(descriptor);
-
+
while (internalTypeEnumeration.hasMoreTypes())
{
String type = internalTypeEnumeration.nextType();
+ if (ClassUtil.isInternalArrayType(type))
+ {
+ type = ClassUtil.internalTypeFromArrayType(type);
+ }
+
if (ClassUtil.isInternalClassType(type))
{
if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
@@ -305,13 +345,28 @@ public class ParameterShrinker
referencedClassFiles[newReferencedClassFileIndex++] =
referencedClassFiles[referencedClassFileIndex];
}
-
+
referencedClassFileIndex++;
}
-
+
parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
}
-
+
+ // Also look at the return value.
+ String type = internalTypeEnumeration.returnType();
+ if (ClassUtil.isInternalArrayType(type))
+ {
+ type = ClassUtil.internalTypeFromArrayType(type);
+ }
+
+ if (ClassUtil.isInternalClassType(type))
+ {
+ referencedClassFiles[newReferencedClassFileIndex++] =
+ referencedClassFiles[referencedClassFileIndex];
+
+ referencedClassFileIndex++;
+ }
+
// Clear the unused entries.
while (newReferencedClassFileIndex < referencedClassFileIndex)
{
diff --git a/src/proguard/optimize/peephole/MethodPrivatizer.java b/src/proguard/optimize/WriteOnlyFieldFilter.java
similarity index 59%
copy from src/proguard/optimize/peephole/MethodPrivatizer.java
copy to src/proguard/optimize/WriteOnlyFieldFilter.java
index 0578abd..0ceccdd 100644
--- a/src/proguard/optimize/peephole/MethodPrivatizer.java
+++ b/src/proguard/optimize/WriteOnlyFieldFilter.java
@@ -1,4 +1,4 @@
-/* $Id: MethodPrivatizer.java,v 1.5 2005/06/11 13:21:35 eric Exp $
+/* $Id: WriteOnlyFieldFilter.java,v 1.1 2005/08/13 21:25:26 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -18,41 +18,48 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-package proguard.optimize.peephole;
+package proguard.optimize;
import proguard.classfile.*;
-import proguard.classfile.util.AccessUtil;
import proguard.classfile.visitor.MemberInfoVisitor;
-import proguard.optimize.NonPrivateMethodMarker;
/**
- * This MemberInfoVisitor makes all final methods that it visits private,
- * unless they have been marked by a NonPrivateMethodMarker.
+ * This <code>MemberInfoVisitor</code> delegates its visits to program fields to
+ * another given <code>MemberInfoVisitor</code>, but only when the visited
+ * field has been marked as write-only.
*
- * @see NonPrivateMethodMarker
+ * @see WriteOnlyFieldMarker#isWriteOnly(VisitorAccepter)
* @author Eric Lafortune
*/
-public class MethodPrivatizer
+public class WriteOnlyFieldFilter
implements MemberInfoVisitor
{
- // Implementations for MemberInfoVisitor.
+ private MemberInfoVisitor memberInfoVisitor;
- public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
- public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+ /**
+ * Creates a new WriteOnlyFieldFilter.
+ * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
+ * visits will be delegated.
+ */
+ public WriteOnlyFieldFilter(MemberInfoVisitor memberInfoVisitor)
{
- int accessFlags = programMethodInfo.getAccessFlags();
+ this.memberInfoVisitor = memberInfoVisitor;
+ }
- // Is the method unmarked?
- if (NonPrivateMethodMarker.canBeMadePrivate(programMethodInfo))
+
+ // Implementations for MemberInfoVisitor.
+
+ public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+ {
+ if (WriteOnlyFieldMarker.isWriteOnly(programFieldInfo))
{
- // Make the method private.
- programMethodInfo.u2accessFlags =
- AccessUtil.replaceAccessFlags(accessFlags,
- ClassConstants.INTERNAL_ACC_PRIVATE);
+ memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
}
}
+
+ public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo) {}
public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
}
diff --git a/src/proguard/optimize/WriteOnlyFieldMarker.java b/src/proguard/optimize/WriteOnlyFieldMarker.java
index 348fae5..a054bca 100644
--- a/src/proguard/optimize/WriteOnlyFieldMarker.java
+++ b/src/proguard/optimize/WriteOnlyFieldMarker.java
@@ -1,4 +1,4 @@
-/* $Id: WriteOnlyFieldMarker.java,v 1.6 2005/06/11 13:13:16 eric Exp $
+/* $Id: WriteOnlyFieldMarker.java,v 1.7 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -108,7 +108,7 @@ public class WriteOnlyFieldMarker
// Hasn't the field been marked as being read yet?
if (!isRead(programFieldInfo))
{
- // Mark it now, depending on whther this is a reading operation
+ // Mark it now, depending on whether this is a reading operation
// or a writing operation.
if (reading)
{
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
similarity index 50%
copy from src/proguard/optimize/evaluation/PartialEvaluator.java
copy to src/proguard/optimize/evaluation/EvaluationSimplifier.java
index acca7d0..1de4b8a 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -1,4 +1,4 @@
-/* $Id: PartialEvaluator.java,v 1.32 2005/06/11 13:13:16 eric Exp $
+/* $Id: EvaluationSimplifier.java,v 1.4 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -36,12 +36,10 @@ import proguard.optimize.evaluation.value.*;
*
* @author Eric Lafortune
*/
-public class PartialEvaluator
+public class EvaluationSimplifier
implements MemberInfoVisitor,
AttrInfoVisitor,
- ExceptionInfoVisitor,
- InstructionVisitor,
- CpInfoVisitor
+ InstructionVisitor
{
//*
private static final boolean DEBUG_RESULTS = false;
@@ -56,34 +54,47 @@ implements MemberInfoVisitor,
private static final int INITIAL_CODE_LENGTH = 1024;
private static final int INITIAL_VALUE_COUNT = 32;
- //private static final int MAXIMUM_EVALUATION_COUNT = 100;
-
- private static final int AT_METHOD_ENTRY = -1;
- private static final int AT_CATCH_ENTRY = -1;
- private static final int NONE = -1;
-
- private InstructionOffsetValue[] varTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] stackTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] unusedTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private TracedVariables[] vars = new TracedVariables[INITIAL_CODE_LENGTH];
- private TracedStack[] stacks = new TracedStack[INITIAL_CODE_LENGTH];
- private int[] evaluationCounts = new int[INITIAL_CODE_LENGTH];
- private int[] initializedVariable = new int[INITIAL_CODE_LENGTH];
- private boolean[] isNecessary = new boolean[INITIAL_CODE_LENGTH];
- private boolean evaluateExceptions;
-
- private TracedVariables parameters = new TracedVariables(INITIAL_VALUE_COUNT);
- private TracedVariables variables = new TracedVariables(INITIAL_VALUE_COUNT);
- private TracedStack stack = new TracedStack(INITIAL_VALUE_COUNT);
- private TracedBranchUnit branchUnit = new TracedBranchUnit();
+ private InstructionVisitor extraPushInstructionVisitor;
+ private InstructionVisitor extraBranchInstructionVisitor;
+ private InstructionVisitor extraDeletedInstructionVisitor;
- private ClassFileCleaner classFileCleaner = new ClassFileCleaner();
+ private PartialEvaluator partialEvaluator = new PartialEvaluator();
private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
+ private ClassFileCleaner classFileCleaner = new ClassFileCleaner();
private CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(INITIAL_CODE_LENGTH);
- private boolean isInitializer;
+
+ private Variables parameters = new TracedVariables(INITIAL_VALUE_COUNT);
+ private boolean[] isNecessary = new boolean[INITIAL_CODE_LENGTH];
+ private boolean[] isSimplified = new boolean[INITIAL_CODE_LENGTH];
+
+
+ /**
+ * Creates a new EvaluationSimplifier.
+ */
+ public EvaluationSimplifier()
+ {
+ this(null, null, null);
+ }
+
+
+ /**
+ * Creates a new EvaluationSimplifier.
+ * @param extraPushInstructionVisitor an optional extra visitor for all
+ * simplified push instructions.
+ * @param extraBranchInstructionVisitor an optional extra visitor for all
+ * simplified branch instructions.
+ * @param extraDeletedInstructionVisitor an optional extra visitor for all
+ * deleted instructions.
+ */
+ public EvaluationSimplifier(InstructionVisitor extraPushInstructionVisitor,
+ InstructionVisitor extraBranchInstructionVisitor,
+ InstructionVisitor extraDeletedInstructionVisitor)
+ {
+ this.extraPushInstructionVisitor = extraPushInstructionVisitor;
+ this.extraBranchInstructionVisitor = extraBranchInstructionVisitor;
+ this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor;
+ }
// Implementations for MemberInfoVisitor.
@@ -97,50 +108,6 @@ implements MemberInfoVisitor,
// programClassFile.getName().equals("abc/Def") &&
// programMethodInfo.getName(programClassFile).equals("abc");
- // Initialize the parameters.
- boolean isStatic =
- (programMethodInfo.u2accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0;
-
- // Count the number of parameters, taking into account their Categories.
- String parameterDescriptor = programMethodInfo.getDescriptor(programClassFile);
- int parameterSize = (isStatic ? 0 : 1) +
- ClassUtil.internalMethodParameterSize(parameterDescriptor);
-
- // Reuse the existing parameters object, ensuring the right size.
- parameters.reset(parameterSize);
-
- // Go over the parameters again.
- InternalTypeEnumeration internalTypeEnumeration =
- new InternalTypeEnumeration(parameterDescriptor);
-
- int index = 0;
-
- // Clear the store value of each parameter.
- parameters.setStoreValue(InstructionOffsetValueFactory.create(AT_METHOD_ENTRY));
-
- // Put the caller's reference in parameter 0.
- if (!isStatic)
- {
- parameters.store(index++, ReferenceValueFactory.create(false));
- }
-
- while (internalTypeEnumeration.hasMoreTypes())
- {
- String type = internalTypeEnumeration.nextType();
-
- // Get a generic corresponding value.
- Value value = ValueFactory.create(type);
-
- // Store the value in the parameter.
- parameters.store(index, value);
-
- // Increment the index according to the Category of the value.
- index += value.isCategory2() ? 2 : 1;
- }
-
- // Reset the return value.
- branchUnit.setTraceReturnValue(null);
-
try
{
// Process the code.
@@ -159,21 +126,6 @@ implements MemberInfoVisitor,
throw ex;
}
}
-
- if (DEBUG)
- {
- Value returnValue = branchUnit.getTraceReturnValue();
- if (returnValue != null)
- {
- System.out.println("Return value for method "+
- ClassUtil.externalFullMethodDescription(programClassFile.getName(),
- 0,
- programMethodInfo.getName(programClassFile),
- programMethodInfo.getDescriptor(programClassFile))+
- " -> ["+returnValue.toString()+"]");
- System.out.println();
- }
- }
}
@@ -216,154 +168,85 @@ implements MemberInfoVisitor,
System.out.println(" Params:"+parameters);
}
- int codeLength = codeAttrInfo.u4codeLength;
-
- // Reset the code changes.
- codeAttrInfoEditor.reset(codeLength);
-
- if (DEBUG)
- {
- System.out.println(" Max locals = "+codeAttrInfo.u2maxLocals);
- System.out.println(" Max stack = "+codeAttrInfo.u2maxStack);
- }
-
- // Create new arrays for storing a stack and a set of variables at each
- // branch target.
- if (isNecessary.length < codeLength)
- {
- varTraceValues = new InstructionOffsetValue[codeLength];
- stackTraceValues = new InstructionOffsetValue[codeLength];
- unusedTraceValues = new InstructionOffsetValue[codeLength];
- branchOriginValues = new InstructionOffsetValue[codeLength];
- branchTargetValues = new InstructionOffsetValue[codeLength];
- vars = new TracedVariables[codeLength];
- stacks = new TracedStack[codeLength];
- evaluationCounts = new int[codeLength];
- initializedVariable = new int[codeLength];
- isNecessary = new boolean[codeLength];
-
- for (int index = 0; index < codeLength; index++)
- {
- initializedVariable[index] = NONE;
- }
- }
- else
- {
- for (int index = 0; index < codeLength; index++)
- {
- varTraceValues[index] = null;
- stackTraceValues[index] = null;
- unusedTraceValues[index] = null;
- branchOriginValues[index] = null;
- branchTargetValues[index] = null;
- evaluationCounts[index] = 0;
- initializedVariable[index] = NONE;
- isNecessary[index] = false;
-
- if (vars[index] != null)
- {
- vars[index].reset(codeAttrInfo.u2maxLocals);
- }
-
- if (stacks[index] != null)
- {
- stacks[index].reset(codeAttrInfo.u2maxStack);
- }
- }
- }
-
- // Reuse the existing variables and stack objects, ensuring the right size.
- variables.reset(codeAttrInfo.u2maxLocals);
- stack.reset(codeAttrInfo.u2maxStack);
+ // Initialize the method parameters.
+ initializeParameters(classFile, methodInfo, codeAttrInfo);
- // Initialize the local variables with the parameters.
- variables.initialize(parameters);
+ // Initialize the necessary array.
+ initializeNecessary(codeAttrInfo);
- // Evaluate the instructions, starting at the entry point.
- if (DEBUG) System.out.println("Partial evaluation: ");
-
- evaluateInstructionBlock(classFile,
- methodInfo,
- codeAttrInfo,
- variables,
- stack,
- branchUnit,
- 0);
-
- // Evaluate the exception catch blocks, until their entry variables
- // have stabilized.
- do
- {
- // Reset the flag to stop evaluating.
- evaluateExceptions = false;
-
- // Evaluate all relevant exception catch blocks once.
- codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
- }
- while (evaluateExceptions);
+ // Evaluate the method.
+ Value returnValue = partialEvaluator.evaluate(classFile,
+ methodInfo,
+ codeAttrInfo,
+ parameters);
// Clean up the visitor information in the exceptions right away.
codeAttrInfo.exceptionsAccept(classFile, methodInfo, classFileCleaner);
+ int codeLength = codeAttrInfo.u4codeLength;
+
+ // Reset the code changes.
+ codeAttrInfoEditor.reset(codeLength);
+
// Replace any instructions that can be simplified.
if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
-
// Mark all essential instructions that have been encountered as used.
if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
+
// The invocation of the "super" or "this" <init> method inside a
// constructor is always necessary, even if it is assumed to have no
// side effects.
boolean markSuperOrThis =
methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
- int aload0Index = 0;
+ int aload0Offset = 0;
- int index = 0;
+ int offset = 0;
do
{
- if (isTraced(index))
+ if (partialEvaluator.isTraced(offset))
{
Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
- index);
+ offset);
// Remember the most recent aload0 instruction index.
if (instruction.opcode == InstructionConstants.OP_ALOAD_0)
{
- aload0Index = index;
+ aload0Offset = offset;
}
- // Mark the instruction as necessary if it is the first
+ // Mark that the instruction is necessary if it is the first
// invocation of the "super" or "this" <init> method
// inside a constructor.
else if (markSuperOrThis &&
instruction.opcode == InstructionConstants.OP_INVOKESPECIAL &&
- stackTraceValues[index].contains(aload0Index))
+ partialEvaluator.stackProducerOffsets(offset).contains(aload0Offset))
{
markSuperOrThis = false;
- if (DEBUG_ANALYSIS) System.out.print(index+",");
- isNecessary[index] = true;
+ if (DEBUG_ANALYSIS) System.out.print(offset +",");
+ isNecessary[offset] = true;
}
- // Mark the instruction as necessary if it has side effects.
+ // Mark that the instruction is necessary if it has side effects.
else if (sideEffectInstructionChecker.hasSideEffects(classFile,
methodInfo,
codeAttrInfo,
- index,
+ offset,
instruction))
{
- if (DEBUG_ANALYSIS) System.out.print(index+",");
- isNecessary[index] = true;
+ if (DEBUG_ANALYSIS) System.out.print(offset +",");
+ isNecessary[offset] = true;
}
}
- index++;
+ offset++;
}
- while (index < codeLength);
+ while (offset < codeLength);
if (DEBUG_ANALYSIS) System.out.println();
@@ -373,227 +256,249 @@ implements MemberInfoVisitor,
// higher, previously unmarked instruction that is being marked.
if (DEBUG_ANALYSIS) System.out.println("Usage marking:");
- int lowestNecessaryIndex = codeLength;
- index = codeLength - 1;
+ int lowestNecessaryOffset = codeLength;
+ offset = codeLength - 1;
do
{
- int nextIndex = index - 1;
+ int nextOffset = offset - 1;
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Check if this instruction is a branch origin from a branch that
// straddles some marked code.
- nextIndex = markStraddlingBranches(index,
- branchTargetValues[index],
- true,
- lowestNecessaryIndex,
- nextIndex);
-
- // Mark the instructions on which this instruction depends.
- nextIndex = markDependencies(index,
- nextIndex);
+ nextOffset = markStraddlingBranches(offset,
+ partialEvaluator.branchTargets(offset),
+ true,
+ lowestNecessaryOffset,
+ nextOffset);
+
+ // Mark the producers on which this instruction depends.
+ if (isNecessaryConsumer(offset))
+ {
+ nextOffset = markProducers(offset,
+ nextOffset);
+ }
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Check if this instruction is a branch target from a branch that
// straddles some marked code.
- nextIndex = markStraddlingBranches(index,
- branchOriginValues[index],
- false,
- lowestNecessaryIndex,
- nextIndex);
+ nextOffset = markStraddlingBranches(offset,
+ partialEvaluator.branchOrigins(offset),
+ false,
+ lowestNecessaryOffset,
+ nextOffset);
if (DEBUG_ANALYSIS)
{
- if (nextIndex >= index)
+ if (nextOffset >= offset)
{
System.out.println();
}
}
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Update the index of the instruction to be investigated next.
- index = nextIndex;
+ offset = nextOffset;
}
- while (index >= 0);
+ while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
// Insert pop instructions where necessary, to keep the stack consistent.
if (DEBUG_ANALYSIS) System.out.println("Stack consistency marking:");
- index = codeLength - 1;
+ offset = codeLength - 1;
do
{
- if (isTraced(index))
+ if (partialEvaluator.isTraced(offset) &&
+ !isDupOrSwap(codeAttrInfo.code[offset]))
{
// Make sure the stack is always consistent at this offset.
fixStackConsistency(classFile,
codeAttrInfo,
- index);
+ offset);
}
- index--;
+ offset--;
}
- while (index >= 0);
+ while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
// Fix dup/swap instructions where necessary, to keep the stack consistent.
- if (DEBUG_ANALYSIS) System.out.println("Dup/swap fixing:");
+ if (DEBUG_ANALYSIS) System.out.println("Dup/swap marking and fixing:");
- index = 0;
+ offset = codeLength - 1;
do
{
- if (isNecessary[index])
+ if (partialEvaluator.isTraced(offset) &&
+ isDupOrSwap(codeAttrInfo.code[offset]))
{
// Make sure any dup/swap instructions are always consistent at this offset.
fixDupInstruction(codeAttrInfo,
- index);
+ offset);
}
- index++;
+ offset--;
}
- while (index < codeLength);
+ while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
// Mark branches straddling just inserted push/pop instructions.
if (DEBUG_ANALYSIS) System.out.println("Final straddling branch marking:");
- lowestNecessaryIndex = codeLength;
- index = codeLength - 1;
+ lowestNecessaryOffset = codeLength;
+ offset = codeLength - 1;
do
{
- int nextIndex = index - 1;
+ int nextOffset = offset - 1;
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Check if this instruction is a branch origin from a branch that
// straddles some marked code.
- nextIndex = markAndSimplifyStraddlingBranches(index,
- branchTargetValues[index],
- lowestNecessaryIndex,
- nextIndex);
+ nextOffset = markAndSimplifyStraddlingBranches(offset,
+ partialEvaluator.branchTargets(offset),
+ lowestNecessaryOffset,
+ nextOffset);
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Check if this instruction is a branch target from a branch that
// straddles some marked code.
- nextIndex = markAndSimplifyStraddlingBranches(branchOriginValues[index],
- index,
- lowestNecessaryIndex,
- nextIndex);
+ nextOffset = markAndSimplifyStraddlingBranches(partialEvaluator.branchOrigins(offset),
+ offset,
+ lowestNecessaryOffset,
+ nextOffset);
if (DEBUG_ANALYSIS)
{
- if (nextIndex >= index)
+ if (nextOffset >= offset)
{
System.out.println();
}
}
// Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
+ if (isNecessary[offset])
{
- lowestNecessaryIndex = index;
+ lowestNecessaryOffset = offset;
}
// Update the index of the instruction to be investigated next.
- index = nextIndex;
+ offset = nextOffset;
}
- while (index >= 0);
+ while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
// Mark variable initializations, even if they aren't strictly necessary.
- // The virtual machine is not smart enough to see this, and may complain
- // otherwise.
+ // The virtual machine's verification step is not smart enough to see
+ // this, and may complain otherwise.
if (DEBUG_ANALYSIS) System.out.println("Initialization marking: ");
- index = 0;
+ offset = 0;
do
{
// Is it an initialization that hasn't been marked yet, and whose
// corresponding variable is used for storage?
- int variableIndex = initializedVariable[index];
- if (variableIndex != NONE &&
- !isNecessary[index] &&
+ int variableIndex = partialEvaluator.initializedVariable(offset);
+ if (variableIndex != PartialEvaluator.NONE &&
+ !isNecessary[offset] &&
isVariableReferenced(codeAttrInfo, variableIndex))
{
- if (DEBUG_ANALYSIS) System.out.println(index+",");
+ if (DEBUG_ANALYSIS) System.out.println(offset +",");
// Figure out what kind of initialization value has to be stored.
- int pushComputationalType = vars[index].load(variableIndex).computationalType();
- increaseStackSize(index, pushComputationalType, false);
+ int pushComputationalType = partialEvaluator.variableValue(offset, variableIndex).computationalType();
+ increaseStackSize(offset, pushComputationalType, false);
}
- index++;
+ offset++;
}
- while (index < codeLength);
+ while (offset < codeLength);
if (DEBUG_ANALYSIS) System.out.println();
if (DEBUG_RESULTS)
{
- System.out.println("Results:");
- int offset = 0;
+ System.out.println("Simplification results:");
+ offset = 0;
do
{
Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
offset);
System.out.println((isNecessary[offset] ? " + " : " - ")+instruction.toString(offset));
- if (isTraced(offset))
+
+ if (partialEvaluator.isTraced(offset))
{
- if (varTraceValues[offset] != null &&
- varTraceValues[offset].instructionOffsetCount() > 0)
+ InstructionOffsetValue varProducerOffsets = partialEvaluator.varProducerOffsets(offset);
+ if (varProducerOffsets.instructionOffsetCount() > 0)
+ {
+ System.out.println(" has overall been using information from instructions setting vars: "+varProducerOffsets);
+ }
+
+ InstructionOffsetValue stackProducerOffsets = partialEvaluator.stackProducerOffsets(offset);
+ if (stackProducerOffsets.instructionOffsetCount() > 0)
{
- System.out.println(" has overall been using information from instructions setting vars: "+varTraceValues[offset]);
+ System.out.println(" has overall been using information from instructions setting stack: "+stackProducerOffsets);
}
- if (stackTraceValues[offset] != null &&
- stackTraceValues[offset].instructionOffsetCount() > 0)
+
+ InstructionOffsetValue unusedProducerOffsets = partialEvaluator.unusedProducerOffsets(offset);
+ if (unusedProducerOffsets.instructionOffsetCount() > 0)
{
- System.out.println(" has overall been using information from instructions setting stack: "+stackTraceValues[offset]);
+ System.out.println(" no longer needs information from instructions setting stack: "+unusedProducerOffsets);
}
- if (branchTargetValues[offset] != null)
+
+ InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+ if (branchTargets != null)
{
- System.out.println(" has overall been branching to "+branchTargetValues[offset]);
+ System.out.println(" has overall been branching to "+branchTargets);
}
- if (codeAttrInfoEditor.preInsertions[offset] != null)
+
+ Instruction preInsertion = codeAttrInfoEditor.preInsertions[offset];
+ if (preInsertion != null)
{
- System.out.println(" is preceded by: "+codeAttrInfoEditor.preInsertions[offset]);
+ System.out.println(" is preceded by: "+preInsertion);
}
- if (codeAttrInfoEditor.replacements[offset] != null)
+
+ Instruction replacement = codeAttrInfoEditor.replacements[offset];
+ if (replacement != null)
{
- System.out.println(" is replaced by: "+codeAttrInfoEditor.replacements[offset]);
+ System.out.println(" is replaced by: "+replacement);
}
- if (codeAttrInfoEditor.postInsertions[offset] != null)
+
+ Instruction postInsertion = codeAttrInfoEditor.postInsertions[offset];
+ if (postInsertion != null)
{
- System.out.println(" is followed by: "+codeAttrInfoEditor.postInsertions[offset]);
+ System.out.println(" is followed by: "+postInsertion);
}
- System.out.println(" Vars: "+vars[offset]);
- System.out.println(" Stack: "+stacks[offset]);
+
+ //System.out.println(" Vars: "+vars[offset]);
+ //System.out.println(" Stack: "+stacks[offset]);
}
offset += instruction.length(offset);
@@ -602,7 +507,7 @@ implements MemberInfoVisitor,
}
// Delete all instructions that are not used.
- int offset = 0;
+ offset = 0;
do
{
Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
@@ -614,6 +519,12 @@ implements MemberInfoVisitor,
codeAttrInfoEditor.insertBeforeInstruction(offset, null);
codeAttrInfoEditor.replaceInstruction(offset, null);
codeAttrInfoEditor.insertAfterInstruction(offset, null);
+
+ // Visit the instruction, if required.
+ if (extraDeletedInstructionVisitor != null)
+ {
+ instruction.accept(classFile, methodInfo, codeAttrInfo, offset, extraDeletedInstructionVisitor);
+ }
}
offset += instruction.length(offset);
@@ -626,142 +537,137 @@ implements MemberInfoVisitor,
/**
- * Marks the instructions at the given offsets, if the current instruction
- * itself has been marked.
- * @param index the offset of the current instruction.
- * @param nextIndex the index of the instruction to be investigated next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal, because instructions are
- * investigated starting at the highest index.
+ * Marks the producers at the given offsets.
+ * @param consumerOffset the offset of the consumer.
+ * @param nextOffset the offset of the instruction to be investigated next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
+ * instructions are investigated starting at the highest index.
*/
- private int markDependencies(int index,
- int nextIndex)
+ private int markProducers(int consumerOffset,
+ int nextOffset)
{
- if (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index))
- {
- if (DEBUG_ANALYSIS) System.out.print(index);
+ if (DEBUG_ANALYSIS) System.out.print(consumerOffset);
- // Mark all instructions whose variable values are used.
- nextIndex = markDependencies(varTraceValues[index], nextIndex);
+ // Mark all instructions whose variable values are used.
+ nextOffset = markProducers(partialEvaluator.varProducerOffsets(consumerOffset), nextOffset);
- // Mark all instructions whose stack values are used.
- nextIndex = markDependencies(stackTraceValues[index], nextIndex);
+ // Mark all instructions whose stack values are used.
+ nextOffset = markProducers(partialEvaluator.stackProducerOffsets(consumerOffset), nextOffset);
- if (DEBUG_ANALYSIS) System.out.print(",");
- }
+ if (DEBUG_ANALYSIS) System.out.print(",");
- return nextIndex;
+ return nextOffset;
}
/**
* Marks the instructions at the given offsets.
- * @param traceOffsetValue the offsets of the instructions to be marked.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal, because instructions are
- * investigated starting at the highest index.
+ * @param producerOffsets the offsets of the producers to be marked.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
+ * instructions are investigated starting at the highest index.
*/
- private int markDependencies(InstructionOffsetValue traceOffsetValue,
- int nextIndex)
+ private int markProducers(InstructionOffsetValue producerOffsets,
+ int nextOffset)
{
- if (traceOffsetValue != null)
+ if (producerOffsets != null)
{
- int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
- for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
+ int offsetCount = producerOffsets.instructionOffsetCount();
+ for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
// Has the other instruction been marked yet?
- int traceOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
- if (traceOffset > AT_METHOD_ENTRY &&
- !isNecessary[traceOffset])
+ int offset = producerOffsets.instructionOffset(offsetIndex);
+ if (offset > PartialEvaluator.AT_METHOD_ENTRY &&
+ !isNecessary[offset])
{
- if (DEBUG_ANALYSIS) System.out.print("["+traceOffset+"]");
+ if (DEBUG_ANALYSIS) System.out.print("["+offset +"]");
// Mark it.
- isNecessary[traceOffset] = true;
+ isNecessary[offset] = true;
// Restart at this instruction if it has a higher offset.
- if (nextIndex < traceOffset)
+ if (nextOffset < offset)
{
if (DEBUG_ANALYSIS) System.out.print("!");
- nextIndex = traceOffset;
+ nextOffset = offset;
}
}
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Marks the branch instructions of straddling branches, if they straddle
* some code that has been marked.
- * @param index the offset of the branch origin or branch target.
- * @param branchValue the offsets of the straddling branch targets
- * or branch origins.
- * @param isPointingToTargets <code>true</code> if the above offsets are
- * branch targets, <code>false</code> if they
- * are branch origins.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
+ * @param index the offset of the branch origin or branch target.
+ * @param branchOffsets the offsets of the straddling branch targets
+ * or branch origins.
+ * @param isPointingToTargets <code>true</code> if the above offsets are
+ * branch targets, <code>false</code> if they
+ * are branch origins.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
* instructions are investigated starting at the highest index.
*/
private int markStraddlingBranches(int index,
- InstructionOffsetValue branchValue,
+ InstructionOffsetValue branchOffsets,
boolean isPointingToTargets,
- int lowestNecessaryIndex,
- int nextIndex)
+ int lowestNecessaryOffset,
+ int nextOffset)
{
- if (branchValue != null)
+ if (branchOffsets != null)
{
// Loop over all branch origins.
- int branchCount = branchValue.instructionOffsetCount();
+ int branchCount = branchOffsets.instructionOffsetCount();
for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
{
// Is the branch straddling any necessary instructions?
- int branch = branchValue.instructionOffset(branchIndex);
+ int branch = branchOffsets.instructionOffset(branchIndex);
// Is the offset pointing to a branch origin or to a branch target?
- nextIndex = isPointingToTargets ?
- markStraddlingBranch(index, branch, lowestNecessaryIndex, nextIndex) :
- markStraddlingBranch(branch, index, lowestNecessaryIndex, nextIndex);
+ nextOffset = isPointingToTargets ?
+ markStraddlingBranch(index, branch, lowestNecessaryOffset, nextOffset) :
+ markStraddlingBranch(branch, index, lowestNecessaryOffset, nextOffset);
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Marks the given branch instruction, if it straddles some code that has
* been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
+ * @param branchOrigin the branch origin.
+ * @param branchTarget the branch target.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
* instructions are investigated starting at the highest index.
*/
private int markStraddlingBranch(int branchOrigin,
int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ int lowestNecessaryOffset,
+ int nextOffset)
{
// Has the branch origin been marked yet, and is it straddling the
// lowest necessary instruction?
if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
+ isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryOffset))
{
if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
@@ -769,35 +675,35 @@ implements MemberInfoVisitor,
isNecessary[branchOrigin] = true;
// Restart at the branch origin if it has a higher offset.
- if (nextIndex < branchOrigin)
+ if (nextOffset < branchOrigin)
{
if (DEBUG_ANALYSIS) System.out.print("!");
- nextIndex = branchOrigin;
+ nextOffset = branchOrigin;
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Marks and simplifies the branch instructions of straddling branches,
* if they straddle some code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTargets the branch targets.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
+ * @param branchOrigin the branch origin.
+ * @param branchTargets the branch targets.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
* instructions are investigated starting at the highest index.
*/
private int markAndSimplifyStraddlingBranches(int branchOrigin,
InstructionOffsetValue branchTargets,
- int lowestNecessaryIndex,
- int nextIndex)
+ int lowestNecessaryOffset,
+ int nextOffset)
{
if (branchTargets != null &&
!isNecessary[branchOrigin])
@@ -813,40 +719,40 @@ implements MemberInfoVisitor,
if (!isStraddlingBranch(branchOrigin,
branchTarget,
- lowestNecessaryIndex))
+ lowestNecessaryOffset))
{
- return nextIndex;
+ return nextOffset;
}
}
- nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTargets.instructionOffset(0),
- lowestNecessaryIndex,
- nextIndex);
+ nextOffset = markAndSimplifyStraddlingBranch(branchOrigin,
+ branchTargets.instructionOffset(0),
+ lowestNecessaryOffset,
+ nextOffset);
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Marks and simplifies the branch instructions of straddling branches,
* if they straddle some code that has been marked.
- * @param branchOrigins the branch origins.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
+ * @param branchOrigins the branch origins.
+ * @param branchTarget the branch target.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
* instructions are investigated starting at the highest index.
*/
private int markAndSimplifyStraddlingBranches(InstructionOffsetValue branchOrigins,
int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ int lowestNecessaryOffset,
+ int nextOffset)
{
if (branchOrigins != null)
{
@@ -857,39 +763,39 @@ implements MemberInfoVisitor,
// Is the branch straddling any necessary instructions?
int branchOrigin = branchOrigins.instructionOffset(branchIndex);
- nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTarget,
- lowestNecessaryIndex,
- nextIndex);
+ nextOffset = markAndSimplifyStraddlingBranch(branchOrigin,
+ branchTarget,
+ lowestNecessaryOffset,
+ nextOffset);
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Marks and simplifies the given branch instruction, if it straddles some
* code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
+ * @param branchOrigin the branch origin.
+ * @param branchTarget the branch target.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
+ * @param nextOffset the offset of the instruction to be investigated
+ * next.
+ * @return the updated offset of the instruction to be investigated next.
+ * It is always greater than or equal the original offset, because
* instructions are investigated starting at the highest index.
*/
private int markAndSimplifyStraddlingBranch(int branchOrigin,
int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ int lowestNecessaryOffset,
+ int nextOffset)
{
// Has the branch origin been marked yet, and is it straddling the
// lowest necessary instruction?
if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
+ isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryOffset))
{
if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
@@ -905,126 +811,130 @@ implements MemberInfoVisitor,
replacementInstruction);
// Restart at the branch origin if it has a higher offset.
- if (nextIndex < branchOrigin)
+ if (nextOffset < branchOrigin)
{
if (DEBUG_ANALYSIS) System.out.print("!");
- nextIndex = branchOrigin;
+ nextOffset = branchOrigin;
}
}
- return nextIndex;
+ return nextOffset;
}
/**
* Returns whether the given branch straddling some code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
+ * @param branchOrigin the branch origin.
+ * @param branchTarget the branch target.
+ * @param lowestNecessaryOffset the lowest offset of all instructions marked
+ * so far.
*/
private boolean isStraddlingBranch(int branchOrigin,
int branchTarget,
- int lowestNecessaryIndex)
+ int lowestNecessaryOffset)
{
- return branchOrigin <= lowestNecessaryIndex ^
- branchTarget <= lowestNecessaryIndex;
+ return branchOrigin <= lowestNecessaryOffset ^
+ branchTarget <= lowestNecessaryOffset;
}
/**
* Inserts pop instructions where necessary, in order to make sure the
* stack is consistent at the given index.
- * @param classFile the class file that is being checked.
- * @param codeAttrInfo the code that is being checked.
- * @param index the offset of the dependent instruction.
+ * @param classFile the class file that is being checked.
+ * @param codeAttrInfo the code that is being checked.
+ * @param consumerOffset the offset of the consumer instruction.
*/
private void fixStackConsistency(ClassFile classFile,
CodeAttrInfo codeAttrInfo,
- int index)
+ int consumerOffset)
{
// See if we have any values pushed on the stack that we aren't using.
- InstructionOffsetValue traceOffsetValue = unusedTraceValues[index];
+ InstructionOffsetValue producerOffsets = partialEvaluator.unusedProducerOffsets(consumerOffset);
// This includes all values if the popping instruction isn't necessary at all.
- boolean isNotNecessary = !isNecessary[index];
+ boolean isNotNecessary = !isNecessaryConsumer(consumerOffset);
if (isNotNecessary)
{
- traceOffsetValue = traceOffsetValue.generalize(stackTraceValues[index]).instructionOffsetValue();
+ producerOffsets = producerOffsets.generalize(partialEvaluator.stackProducerOffsets(consumerOffset)).instructionOffsetValue();
}
// Do we have any pushing instructions?
- if (traceOffsetValue.instructionOffsetCount() > 0)
+ if (producerOffsets.instructionOffsetCount() > 0)
{
// Is this instruction really popping any values?
- // Note that dup instructions have a pop count of 0.
- // Also note that method invocations have their original pop counts,
+ // Note that method invocations have their original pop counts,
// including any unused parameters.
Instruction popInstruction = InstructionFactory.create(codeAttrInfo.code,
- index);
+ consumerOffset);
int popCount = popInstruction.stackPopCount(classFile);
if (popCount > 0)
{
// Can we pop all values at the popping instruction?
if (isNotNecessary &&
popCount <= 6 &&
- isAllNecessary(traceOffsetValue))
+ isAllNecessary(producerOffsets))
{
// Is the popping instruction a simple pop or pop2 instruction?
byte popOpcode = popInstruction.opcode;
if (popOpcode == InstructionConstants.OP_POP ||
popOpcode == InstructionConstants.OP_POP2)
{
- if (DEBUG_ANALYSIS) System.out.println(" Popping value again at "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
+ if (DEBUG_ANALYSIS) System.out.println(" Popping value again at "+popInstruction.toString(consumerOffset)+" (pushed at all "+producerOffsets.instructionOffsetCount()+" offsets)");
// Simply mark the pop or pop2 instruction.
- this.isNecessary[index] = true;
+ isNecessary[consumerOffset] = true;
}
else
{
- if (DEBUG_ANALYSIS) System.out.println(" Popping value instead of "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
+ if (DEBUG_ANALYSIS) System.out.println(" Popping value instead of "+popInstruction.toString(consumerOffset)+" (pushed at all "+producerOffsets.instructionOffsetCount()+" offsets)");
// Make sure the pushed value is popped again,
// right before this instruction.
- decreaseStackSize(index, popCount, true, isNotNecessary);
+ decreaseStackSize(consumerOffset, popCount, true, isNotNecessary);
}
}
- else if (isAnyNecessary(traceOffsetValue))
+ else if (isAnyNecessary(producerOffsets))
{
// Pop the values right after the pushing instructions.
- if (DEBUG_ANALYSIS) System.out.println(" Popping value somewhere before "+index+" (pushed at some of "+traceOffsetValue.instructionOffsetCount()+" offsets):");
+ if (DEBUG_ANALYSIS) System.out.println(" Popping value somewhere before "+consumerOffset+" (pushed at some of "+producerOffsets.instructionOffsetCount()+" offsets):");
// Go over all stack pushing instructions.
- int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
- for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
+ int producerCount = producerOffsets.instructionOffsetCount();
+ for (int producerIndex = 0; producerIndex < producerCount; producerIndex++)
{
// Has the push instruction been marked?
- int pushInstructionOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
- if (this.isNecessary[pushInstructionOffset])
+ int producerOffset = producerOffsets.instructionOffset(producerIndex);
+ if (producerOffset == PartialEvaluator.AT_METHOD_ENTRY)
{
Instruction pushInstruction = InstructionFactory.create(codeAttrInfo.code,
- pushInstructionOffset);
-
- int lastOffset = lastPopInstructionOffset(pushInstructionOffset,
- index,
- pushInstructionOffset);
+ producerOffset);
- if (DEBUG_ANALYSIS) System.out.println(" Popping value right after "+lastOffset+", due to push at "+pushInstructionOffset);
+ if (DEBUG_ANALYSIS) System.out.println(" Popping value at start of method");
+ // Pop it right at the beginning of the method.
+ decreaseStackSize(0,
+ pushInstruction.stackPushCount(classFile),
+ true, false);
+ }
+ else if (isNecessaryProducer(producerOffset))
+ {
// Make sure the pushed value is popped again.
- if (lastOffset == AT_METHOD_ENTRY)
- {
- // Pop it right at the beginning of the method.
- decreaseStackSize(0,
- pushInstruction.stackPushCount(classFile),
- true, false);
- }
- else
+ Instruction pushInstruction = InstructionFactory.create(codeAttrInfo.code,
+ producerOffset);
+
+ // If there is another consumer, then leave it to
+ // the dup instruction to fix the stack.
+ if (!containsConsumer(producerOffset + 1,
+ consumerOffset,
+ producerOffset))
{
+ if (DEBUG_ANALYSIS) System.out.println(" Popping value right after "+producerOffset+", due to push at "+producerOffset);
+
// Pop it right after the instruction that pushes it
// (or after the dup instruction that still uses it).
- decreaseStackSize(lastOffset,
+ decreaseStackSize(producerOffset,
pushInstruction.stackPushCount(classFile),
false, false);
}
@@ -1039,29 +949,274 @@ implements MemberInfoVisitor,
/**
* Returns the last offset of the necessary instruction that depends on the
* stack result of the instruction at the given index.
- * @param startOffset the start offset in the search.
- * @param endOffset the end offset in the search.
- * @param pushInstructionOffset the offset of the instruction that pushes
+ * @param startOffset the start offset in the search.
+ * @param endOffset the end offset in the search.
+ * @param producerOffset the offset of the instruction that pushes
* a result onto the stack.
* @return the last offset of the necessary instruction that uses the
* above result.
*/
- private int lastPopInstructionOffset(int startOffset,
- int endOffset,
- int pushInstructionOffset)
+ private boolean containsConsumer(int startOffset,
+ int endOffset,
+ int producerOffset)
{
- int lastOffset = startOffset;
+ for (int consumerOffset = startOffset; consumerOffset < endOffset; consumerOffset++)
+ {
+ if (isNecessaryConsumer(consumerOffset) &&
+ partialEvaluator.stackProducerOffsets(consumerOffset).contains(producerOffset))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Marks the specified instruction if it is a required dup/swap instruction,
+ * replacing it by an appropriate variant if necessary.
+ * @param codeAttrInfo the code that is being checked.
+ * @param offset the offset of the instruction.
+ */
+ private void fixDupInstruction(CodeAttrInfo codeAttrInfo,
+ int offset)
+ {
+ byte oldOpcode = codeAttrInfo.code[offset];
+ byte newOpcode = 0;
+ boolean present = false;
- for (int index = startOffset + 1; index < endOffset; index++)
+ // Simplify the popping instruction if possible.
+ switch (oldOpcode)
{
- if (isNecessary[index] &&
- stackTraceValues[index].contains(pushInstructionOffset))
+ case InstructionConstants.OP_DUP:
+ {
+ boolean stackEntryPresent0 = isStackEntryPresent(offset, 0);
+ boolean stackEntryPresent1 = isStackEntryPresent(offset, 1);
+
+ // Should either the original element or the copy be present?
+ if (stackEntryPresent0 ||
+ stackEntryPresent1)
+ {
+ present = true;
+
+ // Should both the original element and the copy be present?
+ if (stackEntryPresent0 &&
+ stackEntryPresent1)
+ {
+ newOpcode = InstructionConstants.OP_DUP;
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_DUP_X1:
+ {
+ boolean stackEntryPresent0 = isStackEntryPresent(offset, 0);
+ boolean stackEntryPresent1 = isStackEntryPresent(offset, 1);
+ boolean stackEntryPresent2 = isStackEntryPresent(offset, 2);
+
+ // Should either the original element or the copy be present?
+ if (stackEntryPresent0 ||
+ stackEntryPresent2)
+ {
+ present = true;
+
+ // Should the copy be present?
+ if (stackEntryPresent2)
+ {
+ // Compute the number of elements to be skipped.
+ int skipCount = stackEntryPresent1 ? 1 : 0;
+
+ // Should the original element be present?
+ if (stackEntryPresent0)
+ {
+ // Copy the original element.
+ newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount);
+ }
+ else if (skipCount == 1)
+ {
+ // Move the original element.
+ newOpcode = InstructionConstants.OP_SWAP;
+ }
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_DUP_X2:
+ {
+ boolean stackEntryPresent0 = isStackEntryPresent(offset, 0);
+ boolean stackEntryPresent1 = isStackEntryPresent(offset, 1);
+ boolean stackEntryPresent2 = isStackEntryPresent(offset, 2);
+ boolean stackEntryPresent3 = isStackEntryPresent(offset, 3);
+
+ // Should either the original element or the copy be present?
+ if (stackEntryPresent0 ||
+ stackEntryPresent3)
+ {
+ present = true;
+
+ // Should the copy be present?
+ if (stackEntryPresent3)
+ {
+ int skipCount = (stackEntryPresent1 ? 1 : 0) +
+ (stackEntryPresent2 ? 1 : 0);
+
+ // Should the original element be present?
+ if (stackEntryPresent0)
+ {
+ // Copy the original element.
+ newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount);
+ }
+ else if (skipCount == 1)
+ {
+ // Move the original element.
+ newOpcode = InstructionConstants.OP_SWAP;
+ }
+ else if (skipCount == 2)
+ {
+ // We can't easily move the original element.
+ throw new IllegalArgumentException("Can't handle dup_x2 instruction moving original element across two elements");
+ }
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_DUP2:
+ {
+ boolean stackEntriesPresent01 = isStackEntriesPresent(offset, 0, 1);
+ boolean stackEntriesPresent23 = isStackEntriesPresent(offset, 2, 3);
+
+ // Should either the original element or the copy be present?
+ if (stackEntriesPresent01 ||
+ stackEntriesPresent23)
+ {
+ present = true;
+
+ // Should both the original element and the copy be present?
+ if (stackEntriesPresent01 &&
+ stackEntriesPresent23)
+ {
+ newOpcode = InstructionConstants.OP_DUP2;
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_DUP2_X1:
+ {
+ boolean stackEntriesPresent01 = isStackEntriesPresent(offset, 0, 1);
+ boolean stackEntryPresent2 = isStackEntryPresent(offset, 2);
+ boolean stackEntriesPresent34 = isStackEntriesPresent(offset, 3, 4);
+
+ // Should either the original element or the copy be present?
+ if (stackEntriesPresent01 ||
+ stackEntriesPresent34)
+ {
+ present = true;
+
+ // Should the copy be present?
+ if (stackEntriesPresent34)
+ {
+ int skipCount = stackEntryPresent2 ? 1 : 0;
+
+ // Should the original element be present?
+ if (stackEntriesPresent01)
+ {
+ // Copy the original element.
+ newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount);
+ }
+ else if (skipCount > 0)
+ {
+ // We can't easily move the original element.
+ throw new IllegalArgumentException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements");
+ }
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_DUP2_X2:
+ {
+ boolean stackEntriesPresent01 = isStackEntriesPresent(offset, 0, 1);
+ boolean stackEntryPresent2 = isStackEntryPresent(offset, 2);
+ boolean stackEntryPresent3 = isStackEntryPresent(offset, 3);
+ boolean stackEntriesPresent45 = isStackEntriesPresent(offset, 4, 5);
+
+ // Should either the original element or the copy be present?
+ if (stackEntriesPresent01 ||
+ stackEntriesPresent45)
+ {
+ present = true;
+
+ // Should the copy be present?
+ if (stackEntriesPresent45)
+ {
+ int skipCount = (stackEntryPresent2 ? 1 : 0) +
+ (stackEntryPresent3 ? 1 : 0);
+
+ // Should the original element be present?
+ if (stackEntriesPresent01)
+ {
+ // Copy the original element.
+ newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount);
+ }
+ else if (skipCount > 0)
+ {
+ // We can't easily move the original element.
+ throw new IllegalArgumentException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements");
+ }
+ }
+ }
+ break;
+ }
+ case InstructionConstants.OP_SWAP:
{
- lastOffset = index;
+ boolean stackEntryPresent0 = isStackEntryPresent(offset, 0);
+ boolean stackEntryPresent1 = isStackEntryPresent(offset, 1);
+
+ // Will either element be present?
+ if (stackEntryPresent0 ||
+ stackEntryPresent1)
+ {
+ present = true;
+
+ // Will both elements be present?
+ if (stackEntryPresent0 &&
+ stackEntryPresent1)
+ {
+ newOpcode = InstructionConstants.OP_SWAP;
+ }
+ }
+ break;
}
}
- return lastOffset;
+ // Actually replace the instruction with the new opcode, if any.
+ if (present)
+ {
+ // Mark that the instruction is necessary.
+ isNecessary[offset] = true;
+
+ if (newOpcode == 0)
+ {
+ // Delete the instruction.
+ codeAttrInfoEditor.deleteInstruction(offset);
+
+ if (DEBUG_ANALYSIS) System.out.println(" Marking but deleting instruction at ["+offset+"]");
+ }
+ else if (newOpcode == oldOpcode)
+ {
+ // Leave the instruction unchanged.
+ if (DEBUG_ANALYSIS) System.out.println(" Marking unchanged instruction at ["+offset+"]");
+ }
+ else
+ {
+ // Replace the instruction.
+ Instruction replacementInstruction = new SimpleInstruction(newOpcode);
+ codeAttrInfoEditor.replaceInstruction(offset,
+ replacementInstruction);
+
+ if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
+ }
+ }
}
@@ -1193,465 +1348,11 @@ implements MemberInfoVisitor,
}
- /**
- * Replaces the specified instruction by the proper dup/swap variant,
- * if necessary, depending on the state of the stack.
- * @param codeAttrInfo the code that is being checked.
- * @param offset the offset of the instruction.
- */
- private void fixDupInstruction(CodeAttrInfo codeAttrInfo,
- int offset)
- {
- byte replacementOpcode = 0;
-
- // Simplify the popping instruction if possible.
- switch (codeAttrInfo.code[offset])
- {
- case InstructionConstants.OP_DUP_X1:
- if (!isStackEntryPresent(offset, 1))
- {
- replacementOpcode = InstructionConstants.OP_DUP;
- }
- break;
-
- case InstructionConstants.OP_DUP_X2:
- if (!isStackEntryPresent(offset, 1) ||
- !isStackEntryPresent(offset, 2))
- {
- if (isStackEntryPresent(offset, 1) ||
- isStackEntryPresent(offset, 2))
- {
- replacementOpcode = InstructionConstants.OP_DUP_X1;
- }
- else
- {
- replacementOpcode = InstructionConstants.OP_DUP;
- }
- }
- break;
-
- case InstructionConstants.OP_DUP2_X1:
- if (!isStackEntryPresent(offset, 2))
- {
- replacementOpcode = InstructionConstants.OP_DUP2;
- }
- break;
-
- case InstructionConstants.OP_DUP2_X2:
- if (!isStackEntryPresent(offset, 2) ||
- !isStackEntryPresent(offset, 3))
- {
- if (isStackEntryPresent(offset, 2) ||
- isStackEntryPresent(offset, 3))
- {
- replacementOpcode = InstructionConstants.OP_DUP2_X1;
- }
- else
- {
- replacementOpcode = InstructionConstants.OP_DUP2;
- }
- }
- break;
-
- case InstructionConstants.OP_SWAP:
- if (!isStackEntryPresent(offset, 0))
- {
- isNecessary[offset] = false;
- }
- break;
- }
-
- // Actually replace the instruction with the new opcde, if any.
- if (replacementOpcode != 0)
- {
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
- codeAttrInfoEditor.replaceInstruction(offset,
- replacementInstruction);
-
- if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
- }
- }
-
-
- /**
- * Returns whether the given stack entry is present after execution of the
- * instruction at the given offset.
- */
- private boolean isStackEntryPresent(int instructionOffset, int stackIndex)
- {
- return isAnyNecessary(stacks[instructionOffset].getTopTraceValue(stackIndex).instructionOffsetValue());
- }
-
-
- // Implementations for ExceptionInfoVisitor.
-
- public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
- {
- if (isTraced(exceptionInfo.u2startpc, exceptionInfo.u2endpc))
- {
- if (DEBUG) System.out.println("Partial evaluation of exception ["+exceptionInfo.u2startpc+","+exceptionInfo.u2endpc+"] -> ["+exceptionInfo.u2handlerpc+"]:");
-
- // Generalize the variables of the try block.
- variables.reset(codeAttrInfo.u2maxLocals);
- generalizeVariables(exceptionInfo.u2startpc,
- exceptionInfo.u2endpc,
- variables);
-
- // Remember the entry variables of the exception.
- TracedVariables exceptionVariables = (TracedVariables)exceptionInfo.getVisitorInfo();
- if (exceptionVariables == null)
- {
- exceptionVariables = new TracedVariables(codeAttrInfo.u2maxLocals);
-
- exceptionInfo.setVisitorInfo(exceptionVariables);
- }
- else
- {
- // Bail out if the entry variables are the same as last time.
- if (exceptionVariables.equals(variables))
- {
- if (DEBUG) System.out.println(" Repeated initial variables");
-
- return;
- }
- }
-
- exceptionVariables.initialize(variables);
-
- // Reuse the existing variables and stack objects, ensuring the right size.
- variables.reset(codeAttrInfo.u2maxLocals);
- stack.reset(codeAttrInfo.u2maxStack);
-
- // The initial stack has a generic instruction offset.
- Value storeValue = InstructionOffsetValueFactory.create(AT_CATCH_ENTRY);
- variables.setStoreValue(storeValue);
- stack.setStoreValue(storeValue);
-
- // Initialize the local variables and the stack.
- variables.initialize(exceptionVariables);
- //stack.push(ReferenceValueFactory.create((ClassCpInfo)((ProgramClassFile)classFile).getCpEntry(exceptionInfo.u2catchType), false));
- stack.push(ReferenceValueFactory.create(false));
-
- // Evaluate the instructions, starting at the entry point.
- evaluateInstructionBlock(classFile,
- methodInfo,
- codeAttrInfo,
- variables,
- stack,
- branchUnit,
- exceptionInfo.u2handlerpc);
-
- // Remember to check this exception and other exceptions once more.
- evaluateExceptions = true;
- }
- }
-
-
- /**
- * Returns whether a block of instructions is ever used.
- */
- private boolean isTraced(int startOffset, int endOffset)
- {
- for (int index = startOffset; index < endOffset; index++)
- {
- if (isTraced(index))
- {
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
- * Generalize the local variable frames of a block of instructions.
- */
- private void generalizeVariables(int startOffset, int endOffset, TracedVariables generalizedVariables)
- {
- for (int index = startOffset; index < endOffset; index++)
- {
- if (isTraced(index))
- {
- // We can't use the return value, because local generalization
- // can be different a couple of times, with the global
- // generalization being the same.
- generalizedVariables.generalize(vars[index]);
- }
- }
- }
-
-
- // Utility methods to evaluate instruction blocks.
-
- /**
- * Evaluates a block of instructions, starting at the given offset and ending
- * at a branch instruction, a return instruction, or a throw instruction.
- */
- private void evaluateInstructionBlock(ClassFile classFile,
- MethodInfo methodInfo,
- CodeAttrInfo codeAttrInfo,
- TracedVariables variables,
- TracedStack stack,
- TracedBranchUnit branchUnit,
- int instructionOffset)
- {
- byte[] code = codeAttrInfo.code;
-
- if (DEBUG)
- {
- System.out.println("Instruction block starting at ["+instructionOffset+"] in "+
- ClassUtil.externalFullMethodDescription(classFile.getName(),
- 0,
- methodInfo.getName(classFile),
- methodInfo.getDescriptor(classFile)));
- System.out.println("Init vars: "+variables);
- System.out.println("Init stack: "+stack);
- }
-
- Processor processor = new Processor(variables, stack, branchUnit);
-
- UnusedParameterCleaner unusedParameterCleaner = new UnusedParameterCleaner(stack);
-
- // Evaluate the subsequent instructions.
- while (true)
- {
- // Maintain a generalized trace instruction offset.
- int evaluationCount = evaluationCounts[instructionOffset]++;
- if (evaluationCount == 0)
- {
- varTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
- stackTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
- unusedTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
- }
-
- // Remember this instruction's offset with any stored value.
- Value storeValue = InstructionOffsetValueFactory.create(instructionOffset);
- variables.setStoreValue(storeValue);
- stack.setStoreValue(storeValue);
-
- // Reset the trace value.
- InstructionOffsetValue traceValue = InstructionOffsetValueFactory.create();
- variables.setTraceValue(traceValue);
- stack.setTraceValue(traceValue);
- unusedParameterCleaner.setTraceValue(traceValue);
-
- // Reset the initialization flag.
- variables.resetInitialization();
-
- // Note that the instruction is only volatile.
- Instruction instruction = InstructionFactory.create(code, instructionOffset);
-
- // By default, the next instruction will be the one after this
- // instruction.
- int nextInstructionOffset = instructionOffset +
- instruction.length(instructionOffset);
- InstructionOffsetValue nextInstructionOffsetValue = InstructionOffsetValueFactory.create(nextInstructionOffset);
- branchUnit.resetCalled();
- branchUnit.setTraceBranchTargets(nextInstructionOffsetValue);
-
- // First clean all traces to unused parameters if this is a method
- // invocation.
- instruction.accept(classFile,
- methodInfo,
- codeAttrInfo,
- instructionOffset,
- unusedParameterCleaner);
-
- if (DEBUG)
- {
- System.out.println(instruction.toString(instructionOffset));
- }
-
- try
- {
- // Process the instruction. The processor may call
- // the Variables methods of 'variables',
- // the Stack methods of 'stack', and
- // the BranchUnit methods of 'branchUnit'.
- instruction.accept(classFile,
- methodInfo,
- codeAttrInfo,
- instructionOffset,
- processor);
- }
- catch (RuntimeException ex)
- {
- System.err.println("Unexpected error while performing partial evaluation:");
- System.err.println(" ClassFile = ["+classFile.getName()+"]");
- System.err.println(" Method = ["+methodInfo.getName(classFile)+methodInfo.getDescriptor(classFile)+"]");
- System.err.println(" Instruction = "+instruction.toString(instructionOffset));
-
- throw ex;
- }
-
- // Collect the offsets of the instructions whose results were used.
- InstructionOffsetValue variablesTraceValue = variables.getTraceValue().instructionOffsetValue();
- InstructionOffsetValue stackTraceValue = stack.getTraceValue().instructionOffsetValue();
- InstructionOffsetValue unusedTraceValue = unusedParameterCleaner.getTraceValue().instructionOffsetValue();
- varTraceValues[instructionOffset] =
- varTraceValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
- stackTraceValues[instructionOffset] =
- stackTraceValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
- unusedTraceValues[instructionOffset] =
- unusedTraceValues[instructionOffset].generalize(unusedTraceValue).instructionOffsetValue();
- initializedVariable[instructionOffset] = variables.getInitializationIndex();
-
- // Collect the branch targets from the branch unit.
- InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets();
- int branchTargetCount = branchTargets.instructionOffsetCount();
-
- // Stop tracing.
- variables.setTraceValue(traceValue);
- stack.setTraceValue(traceValue);
- branchUnit.setTraceBranchTargets(traceValue);
-
- if (DEBUG)
- {
- if (variablesTraceValue.instructionOffsetCount() > 0)
- {
- System.out.println(" has used information from instructions setting vars: "+variablesTraceValue);
- }
- if (stackTraceValue.instructionOffsetCount() > 0)
- {
- System.out.println(" has used information from instructions setting stack: "+stackTraceValue);
- }
- if (branchUnit.wasCalled())
- {
- System.out.println(" is branching to "+branchTargets);
- }
-
- if (varTraceValues[instructionOffset].instructionOffsetCount() > 0)
- {
- System.out.println(" has up til now been using information from instructions setting vars: "+varTraceValues[instructionOffset]);
- }
- if (stackTraceValues[instructionOffset].instructionOffsetCount() > 0)
- {
- System.out.println(" has up till now been using information from instructions setting stack: "+stackTraceValues[instructionOffset]);
- }
- if (unusedTraceValues[instructionOffset].instructionOffsetCount() > 0)
- {
- System.out.println(" no longer needs information from instructions setting stack: "+unusedTraceValues[instructionOffset]);
- }
- if (branchTargetValues[instructionOffset] != null)
- {
- System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]);
- }
-
- System.out.println(" Vars: "+variables);
- System.out.println(" Stack: "+stack);
- }
-
- // Maintain a generalized local variable frame and stack at this
- // branch instruction offset.
- if (vars[instructionOffset] == null)
- {
- // First time we're passing by this instruction.
- // There's not even a context at this index yet.
- vars[instructionOffset] = new TracedVariables(variables);
- stacks[instructionOffset] = new TracedStack(stack);
- }
- else if (evaluationCount == 0)
- {
- // First time we're passing by this instruction.
- // Reuse the context objects at this index.
- vars[instructionOffset].initialize(variables);
- stacks[instructionOffset].copy(stack);
- }
- else
- {
- // If there are multiple alternative branches, or if this
- // instruction has been evaluated an excessive number of
- // times, then generalize the current context.
- // TODO: See if we can avoid generalizing the current context.
- //if (branchTargetCount > 1 ||
- // evaluationCount > MAXIMUM_EVALUATION_COUNT)
- //{
- // variables.generalize(vars[instructionOffset]);
- // stack.generalize(stacks[instructionOffset]);
- //}
-
- boolean vars_changed = vars[instructionOffset].generalize(variables);
- boolean stack_changed = stacks[instructionOffset].generalize(stack);
-
- // Bail out if the current context is the same as last time.
- if (!vars_changed &&
- !stack_changed &&
- branchTargets.equals(branchTargetValues[instructionOffset]))
- {
- if (DEBUG) System.out.println("Repeated variables, stack, and branch targets");
-
- break;
- }
-
- // Generalize the current context. Note that the most recent
- // variable values have to remain last in the generalizations,
- // for the sake of the ret instruction.
- variables.initialize(vars[instructionOffset]);
- stack.copy(stacks[instructionOffset]);
- }
-
- // Did the branch unit get called?
- if (branchUnit.wasCalled())
- {
- // Accumulate the branch targets at this offset.
- branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ?
- branchTargets :
- branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue();
-
- // Are there no branch targets at all?
- if (branchTargetCount == 0)
- {
- // Exit from this code block.
- break;
- }
-
- // Accumulate the branch origins at the branch target offsets.
- InstructionOffsetValue instructionOffsetValue = InstructionOffsetValueFactory.create(instructionOffset);
- for (int index = 0; index < branchTargetCount; index++)
- {
- int branchTarget = branchTargets.instructionOffset(index);
- branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ?
- instructionOffsetValue:
- branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue();
- }
-
- // Are there multiple branch targets?
- if (branchTargetCount > 1)
- {
- // Handle them recursively and exit from this code block.
- for (int index = 0; index < branchTargetCount; index++)
- {
- if (DEBUG) System.out.println("Alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]");
-
- evaluateInstructionBlock(classFile,
- methodInfo,
- codeAttrInfo,
- new TracedVariables(variables),
- new TracedStack(stack),
- branchUnit,
- branchTargets.instructionOffset(index));
- }
-
- break;
- }
-
- if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]");
- }
-
- // Just continue with the next instruction.
- instructionOffset = branchTargets.instructionOffset(0);
- }
-
- if (DEBUG) System.out.println("Ending processing of instruction block");
- }
-
-
- // Implementations for InstructionVisitor.
+ // Implementations for InstructionVisitor.
public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
{
- if (isTraced(offset))
+ if (partialEvaluator.isTraced(offset))
{
switch (simpleInstruction.opcode)
{
@@ -1735,7 +1436,7 @@ implements MemberInfoVisitor,
public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
{
- if (isTraced(offset))
+ if (partialEvaluator.isTraced(offset))
{
switch (variableInstruction.opcode)
{
@@ -1786,37 +1487,7 @@ implements MemberInfoVisitor,
public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
{
- // Make sure 'new' instructions (or subsequent 'dup' instructions)
- // depend on the subsequent initializer calls, in case these calls
- // are marked as not having any side effects.
- if (isTraced(offset) &&
- cpInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL)
- {
- // Check if the invoked method is an initalizer.
- classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-
- if (isInitializer)
- {
- // Find the previous instruction (assuming there was no branch).
- int previousOffset = offset - 1;
- while (!isTraced(previousOffset))
- {
- previousOffset--;
- }
-
- // Compute the stack index of the uninitialized object.
- int stackIndex = stacks[offset].size();
-
- // Get the (first and presumably only) offset of the instruction
- // that put it there. This is typically a dup instruction.
- int newOffset = stacks[previousOffset].getBottomTraceValue(stackIndex).instructionOffsetValue().instructionOffset(0);
-
- // Add a reverse dependency. The source instruction depends on
- // the initializer instruction, thus making sure that the latter
- // is preserved whenever the former is used.
- stackTraceValues[newOffset] = stackTraceValues[newOffset].generalize(InstructionOffsetValueFactory.create(offset)).instructionOffsetValue();
- }
- }
+ // Constant pool instructions are not simplified at this point.
}
@@ -1846,8 +1517,7 @@ implements MemberInfoVisitor,
*/
private void replaceIntegerPushInstruction(int offset, Instruction instruction)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
+ Value pushedValue = partialEvaluator.stackTopValue(offset, 0);
if (pushedValue.isSpecific())
{
int value = pushedValue.integerValue().value();
@@ -1867,8 +1537,7 @@ implements MemberInfoVisitor,
*/
private void replaceLongPushInstruction(int offset, Instruction instruction)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
+ Value pushedValue = partialEvaluator.stackTopValue(offset, 0);
if (pushedValue.isSpecific())
{
long value = pushedValue.longValue().value();
@@ -1889,8 +1558,7 @@ implements MemberInfoVisitor,
*/
private void replaceFloatPushInstruction(int offset, Instruction instruction)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
+ Value pushedValue = partialEvaluator.stackTopValue(offset, 0);
if (pushedValue.isSpecific())
{
float value = pushedValue.floatValue().value();
@@ -1912,8 +1580,7 @@ implements MemberInfoVisitor,
*/
private void replaceDoublePushInstruction(int offset, Instruction instruction)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
+ Value pushedValue = partialEvaluator.stackTopValue(offset, 0);
if (pushedValue.isSpecific())
{
double value = pushedValue.doubleValue().value();
@@ -1934,8 +1601,7 @@ implements MemberInfoVisitor,
*/
private void replaceReferencePushInstruction(int offset, Instruction instruction)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
+ Value pushedValue = partialEvaluator.stackTopValue(offset, 0);
if (pushedValue.isSpecific())
{
ReferenceValue value = pushedValue.referenceValue();
@@ -1961,6 +1627,17 @@ implements MemberInfoVisitor,
if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Mark that the instruction has been simplified.
+ isSimplified[offset] = true;
+
+ // Visit the instruction, if required.
+ if (extraPushInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ extraPushInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null);
+ }
}
@@ -1970,20 +1647,20 @@ implements MemberInfoVisitor,
*/
private void replaceBranchInstruction(int offset, Instruction instruction)
{
- if (isTraced(offset))
+ if (partialEvaluator.isTraced(offset))
{
- InstructionOffsetValue branchTargetValue = branchTargetValues[offset];
+ InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
// Is there exactly one branch target (not from a goto or jsr)?
- if (branchTargetValue != null &&
- branchTargetValue.instructionOffsetCount() == 1 &&
+ if (branchTargets != null &&
+ branchTargets.instructionOffsetCount() == 1 &&
instruction.opcode != InstructionConstants.OP_GOTO &&
instruction.opcode != InstructionConstants.OP_GOTO_W &&
instruction.opcode != InstructionConstants.OP_JSR &&
instruction.opcode != InstructionConstants.OP_JSR_W)
{
// Is it branching to the next instruction?
- int branchOffset = branchTargetValue.instructionOffset(0) - offset;
+ int branchOffset = branchTargets.instructionOffset(0) - offset;
if (branchOffset == instruction.length(offset))
{
if (DEBUG_ANALYSIS) System.out.println(" Deleting zero branch instruction at ["+offset+"]");
@@ -2002,79 +1679,128 @@ implements MemberInfoVisitor,
codeAttrInfoEditor.replaceInstruction(offset,
replacementInstruction);
+
+ // Visit the instruction, if required.
+ if (extraBranchInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now,
+ // knowing that they aren't used anyway.
+ extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
+ }
}
}
}
}
- // Implementations for CpInfoVisitor.
+ // Small utility methods.
- public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
- public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
- public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
- public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
- public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
- public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
- public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
- public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
- public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
- public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+ /**
+ * Initializes the parameter data structure.
+ */
+ private void initializeParameters(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo)
+ {
+ // Initialize the parameters.
+ boolean isStatic =
+ (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
+ // Count the number of parameters, taking into account their Categories.
+ String parameterDescriptor = methodInfo.getDescriptor(classFile);
+ int parameterSize = (isStatic ? 0 : 1) +
+ ClassUtil.internalMethodParameterSize(parameterDescriptor);
- public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
- {
- isInitializer = methodrefCpInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
- }
+ // Reuse the existing parameters object, ensuring the right size.
+ parameters.reset(parameterSize);
+ // Go over the parameters again.
+ InternalTypeEnumeration internalTypeEnumeration =
+ new InternalTypeEnumeration(parameterDescriptor);
+
+ int parameterIndex = 0;
+
+ // Put the caller's reference in parameter 0.
+ if (!isStatic)
+ {
+ parameters.store(parameterIndex++, ReferenceValueFactory.create(false));
+ }
+
+ while (internalTypeEnumeration.hasMoreTypes())
+ {
+ String type = internalTypeEnumeration.nextType();
+
+ // Get a generic corresponding value.
+ Value value = ValueFactory.create(type);
+
+ // Store the value in the parameter.
+ parameters.store(parameterIndex, value);
+
+ // Increment the index according to the Category of the value.
+ parameterIndex += value.isCategory2() ? 2 : 1;
+ }
+ }
- // Small utility methods.
/**
- * Returns whether any of the instructions at the given offsets are marked as
- * necessary.
+ * Initializes the necessary data structure.
*/
- private boolean isAnyNecessary(InstructionOffsetValue traceValue)
+ private void initializeNecessary(CodeAttrInfo codeAttrInfo)
{
- int traceCount = traceValue.instructionOffsetCount();
+ int codeLength = codeAttrInfo.u4codeLength;
- for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
+ // Create new arrays for storing information at each instruction offset.
+ if (isNecessary.length < codeLength)
{
- int index = traceValue.instructionOffset(traceIndex);
-
- if (index == AT_METHOD_ENTRY ||
- (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index)))
+ isNecessary = new boolean[codeLength];
+ isSimplified = new boolean[codeLength];
+ }
+ else
+ {
+ for (int index = 0; index < codeLength; index++)
{
- return true;
+ isNecessary[index] = false;
+ isSimplified[index] = false;
}
}
+ }
- return false;
+
+ /**
+ * Returns whether the given opcode represents a dup or swap instruction
+ * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x1, swap).
+ */
+ private boolean isDupOrSwap(byte opcode) {
+ return opcode >= InstructionConstants.OP_DUP &&
+ opcode <= InstructionConstants.OP_SWAP;
}
/**
- * Returns whether all of the instructions at the given offsets are marked as
- * necessary.
+ * Returns whether the given stack entry is present after execution of the
+ * instruction at the given offset.
*/
- private boolean isAllNecessary(InstructionOffsetValue traceValue)
+ private boolean isStackEntriesPresent(int instructionOffset, int stackIndex1, int stackIndex2)
{
- int traceCount = traceValue.instructionOffsetCount();
+ boolean present1 = isStackEntryPresent(instructionOffset, stackIndex1);
+ boolean present2 = isStackEntryPresent(instructionOffset, stackIndex2);
- for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
+ if (present1 ^ present2)
{
- int index = traceValue.instructionOffset(traceIndex);
-
- if (index != AT_METHOD_ENTRY &&
- (!isNecessary[index] ||
- codeAttrInfoEditor.isModified(index)))
- {
- return false;
- }
+ throw new IllegalArgumentException("Can't handle partial use of dup2 instructions");
}
- return true;
+ return present1 || present2;
+ }
+
+
+ /**
+ * Returns whether the given stack entry is present after execution of the
+ * instruction at the given offset.
+ */
+ private boolean isStackEntryPresent(int instructionOffset, int stackIndex)
+ {
+ return isAnyNecessary(partialEvaluator.stackTopConsumerOffsets(instructionOffset, stackIndex));
}
@@ -2087,17 +1813,13 @@ implements MemberInfoVisitor,
{
int codeLength = codeAttrInfo.u4codeLength;
- for (int index = 0; index < codeLength; index++)
+ for (int consumerOffset = 0; consumerOffset < codeLength; consumerOffset++)
{
- if (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index))
+ if (isNecessaryConsumer(consumerOffset) &&
+ partialEvaluator.variableValue(consumerOffset, variableIndex) != null &&
+ isAnyNecessary(partialEvaluator.variableProducerOffsets(consumerOffset, variableIndex)))
{
- Value traceValue = vars[index].getStoredTraceValue(variableIndex);
- if (traceValue != null &&
- isAnyNecessary(traceValue.instructionOffsetValue()))
- {
- return true;
- }
+ return true;
}
}
@@ -2106,11 +1828,66 @@ implements MemberInfoVisitor,
/**
- * Returns whether the instruction at the given offset has ever been
- * executed during the partial evaluation.
+ * Returns whether any of the instructions at the given offsets are marked as
+ * necessary.
+ */
+ private boolean isAnyNecessary(InstructionOffsetValue offsets)
+ {
+ return isNecessary(offsets, false);
+ }
+
+
+ /**
+ * Returns whether all of the instructions at the given offsets are marked as
+ * necessary.
+ */
+ private boolean isAllNecessary(InstructionOffsetValue offsets)
+ {
+ return isNecessary(offsets, true);
+ }
+
+
+ /**
+ * Returns whether any of the instructions at the given offsets are marked as
+ * necessary.
+ */
+ private boolean isNecessary(InstructionOffsetValue offsets,
+ boolean all)
+ {
+ int offsetCount = offsets.instructionOffsetCount();
+
+ for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
+ {
+ int offset = offsets.instructionOffset(offsetIndex);
+
+ if (all ^ isNecessaryProducer(offset))
+ {
+ return !all;
+ }
+ }
+
+ return all;
+ }
+
+
+ /**
+ * Returns whether the instructions at the given offset is marked as
+ * necessary, as a producer.
+ */
+ private boolean isNecessaryProducer(int producerOffset)
+ {
+ return producerOffset == PartialEvaluator.AT_METHOD_ENTRY ||
+ isNecessary[producerOffset];
+ }
+
+
+ /**
+ * Returns whether the instructions at the given offset is marked as
+ * necessary, as a consumer.
*/
- private boolean isTraced(int instructionOffset)
+ private boolean isNecessaryConsumer(int consumerOffset)
{
- return evaluationCounts[instructionOffset] > 0;
+ return isNecessary[consumerOffset] &&
+ !isSimplified[consumerOffset];
}
}
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
index acca7d0..526be5b 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -1,4 +1,4 @@
-/* $Id: PartialEvaluator.java,v 1.32 2005/06/11 13:13:16 eric Exp $
+/* $Id: PartialEvaluator.java,v 1.37 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -22,35 +22,26 @@ package proguard.optimize.evaluation;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.optimize.*;
+import proguard.classfile.util.ClassUtil;
import proguard.optimize.evaluation.value.*;
+import proguard.optimize.peephole.BranchTargetFinder;
/**
- * This MemberInfoVisitor performs partial evaluation on the program methods
- * that it visits.
+ * This class performs partial evaluation.
*
* @author Eric Lafortune
*/
public class PartialEvaluator
-implements MemberInfoVisitor,
- AttrInfoVisitor,
- ExceptionInfoVisitor,
- InstructionVisitor,
- CpInfoVisitor
+implements ExceptionInfoVisitor,
+ InstructionVisitor
{
//*
- private static final boolean DEBUG_RESULTS = false;
- private static final boolean DEBUG_ANALYSIS = false;
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_RESULTS = false;
/*/
- private static boolean DEBUG_RESULTS = true;
- private static boolean DEBUG_ANALYSIS = true;
- private static boolean DEBUG = true;
+ private static boolean DEBUG = true;
+ private static boolean DEBUG_RESULTS = true;
//*/
private static final int INITIAL_CODE_LENGTH = 1024;
@@ -58,226 +49,46 @@ implements MemberInfoVisitor,
//private static final int MAXIMUM_EVALUATION_COUNT = 100;
- private static final int AT_METHOD_ENTRY = -1;
- private static final int AT_CATCH_ENTRY = -1;
- private static final int NONE = -1;
-
- private InstructionOffsetValue[] varTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] stackTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] unusedTraceValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
- private TracedVariables[] vars = new TracedVariables[INITIAL_CODE_LENGTH];
- private TracedStack[] stacks = new TracedStack[INITIAL_CODE_LENGTH];
- private int[] evaluationCounts = new int[INITIAL_CODE_LENGTH];
- private int[] initializedVariable = new int[INITIAL_CODE_LENGTH];
- private boolean[] isNecessary = new boolean[INITIAL_CODE_LENGTH];
+ public static final int AT_METHOD_ENTRY = -1;
+ public static final int AT_CATCH_ENTRY = -1;
+ public static final int NONE = -1;
+
+ private BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
+
+ private InstructionOffsetValue[] varProducerValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+ private InstructionOffsetValue[] stackProducerValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+ private InstructionOffsetValue[] unusedProducerValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+ private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+ private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
+ private TracedVariables[] vars = new TracedVariables[INITIAL_CODE_LENGTH];
+ private TracedStack[] stacks = new TracedStack[INITIAL_CODE_LENGTH];
+ private int[] evaluationCounts = new int[INITIAL_CODE_LENGTH];
+ private int[] initializedVariables = new int[INITIAL_CODE_LENGTH];
private boolean evaluateExceptions;
- private TracedVariables parameters = new TracedVariables(INITIAL_VALUE_COUNT);
private TracedVariables variables = new TracedVariables(INITIAL_VALUE_COUNT);
private TracedStack stack = new TracedStack(INITIAL_VALUE_COUNT);
private TracedBranchUnit branchUnit = new TracedBranchUnit();
- private ClassFileCleaner classFileCleaner = new ClassFileCleaner();
- private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
- private CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(INITIAL_CODE_LENGTH);
- private boolean isInitializer;
-
-
- // Implementations for MemberInfoVisitor.
-
- public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
- public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
- {
-// DEBUG = DEBUG_ANALYSIS = DEBUG_RESULTS =
-// programClassFile.getName().equals("abc/Def") &&
-// programMethodInfo.getName(programClassFile).equals("abc");
-
- // Initialize the parameters.
- boolean isStatic =
- (programMethodInfo.u2accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0;
-
- // Count the number of parameters, taking into account their Categories.
- String parameterDescriptor = programMethodInfo.getDescriptor(programClassFile);
- int parameterSize = (isStatic ? 0 : 1) +
- ClassUtil.internalMethodParameterSize(parameterDescriptor);
-
- // Reuse the existing parameters object, ensuring the right size.
- parameters.reset(parameterSize);
-
- // Go over the parameters again.
- InternalTypeEnumeration internalTypeEnumeration =
- new InternalTypeEnumeration(parameterDescriptor);
-
- int index = 0;
-
- // Clear the store value of each parameter.
- parameters.setStoreValue(InstructionOffsetValueFactory.create(AT_METHOD_ENTRY));
-
- // Put the caller's reference in parameter 0.
- if (!isStatic)
- {
- parameters.store(index++, ReferenceValueFactory.create(false));
- }
-
- while (internalTypeEnumeration.hasMoreTypes())
- {
- String type = internalTypeEnumeration.nextType();
-
- // Get a generic corresponding value.
- Value value = ValueFactory.create(type);
-
- // Store the value in the parameter.
- parameters.store(index, value);
-
- // Increment the index according to the Category of the value.
- index += value.isCategory2() ? 2 : 1;
- }
-
- // Reset the return value.
- branchUnit.setTraceReturnValue(null);
-
- try
- {
- // Process the code.
- programMethodInfo.attributesAccept(programClassFile, this);
- }
- catch (RuntimeException ex)
- {
- // TODO: Remove this when the partial evaluator has stabilized.
- System.err.println("Unexpected error while optimizing after partial evaluation:");
- System.err.println(" ClassFile = ["+programClassFile.getName()+"]");
- System.err.println(" Method = ["+programMethodInfo.getName(programClassFile)+programMethodInfo.getDescriptor(programClassFile)+"]");
- System.err.println("Not optimizing this method");
-
- if (DEBUG)
- {
- throw ex;
- }
- }
-
- if (DEBUG)
- {
- Value returnValue = branchUnit.getTraceReturnValue();
- if (returnValue != null)
- {
- System.out.println("Return value for method "+
- ClassUtil.externalFullMethodDescription(programClassFile.getName(),
- 0,
- programMethodInfo.getName(programClassFile),
- programMethodInfo.getDescriptor(programClassFile))+
- " -> ["+returnValue.toString()+"]");
- System.out.println();
- }
- }
- }
-
-
- public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
- public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
- // Implementations for AttrInfoVisitor.
-
- public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
- public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
- public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
- public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
- public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
- public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
- public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
- public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
- public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
- public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
- public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
- public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
- public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
- public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
- public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
- public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
- public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
- public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
- public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+ /**
+ * Performs partial evaluation of the given method with the given parameters.
+ * @param classFile the method's class file.
+ * @param methodInfo the method's header.
+ * @param codeAttrInfo the method's code.
+ * @param parameters the method parameters.
+ * @return the partial result.
+ */
+ public Value evaluate(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
+ Variables parameters)
{
- if (DEBUG_RESULTS)
- {
- System.out.println();
- System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
- System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
- 0,
- methodInfo.getName(classFile),
- methodInfo.getDescriptor(classFile)));
- System.out.println(" Params:"+parameters);
- }
-
- int codeLength = codeAttrInfo.u4codeLength;
-
- // Reset the code changes.
- codeAttrInfoEditor.reset(codeLength);
-
- if (DEBUG)
- {
- System.out.println(" Max locals = "+codeAttrInfo.u2maxLocals);
- System.out.println(" Max stack = "+codeAttrInfo.u2maxStack);
- }
-
- // Create new arrays for storing a stack and a set of variables at each
- // branch target.
- if (isNecessary.length < codeLength)
- {
- varTraceValues = new InstructionOffsetValue[codeLength];
- stackTraceValues = new InstructionOffsetValue[codeLength];
- unusedTraceValues = new InstructionOffsetValue[codeLength];
- branchOriginValues = new InstructionOffsetValue[codeLength];
- branchTargetValues = new InstructionOffsetValue[codeLength];
- vars = new TracedVariables[codeLength];
- stacks = new TracedStack[codeLength];
- evaluationCounts = new int[codeLength];
- initializedVariable = new int[codeLength];
- isNecessary = new boolean[codeLength];
-
- for (int index = 0; index < codeLength; index++)
- {
- initializedVariable[index] = NONE;
- }
- }
- else
- {
- for (int index = 0; index < codeLength; index++)
- {
- varTraceValues[index] = null;
- stackTraceValues[index] = null;
- unusedTraceValues[index] = null;
- branchOriginValues[index] = null;
- branchTargetValues[index] = null;
- evaluationCounts[index] = 0;
- initializedVariable[index] = NONE;
- isNecessary[index] = false;
+ // Initialize the reusable arrays and variables.
+ initializeVariables(codeAttrInfo, parameters);
- if (vars[index] != null)
- {
- vars[index].reset(codeAttrInfo.u2maxLocals);
- }
-
- if (stacks[index] != null)
- {
- stacks[index].reset(codeAttrInfo.u2maxStack);
- }
- }
- }
-
- // Reuse the existing variables and stack objects, ensuring the right size.
- variables.reset(codeAttrInfo.u2maxLocals);
- stack.reset(codeAttrInfo.u2maxStack);
-
- // Initialize the local variables with the parameters.
- variables.initialize(parameters);
+ // Find all instruction offsets,...
+ codeAttrInfo.accept(classFile, methodInfo, branchTargetFinder);
// Evaluate the instructions, starting at the entry point.
if (DEBUG) System.out.println("Partial evaluation: ");
@@ -302,296 +113,44 @@ implements MemberInfoVisitor,
}
while (evaluateExceptions);
- // Clean up the visitor information in the exceptions right away.
- codeAttrInfo.exceptionsAccept(classFile, methodInfo, classFileCleaner);
-
- // Replace any instructions that can be simplified.
- if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
-
- codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
-
-
- // Mark all essential instructions that have been encountered as used.
- if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
-
- // The invocation of the "super" or "this" <init> method inside a
- // constructor is always necessary, even if it is assumed to have no
- // side effects.
- boolean markSuperOrThis =
- methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
-
- int aload0Index = 0;
-
- int index = 0;
- do
- {
- if (isTraced(index))
- {
- Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
- index);
-
- // Remember the most recent aload0 instruction index.
- if (instruction.opcode == InstructionConstants.OP_ALOAD_0)
- {
- aload0Index = index;
- }
-
- // Mark the instruction as necessary if it is the first
- // invocation of the "super" or "this" <init> method
- // inside a constructor.
- else if (markSuperOrThis &&
- instruction.opcode == InstructionConstants.OP_INVOKESPECIAL &&
- stackTraceValues[index].contains(aload0Index))
- {
- markSuperOrThis = false;
-
- if (DEBUG_ANALYSIS) System.out.print(index+",");
- isNecessary[index] = true;
- }
-
- // Mark the instruction as necessary if it has side effects.
- else if (sideEffectInstructionChecker.hasSideEffects(classFile,
- methodInfo,
- codeAttrInfo,
- index,
- instruction))
- {
- if (DEBUG_ANALYSIS) System.out.print(index+",");
- isNecessary[index] = true;
- }
- }
-
- index++;
- }
- while (index < codeLength);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Mark all other instructions on which the essential instructions
- // depend. Instead of doing this recursively, we loop across all
- // instructions, starting at the last one, and restarting at any
- // higher, previously unmarked instruction that is being marked.
- if (DEBUG_ANALYSIS) System.out.println("Usage marking:");
-
- int lowestNecessaryIndex = codeLength;
- index = codeLength - 1;
- do
- {
- int nextIndex = index - 1;
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Check if this instruction is a branch origin from a branch that
- // straddles some marked code.
- nextIndex = markStraddlingBranches(index,
- branchTargetValues[index],
- true,
- lowestNecessaryIndex,
- nextIndex);
-
- // Mark the instructions on which this instruction depends.
- nextIndex = markDependencies(index,
- nextIndex);
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Check if this instruction is a branch target from a branch that
- // straddles some marked code.
- nextIndex = markStraddlingBranches(index,
- branchOriginValues[index],
- false,
- lowestNecessaryIndex,
- nextIndex);
-
- if (DEBUG_ANALYSIS)
- {
- if (nextIndex >= index)
- {
- System.out.println();
- }
- }
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Update the index of the instruction to be investigated next.
- index = nextIndex;
- }
- while (index >= 0);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Insert pop instructions where necessary, to keep the stack consistent.
- if (DEBUG_ANALYSIS) System.out.println("Stack consistency marking:");
-
- index = codeLength - 1;
- do
- {
- if (isTraced(index))
- {
- // Make sure the stack is always consistent at this offset.
- fixStackConsistency(classFile,
- codeAttrInfo,
- index);
- }
-
- index--;
- }
- while (index >= 0);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Fix dup/swap instructions where necessary, to keep the stack consistent.
- if (DEBUG_ANALYSIS) System.out.println("Dup/swap fixing:");
-
- index = 0;
- do
- {
- if (isNecessary[index])
- {
- // Make sure any dup/swap instructions are always consistent at this offset.
- fixDupInstruction(codeAttrInfo,
- index);
- }
-
- index++;
- }
- while (index < codeLength);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Mark branches straddling just inserted push/pop instructions.
- if (DEBUG_ANALYSIS) System.out.println("Final straddling branch marking:");
-
- lowestNecessaryIndex = codeLength;
- index = codeLength - 1;
- do
- {
- int nextIndex = index - 1;
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Check if this instruction is a branch origin from a branch that
- // straddles some marked code.
- nextIndex = markAndSimplifyStraddlingBranches(index,
- branchTargetValues[index],
- lowestNecessaryIndex,
- nextIndex);
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Check if this instruction is a branch target from a branch that
- // straddles some marked code.
- nextIndex = markAndSimplifyStraddlingBranches(branchOriginValues[index],
- index,
- lowestNecessaryIndex,
- nextIndex);
-
- if (DEBUG_ANALYSIS)
- {
- if (nextIndex >= index)
- {
- System.out.println();
- }
- }
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[index])
- {
- lowestNecessaryIndex = index;
- }
-
- // Update the index of the instruction to be investigated next.
- index = nextIndex;
- }
- while (index >= 0);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Mark variable initializations, even if they aren't strictly necessary.
- // The virtual machine is not smart enough to see this, and may complain
- // otherwise.
- if (DEBUG_ANALYSIS) System.out.println("Initialization marking: ");
-
- index = 0;
- do
+ if (DEBUG_RESULTS)
{
- // Is it an initialization that hasn't been marked yet, and whose
- // corresponding variable is used for storage?
- int variableIndex = initializedVariable[index];
- if (variableIndex != NONE &&
- !isNecessary[index] &&
- isVariableReferenced(codeAttrInfo, variableIndex))
- {
- if (DEBUG_ANALYSIS) System.out.println(index+",");
+ System.out.println("Evaluation results:");
- // Figure out what kind of initialization value has to be stored.
- int pushComputationalType = vars[index].load(variableIndex).computationalType();
- increaseStackSize(index, pushComputationalType, false);
- }
+ int codeLength = codeAttrInfo.u4codeLength;
- index++;
- }
- while (index < codeLength);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- if (DEBUG_RESULTS)
- {
- System.out.println("Results:");
int offset = 0;
do
{
Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
offset);
- System.out.println((isNecessary[offset] ? " + " : " - ")+instruction.toString(offset));
+ System.out.println(instruction.toString(offset));
if (isTraced(offset))
{
- if (varTraceValues[offset] != null &&
- varTraceValues[offset].instructionOffsetCount() > 0)
+ InstructionOffsetValue varProducerOffsets = varProducerOffsets(offset);
+ if (varProducerOffsets.instructionOffsetCount() > 0)
{
- System.out.println(" has overall been using information from instructions setting vars: "+varTraceValues[offset]);
+ System.out.println(" has overall been using information from instructions setting vars: "+varProducerOffsets);
}
- if (stackTraceValues[offset] != null &&
- stackTraceValues[offset].instructionOffsetCount() > 0)
- {
- System.out.println(" has overall been using information from instructions setting stack: "+stackTraceValues[offset]);
- }
- if (branchTargetValues[offset] != null)
- {
- System.out.println(" has overall been branching to "+branchTargetValues[offset]);
- }
- if (codeAttrInfoEditor.preInsertions[offset] != null)
+
+ InstructionOffsetValue stackProducerOffsets = stackProducerOffsets(offset);
+ if (stackProducerOffsets.instructionOffsetCount() > 0)
{
- System.out.println(" is preceded by: "+codeAttrInfoEditor.preInsertions[offset]);
+ System.out.println(" has overall been using information from instructions setting stack: "+stackProducerOffsets);
}
- if (codeAttrInfoEditor.replacements[offset] != null)
+
+ InstructionOffsetValue unusedProducerOffsets = unusedProducerOffsets(offset);
+ if (unusedProducerOffsets.instructionOffsetCount() > 0)
{
- System.out.println(" is replaced by: "+codeAttrInfoEditor.replacements[offset]);
+ System.out.println(" no longer needs information from instructions setting stack: "+unusedProducerOffsets);
}
- if (codeAttrInfoEditor.postInsertions[offset] != null)
+
+ InstructionOffsetValue branchTargets = branchTargets(offset);
+ if (branchTargets != null)
{
- System.out.println(" is followed by: "+codeAttrInfoEditor.postInsertions[offset]);
+ System.out.println(" has overall been branching to "+branchTargets);
}
+
System.out.println(" Vars: "+vars[offset]);
System.out.println(" Stack: "+stacks[offset]);
}
@@ -601,685 +160,167 @@ implements MemberInfoVisitor,
while (offset < codeLength);
}
- // Delete all instructions that are not used.
- int offset = 0;
- do
- {
- Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
- offset);
- if (!isNecessary[offset])
- {
- codeAttrInfoEditor.deleteInstruction(offset);
-
- codeAttrInfoEditor.insertBeforeInstruction(offset, null);
- codeAttrInfoEditor.replaceInstruction(offset, null);
- codeAttrInfoEditor.insertAfterInstruction(offset, null);
- }
-
- offset += instruction.length(offset);
- }
- while (offset < codeLength);
-
- // Apply all accumulated changes to the code.
- codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
- }
-
-
- /**
- * Marks the instructions at the given offsets, if the current instruction
- * itself has been marked.
- * @param index the offset of the current instruction.
- * @param nextIndex the index of the instruction to be investigated next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal, because instructions are
- * investigated starting at the highest index.
- */
- private int markDependencies(int index,
- int nextIndex)
- {
- if (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index))
- {
- if (DEBUG_ANALYSIS) System.out.print(index);
-
- // Mark all instructions whose variable values are used.
- nextIndex = markDependencies(varTraceValues[index], nextIndex);
-
- // Mark all instructions whose stack values are used.
- nextIndex = markDependencies(stackTraceValues[index], nextIndex);
-
- if (DEBUG_ANALYSIS) System.out.print(",");
- }
-
- return nextIndex;
- }
-
-
- /**
- * Marks the instructions at the given offsets.
- * @param traceOffsetValue the offsets of the instructions to be marked.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal, because instructions are
- * investigated starting at the highest index.
- */
- private int markDependencies(InstructionOffsetValue traceOffsetValue,
- int nextIndex)
- {
- if (traceOffsetValue != null)
+ if (DEBUG)
{
- int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
- for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
+ Value returnValue = branchUnit.getTraceReturnValue();
+ if (returnValue != null)
{
- // Has the other instruction been marked yet?
- int traceOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
- if (traceOffset > AT_METHOD_ENTRY &&
- !isNecessary[traceOffset])
- {
- if (DEBUG_ANALYSIS) System.out.print("["+traceOffset+"]");
-
- // Mark it.
- isNecessary[traceOffset] = true;
-
- // Restart at this instruction if it has a higher offset.
- if (nextIndex < traceOffset)
- {
- if (DEBUG_ANALYSIS) System.out.print("!");
-
- nextIndex = traceOffset;
- }
- }
+ System.out.println("Return value for method "+
+ ClassUtil.externalFullMethodDescription(classFile.getName(),
+ 0,
+ methodInfo.getName(classFile),
+ methodInfo.getDescriptor(classFile))+
+ " -> ["+returnValue.toString()+"]");
+ System.out.println();
}
}
- return nextIndex;
- }
-
-
- /**
- * Marks the branch instructions of straddling branches, if they straddle
- * some code that has been marked.
- * @param index the offset of the branch origin or branch target.
- * @param branchValue the offsets of the straddling branch targets
- * or branch origins.
- * @param isPointingToTargets <code>true</code> if the above offsets are
- * branch targets, <code>false</code> if they
- * are branch origins.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
- * instructions are investigated starting at the highest index.
- */
- private int markStraddlingBranches(int index,
- InstructionOffsetValue branchValue,
- boolean isPointingToTargets,
- int lowestNecessaryIndex,
- int nextIndex)
- {
- if (branchValue != null)
- {
- // Loop over all branch origins.
- int branchCount = branchValue.instructionOffsetCount();
- for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
- {
- // Is the branch straddling any necessary instructions?
- int branch = branchValue.instructionOffset(branchIndex);
-
- // Is the offset pointing to a branch origin or to a branch target?
- nextIndex = isPointingToTargets ?
- markStraddlingBranch(index, branch, lowestNecessaryIndex, nextIndex) :
- markStraddlingBranch(branch, index, lowestNecessaryIndex, nextIndex);
- }
- }
+ // Mark special dependencies of constructors.
+ codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
- return nextIndex;
+ return branchUnit.getTraceReturnValue();
}
/**
- * Marks the given branch instruction, if it straddles some code that has
- * been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
- * instructions are investigated starting at the highest index.
+ * Returns whether a block of instructions is ever used.
*/
- private int markStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ public boolean isTraced(int startOffset, int endOffset)
{
- // Has the branch origin been marked yet, and is it straddling the
- // lowest necessary instruction?
- if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
+ for (int index = startOffset; index < endOffset; index++)
{
- if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
-
- // Mark the branch origin.
- isNecessary[branchOrigin] = true;
-
- // Restart at the branch origin if it has a higher offset.
- if (nextIndex < branchOrigin)
+ if (isTraced(index))
{
- if (DEBUG_ANALYSIS) System.out.print("!");
-
- nextIndex = branchOrigin;
+ return true;
}
}
- return nextIndex;
+ return false;
}
/**
- * Marks and simplifies the branch instructions of straddling branches,
- * if they straddle some code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTargets the branch targets.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
- * instructions are investigated starting at the highest index.
+ * Returns whether the instruction at the given offset has ever been
+ * executed during the partial evaluation.
*/
- private int markAndSimplifyStraddlingBranches(int branchOrigin,
- InstructionOffsetValue branchTargets,
- int lowestNecessaryIndex,
- int nextIndex)
+ public boolean isTraced(int instructionOffset)
{
- if (branchTargets != null &&
- !isNecessary[branchOrigin])
- {
- // Loop over all branch targets.
- int branchCount = branchTargets.instructionOffsetCount();
- if (branchCount > 0)
- {
- for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
- {
- // Is the branch straddling any necessary instructions?
- int branchTarget = branchTargets.instructionOffset(branchIndex);
-
- if (!isStraddlingBranch(branchOrigin,
- branchTarget,
- lowestNecessaryIndex))
- {
- return nextIndex;
- }
- }
-
- nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTargets.instructionOffset(0),
- lowestNecessaryIndex,
- nextIndex);
- }
- }
-
- return nextIndex;
+ return evaluationCounts[instructionOffset] > 0;
}
/**
- * Marks and simplifies the branch instructions of straddling branches,
- * if they straddle some code that has been marked.
- * @param branchOrigins the branch origins.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
- * instructions are investigated starting at the highest index.
+ * Returns the variable value at the given instruction offset and variable
+ * index.
*/
- private int markAndSimplifyStraddlingBranches(InstructionOffsetValue branchOrigins,
- int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ public Value variableValue(int instructionOffset,
+ int variableIndex)
{
- if (branchOrigins != null)
- {
- // Loop over all branch origins.
- int branchCount = branchOrigins.instructionOffsetCount();
- for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
- {
- // Is the branch straddling any necessary instructions?
- int branchOrigin = branchOrigins.instructionOffset(branchIndex);
-
- nextIndex = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTarget,
- lowestNecessaryIndex,
- nextIndex);
- }
- }
-
- return nextIndex;
+ return vars[instructionOffset].load(variableIndex);
}
/**
- * Marks and simplifies the given branch instruction, if it straddles some
- * code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
- * @param nextIndex the index of the instruction to be investigated
- * next.
- * @return the updated index of the instruction to be investigated next.
- * It is always greater than or equal the original index, because
- * instructions are investigated starting at the highest index.
+ * Returns the instruction offsets that set the variable value at at the given
+ * instruction offset and variable index.
*/
- private int markAndSimplifyStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryIndex,
- int nextIndex)
+ public InstructionOffsetValue variableProducerOffsets(int instructionOffset,
+ int variableIndex)
{
- // Has the branch origin been marked yet, and is it straddling the
- // lowest necessary instruction?
- if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryIndex))
- {
- if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
-
- // Mark the branch origin.
- isNecessary[branchOrigin] = true;
-
- // Replace the branch instruction by a simple branch instrucion.
- Instruction replacementInstruction =
- new BranchInstruction(InstructionConstants.OP_GOTO_W,
- branchTarget - branchOrigin).shrink();
-
- codeAttrInfoEditor.replaceInstruction(branchOrigin,
- replacementInstruction);
-
- // Restart at the branch origin if it has a higher offset.
- if (nextIndex < branchOrigin)
- {
- if (DEBUG_ANALYSIS) System.out.print("!");
-
- nextIndex = branchOrigin;
- }
- }
-
- return nextIndex;
+ return vars[instructionOffset].getStoredTraceValue(variableIndex).instructionOffsetValue();
}
/**
- * Returns whether the given branch straddling some code that has been marked.
- * @param branchOrigin the branch origin.
- * @param branchTarget the branch target.
- * @param lowestNecessaryIndex the lowest offset of all instructions marked
- * so far.
+ * Returns the instruction offsets that set the variable that is being
+ * used at the given instruction offset.
*/
- private boolean isStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryIndex)
+ public InstructionOffsetValue varProducerOffsets(int instructionOffset)
{
- return branchOrigin <= lowestNecessaryIndex ^
- branchTarget <= lowestNecessaryIndex;
+ return varProducerValues[instructionOffset];
}
/**
- * Inserts pop instructions where necessary, in order to make sure the
- * stack is consistent at the given index.
- * @param classFile the class file that is being checked.
- * @param codeAttrInfo the code that is being checked.
- * @param index the offset of the dependent instruction.
+ * Returns the stack value at the given instruction offset and stack index.
*/
- private void fixStackConsistency(ClassFile classFile,
- CodeAttrInfo codeAttrInfo,
- int index)
+ public Value stackTopValue(int instructionOffset,
+ int stackIndex)
{
- // See if we have any values pushed on the stack that we aren't using.
- InstructionOffsetValue traceOffsetValue = unusedTraceValues[index];
-
- // This includes all values if the popping instruction isn't necessary at all.
- boolean isNotNecessary = !isNecessary[index];
- if (isNotNecessary)
- {
- traceOffsetValue = traceOffsetValue.generalize(stackTraceValues[index]).instructionOffsetValue();
- }
-
- // Do we have any pushing instructions?
- if (traceOffsetValue.instructionOffsetCount() > 0)
- {
- // Is this instruction really popping any values?
- // Note that dup instructions have a pop count of 0.
- // Also note that method invocations have their original pop counts,
- // including any unused parameters.
- Instruction popInstruction = InstructionFactory.create(codeAttrInfo.code,
- index);
- int popCount = popInstruction.stackPopCount(classFile);
- if (popCount > 0)
- {
- // Can we pop all values at the popping instruction?
- if (isNotNecessary &&
- popCount <= 6 &&
- isAllNecessary(traceOffsetValue))
- {
- // Is the popping instruction a simple pop or pop2 instruction?
- byte popOpcode = popInstruction.opcode;
- if (popOpcode == InstructionConstants.OP_POP ||
- popOpcode == InstructionConstants.OP_POP2)
- {
- if (DEBUG_ANALYSIS) System.out.println(" Popping value again at "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
-
- // Simply mark the pop or pop2 instruction.
- this.isNecessary[index] = true;
- }
- else
- {
- if (DEBUG_ANALYSIS) System.out.println(" Popping value instead of "+popInstruction.toString(index)+" (pushed at all "+traceOffsetValue.instructionOffsetCount()+" offsets)");
-
- // Make sure the pushed value is popped again,
- // right before this instruction.
- decreaseStackSize(index, popCount, true, isNotNecessary);
- }
- }
- else if (isAnyNecessary(traceOffsetValue))
- {
- // Pop the values right after the pushing instructions.
- if (DEBUG_ANALYSIS) System.out.println(" Popping value somewhere before "+index+" (pushed at some of "+traceOffsetValue.instructionOffsetCount()+" offsets):");
-
- // Go over all stack pushing instructions.
- int traceOffsetCount = traceOffsetValue.instructionOffsetCount();
- for (int traceOffsetIndex = 0; traceOffsetIndex < traceOffsetCount; traceOffsetIndex++)
- {
- // Has the push instruction been marked?
- int pushInstructionOffset = traceOffsetValue.instructionOffset(traceOffsetIndex);
- if (this.isNecessary[pushInstructionOffset])
- {
- Instruction pushInstruction = InstructionFactory.create(codeAttrInfo.code,
- pushInstructionOffset);
-
- int lastOffset = lastPopInstructionOffset(pushInstructionOffset,
- index,
- pushInstructionOffset);
-
- if (DEBUG_ANALYSIS) System.out.println(" Popping value right after "+lastOffset+", due to push at "+pushInstructionOffset);
-
- // Make sure the pushed value is popped again.
- if (lastOffset == AT_METHOD_ENTRY)
- {
- // Pop it right at the beginning of the method.
- decreaseStackSize(0,
- pushInstruction.stackPushCount(classFile),
- true, false);
- }
- else
- {
- // Pop it right after the instruction that pushes it
- // (or after the dup instruction that still uses it).
- decreaseStackSize(lastOffset,
- pushInstruction.stackPushCount(classFile),
- false, false);
- }
- }
- }
- }
- }
- }
+ return stacks[instructionOffset].getTop(stackIndex);
}
/**
- * Returns the last offset of the necessary instruction that depends on the
- * stack result of the instruction at the given index.
- * @param startOffset the start offset in the search.
- * @param endOffset the end offset in the search.
- * @param pushInstructionOffset the offset of the instruction that pushes
- * a result onto the stack.
- * @return the last offset of the necessary instruction that uses the
- * above result.
+ * Returns the instruction offsets that set the stack value at at the given
+ * instruction offset and stack index.
*/
- private int lastPopInstructionOffset(int startOffset,
- int endOffset,
- int pushInstructionOffset)
+ public InstructionOffsetValue stackTopProducerOffsets(int instructionOffset,
+ int stackIndex)
{
- int lastOffset = startOffset;
-
- for (int index = startOffset + 1; index < endOffset; index++)
- {
- if (isNecessary[index] &&
- stackTraceValues[index].contains(pushInstructionOffset))
- {
- lastOffset = index;
- }
- }
-
- return lastOffset;
+ return stacks[instructionOffset].getTopProducerValue(stackIndex).instructionOffsetValue();
}
/**
- * Puts the required push instruction before the given index. The
- * instruction is marked as necessary.
- * @param offset the offset of the instruction.
- * @param computationalType the computational type on the stack, for
- * push instructions.
- * @param delete specifies whether the instruction should be
- * deleted.
+ * Returns the instruction offsets that set the stack entries that are being
+ * used at the given instruction offset.
*/
- private void increaseStackSize(int offset,
- int computationalType,
- boolean delete)
+ public InstructionOffsetValue stackProducerOffsets(int instructionOffset)
{
- // Mark this instruction.
- isNecessary[offset] = true;
-
- // Create a simple push instrucion.
- byte replacementOpcode =
- computationalType == Value.TYPE_INTEGER ? InstructionConstants.OP_ICONST_0 :
- computationalType == Value.TYPE_LONG ? InstructionConstants.OP_LCONST_0 :
- computationalType == Value.TYPE_FLOAT ? InstructionConstants.OP_FCONST_0 :
- computationalType == Value.TYPE_DOUBLE ? InstructionConstants.OP_DCONST_0 :
- computationalType == Value.TYPE_REFERENCE ? InstructionConstants.OP_ACONST_NULL :
- InstructionConstants.OP_NOP;
-
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
- // Insert the pop or push instruction.
- codeAttrInfoEditor.insertBeforeInstruction(offset,
- replacementInstruction);
-
- // Delete the original instruction if necessary.
- if (delete)
- {
- codeAttrInfoEditor.deleteInstruction(offset);
- }
+ return stackProducerValues[instructionOffset];
}
/**
- * Puts the required pop instruction at the given index. The
- * instruction is marked as necessary.
- * @param offset the offset of the instruction.
- * @param popCount the required reduction of the stack size.
- * @param before specifies whether the pop instruction should be inserted
- * before or after the present instruction.
- * @param delete specifies whether the instruction should be deleted.
+ * Returns the instruction offsets that use the stack value at at the given
+ * instruction offset and stack index.
*/
- private void decreaseStackSize(int offset,
- int popCount,
- boolean before,
- boolean delete)
+ public InstructionOffsetValue stackTopConsumerOffsets(int instructionOffset,
+ int stackIndex)
{
- // Mark this instruction.
- isNecessary[offset] = true;
-
- boolean after = !before;
-
- int remainingPopCount = popCount;
-
- if (delete)
- {
- // Replace the original instruction.
- int count = remainingPopCount == 1 ? 1 : 2;
-
- // Create a simple pop instrucion.
- byte replacementOpcode = count == 1 ?
- InstructionConstants.OP_POP :
- InstructionConstants.OP_POP2;
-
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
- // Insert the pop instruction.
- codeAttrInfoEditor.replaceInstruction(offset,
- replacementInstruction);
-
- remainingPopCount -= count;
-
- // We may insert other pop instructions before and after this one.
- before = true;
- after = true;
- }
-
- if (before && remainingPopCount > 0)
- {
- // Insert before the original instruction.
- int count = remainingPopCount == 1 ? 1 : 2;
-
- // Create a simple pop instrucion.
- byte replacementOpcode = count == 1 ?
- InstructionConstants.OP_POP :
- InstructionConstants.OP_POP2;
-
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
- // Insert the pop instruction.
- codeAttrInfoEditor.insertBeforeInstruction(offset,
- replacementInstruction);
-
- remainingPopCount -= count;
- }
-
- if (after && remainingPopCount > 0)
- {
- // Insert after the original instruction.
- int count = remainingPopCount == 1 ? 1 : 2;
-
- // Create a simple pop instrucion.
- byte replacementOpcode = count == 1 ?
- InstructionConstants.OP_POP :
- InstructionConstants.OP_POP2;
-
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
- // Insert the pop instruction.
- codeAttrInfoEditor.insertAfterInstruction(offset,
- replacementInstruction);
-
- remainingPopCount -= count;
- }
-
- if (remainingPopCount > 0)
- {
- throw new IllegalArgumentException("Unsupported stack size reduction ["+popCount+"]");
- }
+ return stacks[instructionOffset].getTopConsumerValue(stackIndex).instructionOffsetValue();
}
/**
- * Replaces the specified instruction by the proper dup/swap variant,
- * if necessary, depending on the state of the stack.
- * @param codeAttrInfo the code that is being checked.
- * @param offset the offset of the instruction.
+ * Returns the instruction offsets that set stack entries that are not being
+ * used at the given instruction offset (e.g. because the parameters are not
+ * being used).
*/
- private void fixDupInstruction(CodeAttrInfo codeAttrInfo,
- int offset)
+ public InstructionOffsetValue unusedProducerOffsets(int instructionOffset)
{
- byte replacementOpcode = 0;
-
- // Simplify the popping instruction if possible.
- switch (codeAttrInfo.code[offset])
- {
- case InstructionConstants.OP_DUP_X1:
- if (!isStackEntryPresent(offset, 1))
- {
- replacementOpcode = InstructionConstants.OP_DUP;
- }
- break;
-
- case InstructionConstants.OP_DUP_X2:
- if (!isStackEntryPresent(offset, 1) ||
- !isStackEntryPresent(offset, 2))
- {
- if (isStackEntryPresent(offset, 1) ||
- isStackEntryPresent(offset, 2))
- {
- replacementOpcode = InstructionConstants.OP_DUP_X1;
- }
- else
- {
- replacementOpcode = InstructionConstants.OP_DUP;
- }
- }
- break;
-
- case InstructionConstants.OP_DUP2_X1:
- if (!isStackEntryPresent(offset, 2))
- {
- replacementOpcode = InstructionConstants.OP_DUP2;
- }
- break;
+ return unusedProducerValues[instructionOffset];
+ }
- case InstructionConstants.OP_DUP2_X2:
- if (!isStackEntryPresent(offset, 2) ||
- !isStackEntryPresent(offset, 3))
- {
- if (isStackEntryPresent(offset, 2) ||
- isStackEntryPresent(offset, 3))
- {
- replacementOpcode = InstructionConstants.OP_DUP2_X1;
- }
- else
- {
- replacementOpcode = InstructionConstants.OP_DUP2;
- }
- }
- break;
- case InstructionConstants.OP_SWAP:
- if (!isStackEntryPresent(offset, 0))
- {
- isNecessary[offset] = false;
- }
- break;
- }
+ /**
+ * Returns the instruction offsets that branch to the given instruction
+ * offset.
+ */
+ public InstructionOffsetValue branchOrigins(int instructionOffset)
+ {
+ return branchOriginValues[instructionOffset];
+ }
- // Actually replace the instruction with the new opcde, if any.
- if (replacementOpcode != 0)
- {
- Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
- codeAttrInfoEditor.replaceInstruction(offset,
- replacementInstruction);
- if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
- }
+ /**
+ * Returns the instruction offsets to which the given instruction offset
+ * branches.
+ */
+ public InstructionOffsetValue branchTargets(int instructionOffset)
+ {
+ return branchTargetValues[instructionOffset];
}
/**
- * Returns whether the given stack entry is present after execution of the
- * instruction at the given offset.
+ * Returns the variable that is initialized at the given instruction offset,
+ * or NONE if no variable was initialized.
*/
- private boolean isStackEntryPresent(int instructionOffset, int stackIndex)
+ public int initializedVariable(int instructionOffset)
{
- return isAnyNecessary(stacks[instructionOffset].getTopTraceValue(stackIndex).instructionOffsetValue());
+ return initializedVariables[instructionOffset];
}
@@ -1324,8 +365,8 @@ implements MemberInfoVisitor,
// The initial stack has a generic instruction offset.
Value storeValue = InstructionOffsetValueFactory.create(AT_CATCH_ENTRY);
- variables.setStoreValue(storeValue);
- stack.setStoreValue(storeValue);
+ variables.setProducerValue(storeValue);
+ stack.setProducerValue(storeValue);
// Initialize the local variables and the stack.
variables.initialize(exceptionVariables);
@@ -1347,37 +388,43 @@ implements MemberInfoVisitor,
}
- /**
- * Returns whether a block of instructions is ever used.
- */
- private boolean isTraced(int startOffset, int endOffset)
- {
- for (int index = startOffset; index < endOffset; index++)
- {
- if (isTraced(index))
- {
- return true;
- }
- }
+ // Implementations for InstructionVisitor.
- return false;
- }
+ public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+ public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+ public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
+ public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+ public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
- /**
- * Generalize the local variable frames of a block of instructions.
- */
- private void generalizeVariables(int startOffset, int endOffset, TracedVariables generalizedVariables)
+ public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
{
- for (int index = startOffset; index < endOffset; index++)
+ // Make sure 'new' instructions (or subsequent 'dup' instructions)
+ // depend on the subsequent initializer calls, in case these calls
+ // are marked as not having any side effects.
+
+ // Check if the invoked method is an initalizer.
+ if (isTraced(offset) &&
+ branchTargetFinder.isInitializer(offset))
{
- if (isTraced(index))
+ // Find the previous instruction (assuming there was no branch).
+ int previousOffset = offset - 1;
+ while (!isTraced(previousOffset))
{
- // We can't use the return value, because local generalization
- // can be different a couple of times, with the global
- // generalization being the same.
- generalizedVariables.generalize(vars[index]);
+ previousOffset--;
}
+
+ // Compute the stack index of the uninitialized object.
+ int stackIndex = stacks[offset].size();
+
+ // Get the (first and presumably only) offset of the instruction
+ // that put it there. This is typically a dup instruction.
+ int newOffset = stacks[previousOffset].getBottomProducerValue(stackIndex).instructionOffsetValue().instructionOffset(0);
+
+ // Add a reverse dependency. The source instruction depends on
+ // the initializer instruction, thus making sure that the latter
+ // is preserved whenever the former is used.
+ stackProducerValues[newOffset] = stackProducerValues[newOffset].generalize(InstructionOffsetValueFactory.create(offset)).instructionOffsetValue();
}
}
@@ -1420,20 +467,20 @@ implements MemberInfoVisitor,
int evaluationCount = evaluationCounts[instructionOffset]++;
if (evaluationCount == 0)
{
- varTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
- stackTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
- unusedTraceValues[instructionOffset] = InstructionOffsetValueFactory.create();
+ varProducerValues[instructionOffset] = InstructionOffsetValueFactory.create();
+ stackProducerValues[instructionOffset] = InstructionOffsetValueFactory.create();
+ unusedProducerValues[instructionOffset] = InstructionOffsetValueFactory.create();
}
// Remember this instruction's offset with any stored value.
Value storeValue = InstructionOffsetValueFactory.create(instructionOffset);
- variables.setStoreValue(storeValue);
- stack.setStoreValue(storeValue);
+ variables.setProducerValue(storeValue);
+ stack.setProducerValue(storeValue);
// Reset the trace value.
InstructionOffsetValue traceValue = InstructionOffsetValueFactory.create();
- variables.setTraceValue(traceValue);
- stack.setTraceValue(traceValue);
+ variables.setCollectedProducerValue(traceValue);
+ stack.setCollectedProducerValue(traceValue);
unusedParameterCleaner.setTraceValue(traceValue);
// Reset the initialization flag.
@@ -1486,24 +533,24 @@ implements MemberInfoVisitor,
}
// Collect the offsets of the instructions whose results were used.
- InstructionOffsetValue variablesTraceValue = variables.getTraceValue().instructionOffsetValue();
- InstructionOffsetValue stackTraceValue = stack.getTraceValue().instructionOffsetValue();
+ InstructionOffsetValue variablesTraceValue = variables.getCollectedProducerValue().instructionOffsetValue();
+ InstructionOffsetValue stackTraceValue = stack.getCollectedProducerValue().instructionOffsetValue();
InstructionOffsetValue unusedTraceValue = unusedParameterCleaner.getTraceValue().instructionOffsetValue();
- varTraceValues[instructionOffset] =
- varTraceValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
- stackTraceValues[instructionOffset] =
- stackTraceValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
- unusedTraceValues[instructionOffset] =
- unusedTraceValues[instructionOffset].generalize(unusedTraceValue).instructionOffsetValue();
- initializedVariable[instructionOffset] = variables.getInitializationIndex();
+ varProducerValues[instructionOffset] =
+ varProducerValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
+ stackProducerValues[instructionOffset] =
+ stackProducerValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
+ unusedProducerValues[instructionOffset] =
+ unusedProducerValues[instructionOffset].generalize(unusedTraceValue).instructionOffsetValue();
+ initializedVariables[instructionOffset] = variables.getInitializationIndex();
// Collect the branch targets from the branch unit.
InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets();
int branchTargetCount = branchTargets.instructionOffsetCount();
// Stop tracing.
- variables.setTraceValue(traceValue);
- stack.setTraceValue(traceValue);
+ variables.setCollectedProducerValue(traceValue);
+ stack.setCollectedProducerValue(traceValue);
branchUnit.setTraceBranchTargets(traceValue);
if (DEBUG)
@@ -1521,17 +568,17 @@ implements MemberInfoVisitor,
System.out.println(" is branching to "+branchTargets);
}
- if (varTraceValues[instructionOffset].instructionOffsetCount() > 0)
+ if (varProducerValues[instructionOffset].instructionOffsetCount() > 0)
{
- System.out.println(" has up til now been using information from instructions setting vars: "+varTraceValues[instructionOffset]);
+ System.out.println(" has up til now been using information from instructions setting vars: "+varProducerValues[instructionOffset]);
}
- if (stackTraceValues[instructionOffset].instructionOffsetCount() > 0)
+ if (stackProducerValues[instructionOffset].instructionOffsetCount() > 0)
{
- System.out.println(" has up till now been using information from instructions setting stack: "+stackTraceValues[instructionOffset]);
+ System.out.println(" has up till now been using information from instructions setting stack: "+stackProducerValues[instructionOffset]);
}
- if (unusedTraceValues[instructionOffset].instructionOffsetCount() > 0)
+ if (unusedProducerValues[instructionOffset].instructionOffsetCount() > 0)
{
- System.out.println(" no longer needs information from instructions setting stack: "+unusedTraceValues[instructionOffset]);
+ System.out.println(" no longer needs information from instructions setting stack: "+unusedProducerValues[instructionOffset]);
}
if (branchTargetValues[instructionOffset] != null)
{
@@ -1647,470 +694,98 @@ implements MemberInfoVisitor,
}
- // Implementations for InstructionVisitor.
-
- public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
- {
- if (isTraced(offset))
- {
- switch (simpleInstruction.opcode)
- {
- case InstructionConstants.OP_IALOAD:
- case InstructionConstants.OP_BALOAD:
- case InstructionConstants.OP_CALOAD:
- case InstructionConstants.OP_SALOAD:
- case InstructionConstants.OP_IADD:
- case InstructionConstants.OP_ISUB:
- case InstructionConstants.OP_IMUL:
- case InstructionConstants.OP_IDIV:
- case InstructionConstants.OP_IREM:
- case InstructionConstants.OP_INEG:
- case InstructionConstants.OP_ISHL:
- case InstructionConstants.OP_ISHR:
- case InstructionConstants.OP_IUSHR:
- case InstructionConstants.OP_IAND:
- case InstructionConstants.OP_IOR:
- case InstructionConstants.OP_IXOR:
- case InstructionConstants.OP_L2I:
- case InstructionConstants.OP_F2I:
- case InstructionConstants.OP_D2I:
- case InstructionConstants.OP_I2B:
- case InstructionConstants.OP_I2C:
- case InstructionConstants.OP_I2S:
- replaceIntegerPushInstruction(offset, simpleInstruction);
- break;
-
- case InstructionConstants.OP_LALOAD:
- case InstructionConstants.OP_LADD:
- case InstructionConstants.OP_LSUB:
- case InstructionConstants.OP_LMUL:
- case InstructionConstants.OP_LDIV:
- case InstructionConstants.OP_LREM:
- case InstructionConstants.OP_LNEG:
- case InstructionConstants.OP_LSHL:
- case InstructionConstants.OP_LSHR:
- case InstructionConstants.OP_LUSHR:
- case InstructionConstants.OP_LAND:
- case InstructionConstants.OP_LOR:
- case InstructionConstants.OP_LXOR:
- case InstructionConstants.OP_I2L:
- case InstructionConstants.OP_F2L:
- case InstructionConstants.OP_D2L:
- replaceLongPushInstruction(offset, simpleInstruction);
- break;
-
- case InstructionConstants.OP_FALOAD:
- case InstructionConstants.OP_FADD:
- case InstructionConstants.OP_FSUB:
- case InstructionConstants.OP_FMUL:
- case InstructionConstants.OP_FDIV:
- case InstructionConstants.OP_FREM:
- case InstructionConstants.OP_FNEG:
- case InstructionConstants.OP_I2F:
- case InstructionConstants.OP_L2F:
- case InstructionConstants.OP_D2F:
- replaceFloatPushInstruction(offset, simpleInstruction);
- break;
-
- case InstructionConstants.OP_DALOAD:
- case InstructionConstants.OP_DADD:
- case InstructionConstants.OP_DSUB:
- case InstructionConstants.OP_DMUL:
- case InstructionConstants.OP_DDIV:
- case InstructionConstants.OP_DREM:
- case InstructionConstants.OP_DNEG:
- case InstructionConstants.OP_I2D:
- case InstructionConstants.OP_L2D:
- case InstructionConstants.OP_F2D:
- replaceDoublePushInstruction(offset, simpleInstruction);
- break;
-
- case InstructionConstants.OP_AALOAD:
- replaceReferencePushInstruction(offset, simpleInstruction);
- break;
- }
- }
- }
-
-
- public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
- {
- if (isTraced(offset))
- {
- switch (variableInstruction.opcode)
- {
- case InstructionConstants.OP_ILOAD:
- case InstructionConstants.OP_ILOAD_0:
- case InstructionConstants.OP_ILOAD_1:
- case InstructionConstants.OP_ILOAD_2:
- case InstructionConstants.OP_ILOAD_3:
- replaceIntegerPushInstruction(offset, variableInstruction);
- break;
-
- case InstructionConstants.OP_LLOAD:
- case InstructionConstants.OP_LLOAD_0:
- case InstructionConstants.OP_LLOAD_1:
- case InstructionConstants.OP_LLOAD_2:
- case InstructionConstants.OP_LLOAD_3:
- replaceLongPushInstruction(offset, variableInstruction);
- break;
-
- case InstructionConstants.OP_FLOAD:
- case InstructionConstants.OP_FLOAD_0:
- case InstructionConstants.OP_FLOAD_1:
- case InstructionConstants.OP_FLOAD_2:
- case InstructionConstants.OP_FLOAD_3:
- replaceFloatPushInstruction(offset, variableInstruction);
- break;
-
- case InstructionConstants.OP_DLOAD:
- case InstructionConstants.OP_DLOAD_0:
- case InstructionConstants.OP_DLOAD_1:
- case InstructionConstants.OP_DLOAD_2:
- case InstructionConstants.OP_DLOAD_3:
- replaceDoublePushInstruction(offset, variableInstruction);
- break;
-
- case InstructionConstants.OP_ALOAD:
- case InstructionConstants.OP_ALOAD_0:
- case InstructionConstants.OP_ALOAD_1:
- case InstructionConstants.OP_ALOAD_2:
- case InstructionConstants.OP_ALOAD_3:
- replaceReferencePushInstruction(offset, variableInstruction);
- break;
-
- }
- }
- }
-
-
- public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
- {
- // Make sure 'new' instructions (or subsequent 'dup' instructions)
- // depend on the subsequent initializer calls, in case these calls
- // are marked as not having any side effects.
- if (isTraced(offset) &&
- cpInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL)
- {
- // Check if the invoked method is an initalizer.
- classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-
- if (isInitializer)
- {
- // Find the previous instruction (assuming there was no branch).
- int previousOffset = offset - 1;
- while (!isTraced(previousOffset))
- {
- previousOffset--;
- }
-
- // Compute the stack index of the uninitialized object.
- int stackIndex = stacks[offset].size();
-
- // Get the (first and presumably only) offset of the instruction
- // that put it there. This is typically a dup instruction.
- int newOffset = stacks[previousOffset].getBottomTraceValue(stackIndex).instructionOffsetValue().instructionOffset(0);
-
- // Add a reverse dependency. The source instruction depends on
- // the initializer instruction, thus making sure that the latter
- // is preserved whenever the former is used.
- stackTraceValues[newOffset] = stackTraceValues[newOffset].generalize(InstructionOffsetValueFactory.create(offset)).instructionOffsetValue();
- }
- }
- }
-
-
- public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
- {
- replaceBranchInstruction(offset, branchInstruction);
- }
-
-
- public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
- {
- replaceBranchInstruction(offset, tableSwitchInstruction);
- }
-
-
- public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
- {
- replaceBranchInstruction(offset, lookUpSwitchInstruction);
- }
-
-
// Small utility methods.
/**
- * Replaces the given integer instruction by a simpler push instruction,
- * if possible.
+ * Initializes the data structures for the variables, stack, etc.
*/
- private void replaceIntegerPushInstruction(int offset, Instruction instruction)
+ private void initializeVariables(CodeAttrInfo codeAttrInfo,
+ Variables parameters)
{
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
- if (pushedValue.isSpecific())
- {
- int value = pushedValue.integerValue().value();
- if (value << 16 >> 16 == value)
- {
- replacePushInstruction(offset,
- InstructionConstants.OP_SIPUSH,
- value);
- }
- }
- }
-
+ int codeLength = codeAttrInfo.u4codeLength;
- /**
- * Replaces the given long instruction by a simpler push instruction,
- * if possible.
- */
- private void replaceLongPushInstruction(int offset, Instruction instruction)
- {
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
- if (pushedValue.isSpecific())
+ if (DEBUG)
{
- long value = pushedValue.longValue().value();
- if (value == 0L ||
- value == 1L)
- {
- replacePushInstruction(offset,
- (byte)(InstructionConstants.OP_LCONST_0 + value),
- 0);
- }
+ System.out.println(" Max locals = "+codeAttrInfo.u2maxLocals);
+ System.out.println(" Max stack = "+codeAttrInfo.u2maxStack);
}
- }
-
- /**
- * Replaces the given float instruction by a simpler push instruction,
- * if possible.
- */
- private void replaceFloatPushInstruction(int offset, Instruction instruction)
- {
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
- if (pushedValue.isSpecific())
+ // Create new arrays for storing information at each instruction offset.
+ if (vars.length < codeLength)
{
- float value = pushedValue.floatValue().value();
- if (value == 0f ||
- value == 1f ||
- value == 2f)
- {
- replacePushInstruction(offset,
- (byte)(InstructionConstants.OP_FCONST_0 + value),
- 0);
- }
- }
- }
+ varProducerValues = new InstructionOffsetValue[codeLength];
+ stackProducerValues = new InstructionOffsetValue[codeLength];
+ unusedProducerValues = new InstructionOffsetValue[codeLength];
+ branchOriginValues = new InstructionOffsetValue[codeLength];
+ branchTargetValues = new InstructionOffsetValue[codeLength];
+ vars = new TracedVariables[codeLength];
+ stacks = new TracedStack[codeLength];
+ evaluationCounts = new int[codeLength];
+ initializedVariables = new int[codeLength];
-
- /**
- * Replaces the given double instruction by a simpler push instruction,
- * if possible.
- */
- private void replaceDoublePushInstruction(int offset, Instruction instruction)
- {
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
- if (pushedValue.isSpecific())
- {
- double value = pushedValue.doubleValue().value();
- if (value == 0.0 ||
- value == 1.0)
+ for (int index = 0; index < codeLength; index++)
{
- replacePushInstruction(offset,
- (byte)(InstructionConstants.OP_DCONST_0 + value),
- 0);
+ initializedVariables[index] = NONE;
}
}
- }
-
-
- /**
- * Replaces the given reference instruction by a simpler push instruction,
- * if possible.
- */
- private void replaceReferencePushInstruction(int offset, Instruction instruction)
- {
- Stack stack = stacks[offset];
- Value pushedValue = stack.getTop(0);
- if (pushedValue.isSpecific())
+ else
{
- ReferenceValue value = pushedValue.referenceValue();
- if (value.isNull() == Value.ALWAYS)
+ for (int index = 0; index < codeLength; index++)
{
- replacePushInstruction(offset,
- InstructionConstants.OP_ACONST_NULL,
- 0);
- }
- }
- }
-
+ varProducerValues[index] = null;
+ stackProducerValues[index] = null;
+ unusedProducerValues[index] = null;
+ branchOriginValues[index] = null;
+ branchTargetValues[index] = null;
+ evaluationCounts[index] = 0;
+ initializedVariables[index] = NONE;
- /**
- * Replaces the instruction at a given offset by a given push instruction.
- */
- private void replacePushInstruction(int offset, byte opcode, int value)
- {
- // Remember the replacement instruction.
- Instruction replacementInstruction =
- new SimpleInstruction(opcode, value).shrink();
-
- if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
-
- codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
- }
-
-
- /**
- * Deletes the given branch instruction, or replaces it by a simpler branch
- * instruction, if possible.
- */
- private void replaceBranchInstruction(int offset, Instruction instruction)
- {
- if (isTraced(offset))
- {
- InstructionOffsetValue branchTargetValue = branchTargetValues[offset];
-
- // Is there exactly one branch target (not from a goto or jsr)?
- if (branchTargetValue != null &&
- branchTargetValue.instructionOffsetCount() == 1 &&
- instruction.opcode != InstructionConstants.OP_GOTO &&
- instruction.opcode != InstructionConstants.OP_GOTO_W &&
- instruction.opcode != InstructionConstants.OP_JSR &&
- instruction.opcode != InstructionConstants.OP_JSR_W)
- {
- // Is it branching to the next instruction?
- int branchOffset = branchTargetValue.instructionOffset(0) - offset;
- if (branchOffset == instruction.length(offset))
+ if (vars[index] != null)
{
- if (DEBUG_ANALYSIS) System.out.println(" Deleting zero branch instruction at ["+offset+"]");
-
- // Delete the branch instruction.
- codeAttrInfoEditor.deleteInstruction(offset);
+ vars[index].reset(codeAttrInfo.u2maxLocals);
}
- else
- {
- // Replace the branch instruction by a simple branch instrucion.
- Instruction replacementInstruction =
- new BranchInstruction(InstructionConstants.OP_GOTO_W,
- branchOffset).shrink();
-
- if (DEBUG_ANALYSIS) System.out.println(" Replacing branch instruction at ["+offset+"] by "+replacementInstruction.toString());
- codeAttrInfoEditor.replaceInstruction(offset,
- replacementInstruction);
+ if (stacks[index] != null)
+ {
+ stacks[index].reset(codeAttrInfo.u2maxStack);
}
}
}
- }
-
-
- // Implementations for CpInfoVisitor.
-
- public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
- public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
- public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
- public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
- public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
- public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
- public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
- public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
- public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
- public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
- public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
- {
- isInitializer = methodrefCpInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
- }
-
-
- // Small utility methods.
-
- /**
- * Returns whether any of the instructions at the given offsets are marked as
- * necessary.
- */
- private boolean isAnyNecessary(InstructionOffsetValue traceValue)
- {
- int traceCount = traceValue.instructionOffsetCount();
-
- for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
- {
- int index = traceValue.instructionOffset(traceIndex);
-
- if (index == AT_METHOD_ENTRY ||
- (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index)))
- {
- return true;
- }
- }
- return false;
- }
+ // Reuse the existing variables and stack objects, ensuring the right size.
+ variables.reset(codeAttrInfo.u2maxLocals);
+ stack.reset(codeAttrInfo.u2maxStack);
+ // Initialize the variables with the parameters.
+ variables.initialize(parameters);
- /**
- * Returns whether all of the instructions at the given offsets are marked as
- * necessary.
- */
- private boolean isAllNecessary(InstructionOffsetValue traceValue)
- {
- int traceCount = traceValue.instructionOffsetCount();
+ // Set the store value of each parameter variable.
+ InstructionOffsetValue atMethodEntry = InstructionOffsetValueFactory.create(PartialEvaluator.AT_METHOD_ENTRY);
- for (int traceIndex = 0; traceIndex < traceCount; traceIndex++)
+ for (int index = 0; index < parameters.size(); index++)
{
- int index = traceValue.instructionOffset(traceIndex);
-
- if (index != AT_METHOD_ENTRY &&
- (!isNecessary[index] ||
- codeAttrInfoEditor.isModified(index)))
- {
- return false;
- }
+ variables.setStoredTraceValue(index, atMethodEntry);
}
- return true;
+ // Reset the return value.
+ branchUnit.setTraceReturnValue(null);
}
/**
- * Returns whether the given variable is ever referenced (stored) by an
- * instruction that is marked as necessary.
+ * Generalize the local variable frames of a block of instructions.
*/
- private boolean isVariableReferenced(CodeAttrInfo codeAttrInfo,
- int variableIndex)
+ private void generalizeVariables(int startOffset, int endOffset, TracedVariables generalizedVariables)
{
- int codeLength = codeAttrInfo.u4codeLength;
-
- for (int index = 0; index < codeLength; index++)
+ for (int index = startOffset; index < endOffset; index++)
{
- if (isNecessary[index] &&
- !codeAttrInfoEditor.isModified(index))
+ if (isTraced(index))
{
- Value traceValue = vars[index].getStoredTraceValue(variableIndex);
- if (traceValue != null &&
- isAnyNecessary(traceValue.instructionOffsetValue()))
- {
- return true;
- }
+ // We can't use the return value, because local generalization
+ // can be different a couple of times, with the global
+ // generalization being the same.
+ generalizedVariables.generalize(vars[index]);
}
}
-
- return false;
- }
-
-
- /**
- * Returns whether the instruction at the given offset has ever been
- * executed during the partial evaluation.
- */
- private boolean isTraced(int instructionOffset)
- {
- return evaluationCounts[instructionOffset] > 0;
}
}
diff --git a/src/proguard/optimize/evaluation/TracedStack.java b/src/proguard/optimize/evaluation/TracedStack.java
index 6304fe6..f6db30c 100644
--- a/src/proguard/optimize/evaluation/TracedStack.java
+++ b/src/proguard/optimize/evaluation/TracedStack.java
@@ -1,4 +1,4 @@
-/* $Id: TracedStack.java,v 1.9 2005/06/11 13:13:16 eric Exp $
+/* $Id: TracedStack.java,v 1.10 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -23,27 +23,35 @@ package proguard.optimize.evaluation;
import proguard.optimize.evaluation.value.*;
/**
- * This Stack saves a given store Value along with each Value it stores, and
- * at the same time generalizes a given trace Value with the store Value of
- * each Value it loads. The store Value and the trace Value can be set; the
- * generalized trace Value can be retrieved. The object is to store additional
- * information along with the actual stack values, for instance to keep track
- * of their origins.
+ * This Stack saves additional information with stack elements, to keep track
+ * of their origins and destionations.
+ * <p>
+ * The stack stores a given producer Value along with each Value it stores.
+ * It then generalizes a given collected Value with the producer Value
+ * of each Value it loads. The producer Value and the initial collected Value
+ * can be set; the generalized collected Value can be retrieved.
+ * <p>
+ * The stack also stores an empty consumer Value along with each Value it
+ * stores. It then generalizes and updates this consumer Value with the
+ * given producer Value each time the Value is loaded from the stack. The
+ * generalized consumer Value of each stack element can be retrieved.
*
* @author Eric Lafortune
*/
class TracedStack extends Stack
{
- private Stack traceStack;
- private Value storeValue;
- private Value traceValue;
+ private Value producerValue;
+ private Value collectedProducerValue;
+ private Stack producerStack;
+ private Stack consumerStack;
public TracedStack(int maxSize)
{
super(maxSize);
- traceStack = new Stack(maxSize);
+ producerStack = new Stack(maxSize);
+ consumerStack = new Stack(maxSize);
}
@@ -51,16 +59,18 @@ class TracedStack extends Stack
{
super(tracedStack);
- traceStack = new Stack(tracedStack.traceStack);
+ producerStack = new Stack(tracedStack.producerStack);
+ consumerStack = new Stack(tracedStack.consumerStack);
}
/**
- * Sets the Value that will be stored along with all store instructions.
+ * Sets the Value that will be stored along with all push and pop
+ * instructions.
*/
- public void setStoreValue(Value storeValue)
+ public void setProducerValue(Value producerValue)
{
- this.storeValue = storeValue;
+ this.producerValue = producerValue;
}
@@ -68,62 +78,99 @@ class TracedStack extends Stack
* Sets the initial Value with which all values stored along with load
* instructions will be generalized.
*/
- public void setTraceValue(Value traceValue)
+ public void setCollectedProducerValue(Value collectedProducerValue)
{
- this.traceValue = traceValue;
+ this.collectedProducerValue = collectedProducerValue;
}
- public Value getTraceValue()
+
+ public Value getCollectedProducerValue()
{
- return traceValue;
+ return collectedProducerValue;
}
/**
- * Gets the specified trace Value from the stack, without disturbing it.
+ * Gets the specified producer Value from the stack, without disturbing it.
* @param index the index of the stack element, counting from the bottom
* of the stack.
- * @return the trace value at the specified position.
+ * @return the producer value at the specified position.
*/
- public Value getBottomTraceValue(int index)
+ public Value getBottomProducerValue(int index)
{
- return traceStack.getBottom(index);
+ return producerStack.getBottom(index);
}
/**
- * Sets the specified trace Value on the stack, without disturbing it.
+ * Sets the specified producer Value on the stack, without disturbing it.
* @param index the index of the stack element, counting from the bottom
* of the stack.
- * @param value the trace value to set.
+ * @param value the producer value to set.
*/
- public void setBottomTraceValue(int index, Value value)
+ public void setBottomProducerValue(int index, Value value)
{
- traceStack.setBottom(index, value);
+ producerStack.setBottom(index, value);
}
/**
- * Gets the specified trace Value from the stack, without disturbing it.
+ * Gets the specified producer Value from the stack, without disturbing it.
* @param index the index of the stack element, counting from the top
* of the stack.
- * @return the trace value at the specified position.
+ * @return the producer value at the specified position.
+ */
+ public Value getTopProducerValue(int index)
+ {
+ return producerStack.getTop(index);
+ }
+
+
+ /**
+ * Sets the specified producer Value on the stack, without disturbing it.
+ * @param index the index of the stack element, counting from the top
+ * of the stack.
+ * @param value the producer value to set.
+ */
+ public void setTopProducerValue(int index, Value value)
+ {
+ producerStack.setTop(index, value);
+ }
+
+
+ /**
+ * Gets the specified consumer Value from the stack, without disturbing it.
+ * @param index the index of the stack element, counting from the bottom of
+ * the stack.
+ * @return the consumer value at the specified position.
+ */
+ public Value getBottomConsumerValue(int index)
+ {
+ return ((MutableValue)consumerStack.getBottom(index)).getContainedValue();
+ }
+
+ /**
+ * Gets the specified consumer Value from the stack, without disturbing it.
+ * @param index the index of the stack element, counting from the top of the
+ * stack.
+ * @return the consumer value at the specified position.
*/
- public Value getTopTraceValue(int index)
+ public Value getTopConsumerValue(int index)
{
- return traceStack.getTop(index);
+ return ((MutableValue)consumerStack.getTop(index)).getContainedValue();
}
/**
- * Sets the specified trace Value on the stack, without disturbing it.
+ * Sets the specified consumer Value on the stack, without disturbing it.
* @param index the index of the stack element, counting from the top
* of the stack.
- * @param value the trace value to set.
+ * @param value the consumer value to set.
*/
- public void setTopTraceValue(int index, Value value)
+ public void setTopConsumerValue(int index, Value value)
{
- traceStack.setTop(index, value);
+ ((MutableValue)consumerStack.getTop(index)).setContainedValue(value);
+ consumerStack.setTop(index, new MutableValue());
}
@@ -133,28 +180,32 @@ class TracedStack extends Stack
{
super.reset(size);
- traceStack.reset(size);
+ producerStack.reset(size);
+ consumerStack.reset(size);
}
public void copy(TracedStack other)
{
super.copy(other);
- traceStack.copy(other.traceStack);
+ producerStack.copy(other.producerStack);
+ consumerStack.copy(other.consumerStack);
}
public boolean generalize(TracedStack other)
{
return
super.generalize(other) |
- traceStack.generalize(other.traceStack);
+ producerStack.generalize(other.producerStack) |
+ consumerStack.generalize(other.consumerStack);
}
public void clear()
{
super.clear();
- traceStack.clear();
+ producerStack.clear();
+ consumerStack.clear();
}
public void push(Value value)
@@ -204,100 +255,105 @@ class TracedStack extends Stack
{
super.dup();
- // For now, we're letting all stack values that are somehow involved
- // depend on this instruction.
- Value tracePopValue = tracePop();
+ producerGeneralize(0);
+ producerStack.dup();
- tracePush();
- traceStack.push(tracePopValue);
+ consumerPop();
+ consumerPush();
+ consumerPush();
}
public void dup_x1()
{
super.dup_x1();
- // Let the duplicated value depend on this instruction.
- Value tracePopValue = tracePop();
- Value traceSkipValue = traceStack.pop();
+ producerGeneralize(0);
+ producerStack.dup_x1();
- tracePush();
- traceStack.push(traceSkipValue);
- traceStack.push(tracePopValue);
+ consumerPop();
+ consumerPush();
+ consumerStack.swap();
+ consumerPush();
}
public void dup_x2()
{
super.dup_x2();
- // Let the duplicated value depend on this instruction.
- Value tracePopValue = tracePop();
- Value traceSkipValue1 = traceStack.pop();
- Value traceSkipValue2 = traceStack.pop();
+ producerGeneralize(0);
+ producerStack.dup_x2();
- tracePush();
- traceStack.push(traceSkipValue2);
- traceStack.push(traceSkipValue1);
- traceStack.push(tracePopValue);
+ consumerPop();
+ consumerPush();
+ consumerStack.dup_x2();
+ consumerStack.pop();
+ consumerPush();
}
public void dup2()
{
super.dup2();
- // Let the duplicated value depend on this instruction.
- Value tracePopValue1 = tracePop();
- Value tracePopValue2 = tracePop();
+ producerGeneralize(0);
+ producerGeneralize(1);
+ producerStack.dup2();
- tracePush();
- tracePush();
- traceStack.push(tracePopValue2);
- traceStack.push(tracePopValue1);
+ consumerPop();
+ consumerPop();
+ consumerPush();
+ consumerPush();
+ consumerPush();
+ consumerPush();
}
public void dup2_x1()
{
super.dup2_x1();
- // Let the duplicated value depend on this instruction.
- Value tracePopValue1 = tracePop();
- Value tracePopValue2 = tracePop();
- Value traceSkipValue = traceStack.pop();
-
- tracePush();
- tracePush();
- traceStack.push(traceSkipValue);
- traceStack.push(tracePopValue2);
- traceStack.push(tracePopValue1);
+ producerGeneralize(0);
+ producerGeneralize(1);
+ producerStack.dup2_x1();
+
+ consumerPop();
+ consumerPop();
+ consumerPush();
+ consumerPush();
+ consumerStack.dup2_x1();
+ consumerStack.pop2();
+ consumerPush();
+ consumerPush();
}
public void dup2_x2()
{
super.dup2_x2();
- // Let the duplicated value depend on this instruction.
- Value tracePopValue1 = tracePop();
- Value tracePopValue2 = tracePop();
- Value traceSkipValue1 = traceStack.pop();
- Value traceSkipValue2 = traceStack.pop();
-
- tracePush();
- tracePush();
- traceStack.push(traceSkipValue2);
- traceStack.push(traceSkipValue1);
- traceStack.push(tracePopValue2);
- traceStack.push(tracePopValue1);
+ producerGeneralize(0);
+ producerGeneralize(1);
+ producerStack.dup2_x2();
+
+ consumerPop();
+ consumerPop();
+ consumerPush();
+ consumerPush();
+ consumerStack.dup2_x2();
+ consumerStack.pop2();
+ consumerPush();
+ consumerPush();
}
public void swap()
{
super.swap();
- // Let one of the swapped values depend on this instruction.
- tracePop();
- Value traceSwapValue = traceStack.pop();
+ producerGeneralize(0);
+ producerGeneralize(1);
+ producerStack.swap();
- tracePush();
- traceStack.push(traceSwapValue);
+ consumerPop();
+ consumerPop();
+ consumerPush();
+ consumerPush();
}
@@ -312,13 +368,17 @@ class TracedStack extends Stack
TracedStack other = (TracedStack)object;
- return super.equals(object) && this.traceStack.equals(other.traceStack);
+ return super.equals(object) &&
+ this.producerStack.equals(other.producerStack) &&
+ this.consumerStack.equals(other.consumerStack);
}
public int hashCode()
{
- return super.hashCode() ^ traceStack.hashCode();
+ return super.hashCode() ^
+ producerStack.hashCode() ^
+ consumerStack.hashCode();
}
@@ -328,12 +388,15 @@ class TracedStack extends Stack
for (int index = 0; index < this.size(); index++)
{
- Value value = this.values[index];
- Value tracedValue = traceStack.values[index];
+ Value value = this.values[index];
+ Value producerValue = producerStack.values[index];
+ Value consumerValue = consumerStack.values[index];
buffer = buffer.append('[')
- .append(tracedValue == null ? "empty" : tracedValue.toString())
+ .append(producerValue == null ? "empty" : producerValue.toString())
.append('>')
- .append(value == null ? "empty" : value.toString())
+ .append(value == null ? "empty" : value.toString())
+ .append('>')
+ .append(consumerValue == null ? "empty" : consumerValue.toString())
.append(']');
}
@@ -345,18 +408,156 @@ class TracedStack extends Stack
private void tracePush()
{
- traceStack.push(storeValue);
+ producerPush();
+ consumerPush();
+ }
+
+
+ private void producerPush()
+ {
+ producerStack.push(producerValue);
+ }
+
+
+ private void consumerPush()
+ {
+ consumerStack.push(new MutableValue());
+ }
+
+
+ private void tracePop()
+ {
+ producerPop();
+ consumerPop();
}
- private Value tracePop()
+ private void producerPop()
{
- Value popTraceValue = traceStack.pop();
- if (traceValue != null)
+ Value popProducerValue = producerStack.pop();
+ producerGeneralize(popProducerValue);
+ }
+
+
+ private void consumerPop()
+ {
+ MutableValue popConsumerValue = (MutableValue)consumerStack.pop();
+ popConsumerValue.generalizeContainedValue(producerValue);
+ }
+
+
+ private void producerGeneralize(int index)
+ {
+ // We're not remembering the producers for dup/swap calls.
+ producerGeneralize(producerStack.getTop(index));
+ }
+
+
+ private void producerGeneralize(Value producerValue)
+ {
+ if (collectedProducerValue != null)
+ {
+ collectedProducerValue = collectedProducerValue.generalize(producerValue);
+ }
+ }
+
+
+ /**
+ * This Value is a mutable wrapper for other Value instances.
+ * Its generalization method affects the Value itself as a side-effect.
+ */
+ private static class MutableValue extends Category1Value
+ {
+ Value containedValue;
+
+
+ public void generalizeContainedValue(Value containedValue)
{
- traceValue = traceValue.generalize(popTraceValue);
+ MutableValue lastMutableValue = lastMutableValue();
+ Value lastContainedValue= lastMutableValue.containedValue;
+
+ lastMutableValue.containedValue =
+ lastContainedValue == null ? containedValue :
+ containedValue.generalize(lastContainedValue);
}
- return popTraceValue;
+
+ public void setContainedValue(Value value)
+ {
+ lastMutableValue().containedValue = value;
+ }
+
+
+ public Value getContainedValue()
+ {
+ return lastMutableValue().containedValue;
+ }
+
+
+ // Implementations for Value.
+
+ public Value generalize(Value other)
+ {
+ MutableValue otherMutableValue = (MutableValue)other;
+
+ MutableValue thisLastMutableValue = this.lastMutableValue();
+ MutableValue otherLastMutableValue = otherMutableValue.lastMutableValue();
+
+ Value thisLastContainedValue = thisLastMutableValue.containedValue;
+ Value otherLastContainedValue = otherLastMutableValue.containedValue;
+
+ if (thisLastMutableValue != otherLastMutableValue)
+ {
+ otherLastMutableValue.containedValue = thisLastMutableValue;
+ }
+
+ thisLastMutableValue.containedValue =
+ thisLastContainedValue == null ? otherLastContainedValue :
+ otherLastContainedValue == null ? thisLastContainedValue :
+ thisLastContainedValue.generalize(otherLastContainedValue);
+ return thisLastMutableValue;
+ }
+
+
+ public int computationalType()
+ {
+ return 0;
+ }
+
+
+ // Implementations for Object.
+
+// public boolean equals(Object other)
+// {
+// return this.getClass() == other.getClass() &&
+// this.lastMutableValue() == ((MutableValue)other).lastMutableValue();
+// }
+//
+//
+// public int hashCode()
+// {
+// return lastMutableValue().containedValue.hashCode();
+// }
+
+
+ public String toString()
+ {
+ return containedValue == null ? "none" : containedValue.toString();
+ }
+
+
+ // Small utility methods.
+
+ public MutableValue lastMutableValue()
+ {
+ MutableValue mutableValue = this;
+
+ while (mutableValue.containedValue instanceof MutableValue)
+ {
+ mutableValue = (MutableValue)mutableValue.containedValue;
+ }
+
+ return mutableValue;
+ }
}
}
diff --git a/src/proguard/optimize/evaluation/TracedVariables.java b/src/proguard/optimize/evaluation/TracedVariables.java
index 8d7dadc..8066508 100644
--- a/src/proguard/optimize/evaluation/TracedVariables.java
+++ b/src/proguard/optimize/evaluation/TracedVariables.java
@@ -1,4 +1,4 @@
-/* $Id: TracedVariables.java,v 1.9 2005/06/11 13:13:16 eric Exp $
+/* $Id: TracedVariables.java,v 1.10 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -23,12 +23,13 @@ package proguard.optimize.evaluation;
import proguard.optimize.evaluation.value.*;
/**
- * This Variables class saves a given store Value along with each Value it
- * stores, and at the same time generalizes a given trace Value with the store
- * Value of each Value it loads. The store Value and the trace Value can be set;
- * the generalized trace Value can be retrieved. The object is to store
- * additional information along with the actual variable values, for instance
- * to keep track of their origins.
+ * This Variables class saves additional information with variables, to keep track
+ * of their origins.
+ * <p>
+ * The Variables class stores a given producer Value along with each Value it
+ * stores. It then generalizes a given collected Value with the producer Value
+ * of each Value it loads. The producer Value and the initial collected Value
+ * can be set; the generalized collected Value can be retrieved.
* <p>
* In addition, an initialization index can be reset and retrieved, pointing
* to the most recent variable that has been initialized by a store operation.
@@ -37,9 +38,10 @@ import proguard.optimize.evaluation.value.*;
*/
class TracedVariables extends Variables
{
- private Variables traceVariables;
- private Value storeValue;
- private Value traceValue;
+ private Value producerValue;
+ private Value collectedProducerValue;
+ private Variables producerVariables;
+// private Variables consumerVariables;
private int initializationIndex;
@@ -47,7 +49,7 @@ class TracedVariables extends Variables
{
super(size);
- traceVariables = new Variables(size);
+ producerVariables = new Variables(size);
}
@@ -55,16 +57,16 @@ class TracedVariables extends Variables
{
super(tracedVariables);
- traceVariables = new Variables(tracedVariables.traceVariables);
+ producerVariables = new Variables(tracedVariables.producerVariables);
}
/**
* Sets the Value that will be stored along with all store instructions.
*/
- public void setStoreValue(Value storeValue)
+ public void setProducerValue(Value producerValue)
{
- this.storeValue = storeValue;
+ this.producerValue = producerValue;
}
@@ -72,14 +74,14 @@ class TracedVariables extends Variables
* Sets the initial Value with which all values stored along with load
* instructions will be generalized.
*/
- public void setTraceValue(Value traceValue)
+ public void setCollectedProducerValue(Value collectedProducerValue)
{
- this.traceValue = traceValue;
+ this.collectedProducerValue = collectedProducerValue;
}
- public Value getTraceValue()
+ public Value getCollectedProducerValue()
{
- return traceValue;
+ return collectedProducerValue;
}
@@ -104,7 +106,7 @@ class TracedVariables extends Variables
*/
public Value getStoredTraceValue(int index)
{
- return traceVariables.load(index);
+ return producerVariables.load(index);
}
@@ -115,7 +117,7 @@ class TracedVariables extends Variables
*/
public void setStoredTraceValue(int index, Value value)
{
- traceVariables.store(index, value);
+ producerVariables.store(index, value);
}
@@ -125,21 +127,21 @@ class TracedVariables extends Variables
{
super.reset(size);
- traceVariables.reset(size);
+ producerVariables.reset(size);
}
public void initialize(TracedVariables other)
{
super.initialize(other);
- traceVariables.initialize(other.traceVariables);
+ producerVariables.initialize(other.producerVariables);
}
public boolean generalize(TracedVariables other)
{
return
super.generalize(other) |
- traceVariables.generalize(other.traceVariables);
+ producerVariables.generalize(other.producerVariables);
}
public void store(int index, Value value)
@@ -156,15 +158,15 @@ class TracedVariables extends Variables
super.store(index, value);
// Store the store value in its trace variable.
- traceVariables.store(index, storeValue);
+ producerVariables.store(index, producerValue);
}
public Value load(int index)
{
// Load and accumulate the store value of the variable.
- if (traceValue != null)
+ if (collectedProducerValue != null)
{
- traceValue = traceValue.generalize(traceVariables.load(index));
+ collectedProducerValue = collectedProducerValue.generalize(producerVariables.load(index));
}
// Return the value itself.
@@ -183,13 +185,13 @@ class TracedVariables extends Variables
TracedVariables other = (TracedVariables)object;
- return super.equals(object) && this.traceVariables.equals(other.traceVariables);
+ return super.equals(object) && this.producerVariables.equals(other.producerVariables);
}
public int hashCode()
{
- return super.hashCode() ^ traceVariables.hashCode();
+ return super.hashCode() ^ producerVariables.hashCode();
}
@@ -200,7 +202,7 @@ class TracedVariables extends Variables
for (int index = 0; index < this.size(); index++)
{
Value value = this.values[index];
- Value tracedValue = traceVariables.values[index];
+ Value tracedValue = producerVariables.values[index];
buffer = buffer.append('[')
.append(tracedValue == null ? "empty" : tracedValue.toString())
.append('>')
diff --git a/src/proguard/optimize/evaluation/UnusedParameterCleaner.java b/src/proguard/optimize/evaluation/UnusedParameterCleaner.java
index 6a0d689..b4b5619 100644
--- a/src/proguard/optimize/evaluation/UnusedParameterCleaner.java
+++ b/src/proguard/optimize/evaluation/UnusedParameterCleaner.java
@@ -1,4 +1,4 @@
-/* $Id: UnusedParameterCleaner.java,v 1.4 2005/06/11 13:13:16 eric Exp $
+/* $Id: UnusedParameterCleaner.java,v 1.5 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -147,11 +147,13 @@ implements InstructionVisitor,
if (traceValue != null)
{
- traceValue = traceValue.generalize(tracedStack.getTopTraceValue(stackIndex));
+ traceValue = traceValue.generalize(tracedStack.getTopProducerValue(stackIndex));
}
- tracedStack.setTopTraceValue(stackIndex,
- InstructionOffsetValueFactory.create());
+ tracedStack.setTopProducerValue(stackIndex,
+ InstructionOffsetValueFactory.create());
+ tracedStack.setTopConsumerValue(stackIndex,
+ InstructionOffsetValueFactory.create());
if (DEBUG)
{
diff --git a/src/proguard/optimize/evaluation/value/Category1Value.java b/src/proguard/optimize/evaluation/value/Category1Value.java
index 3aaef5b..feffd9b 100644
--- a/src/proguard/optimize/evaluation/value/Category1Value.java
+++ b/src/proguard/optimize/evaluation/value/Category1Value.java
@@ -1,4 +1,4 @@
-/* $Id: Category1Value.java,v 1.3 2005/06/11 13:13:16 eric Exp $
+/* $Id: Category1Value.java,v 1.4 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -25,7 +25,7 @@ package proguard.optimize.evaluation.value;
*
* @author Eric Lafortune
*/
-abstract class Category1Value extends Value
+public abstract class Category1Value extends Value
{
// Implementations for Value.
diff --git a/src/proguard/optimize/evaluation/value/Category2Value.java b/src/proguard/optimize/evaluation/value/Category2Value.java
index 6ebd773..3857132 100644
--- a/src/proguard/optimize/evaluation/value/Category2Value.java
+++ b/src/proguard/optimize/evaluation/value/Category2Value.java
@@ -1,4 +1,4 @@
-/* $Id: Category2Value.java,v 1.3 2005/06/11 13:13:16 eric Exp $
+/* $Id: Category2Value.java,v 1.4 2005/10/22 11:55:29 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -25,7 +25,7 @@ package proguard.optimize.evaluation.value;
*
* @author Eric Lafortune
*/
-abstract class Category2Value extends Value
+public abstract class Category2Value extends Value
{
// Implementations for Value.
diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java
index 25f04ba..f2083aa 100644
--- a/src/proguard/optimize/peephole/BranchTargetFinder.java
+++ b/src/proguard/optimize/peephole/BranchTargetFinder.java
@@ -1,4 +1,4 @@
-/* $Id: BranchTargetFinder.java,v 1.6 2005/06/11 13:13:16 eric Exp $
+/* $Id: BranchTargetFinder.java,v 1.8 2005/08/13 20:59:45 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -27,17 +27,29 @@ import proguard.classfile.instruction.*;
import proguard.classfile.visitor.*;
/**
- * This AttrInfoVisitor finds all branch targets in the CodeAttrInfo objects
- * that it visits.
+ * This AttrInfoVisitor finds all instruction offsets, branch targets, and
+ * exception targets in the CodeAttrInfo objects that it visits.
*
* @author Eric Lafortune
*/
public class BranchTargetFinder
implements AttrInfoVisitor,
InstructionVisitor,
- ExceptionInfoVisitor
+ ExceptionInfoVisitor,
+ CpInfoVisitor
{
- private boolean[] isBranchTarget;
+ private static final byte INSTRUCTION = 1;
+ private static final byte BRANCH_ORIGIN = 2;
+ private static final byte BRANCH_TARGET = 4;
+ private static final byte INITIALIZER = 8;
+ private static final byte EXCEPTION_START = 16;
+ private static final byte EXCEPTION_END = 32;
+ private static final byte EXCEPTION_HANDLER = 64;
+
+
+ private byte[] instructionMarks;
+
+ private boolean isInitializer;
/**
@@ -47,7 +59,41 @@ implements AttrInfoVisitor,
*/
public BranchTargetFinder(int codeLength)
{
- isBranchTarget = new boolean[codeLength + 1];
+ instructionMarks = new byte[codeLength + 1];
+ }
+
+
+ /**
+ * Returns whether there is an instruction at the given offset in the
+ * CodeAttrInfo that was visited most recently.
+ */
+ public boolean isInstruction(int offset)
+ {
+ return (instructionMarks[offset] & INSTRUCTION) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the target of a
+ * branch instruction or an exception in the CodeAttrInfo that was visited
+ * most recently.
+ */
+ public boolean isTarget(int offset)
+ {
+ return (instructionMarks[offset] & (BRANCH_TARGET |
+ EXCEPTION_START |
+ EXCEPTION_END |
+ EXCEPTION_HANDLER)) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the origin of a
+ * branch instruction in the CodeAttrInfo that was visited most recently.
+ */
+ public boolean isBranchOrigin(int offset)
+ {
+ return (instructionMarks[offset] & BRANCH_ORIGIN) != 0;
}
@@ -57,7 +103,48 @@ implements AttrInfoVisitor,
*/
public boolean isBranchTarget(int offset)
{
- return isBranchTarget[offset];
+ return (instructionMarks[offset] & BRANCH_TARGET) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the start of an
+ * exception try block in the CodeAttrInfo that was visited most recently.
+ */
+ public boolean isExceptionStart(int offset)
+ {
+ return (instructionMarks[offset] & EXCEPTION_START) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the end of an
+ * exception try block in the CodeAttrInfo that was visited most recently.
+ */
+ public boolean isExceptionEnd(int offset)
+ {
+ return (instructionMarks[offset] & EXCEPTION_END) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the start of an
+ * exception catch block in the CodeAttrInfo that was visited most recently.
+ */
+ public boolean isExceptionHandler(int offset)
+ {
+ return (instructionMarks[offset] & EXCEPTION_HANDLER) != 0;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is the special
+ * invocation of an instance initializer in the CodeAttrInfo that was
+ * visited most recently.
+ */
+ public boolean isInitializer(int offset)
+ {
+ return (instructionMarks[offset] & INITIALIZER) != 0;
}
@@ -87,23 +174,23 @@ implements AttrInfoVisitor,
{
// Make sure there is a sufficiently large boolean array.
int length = codeAttrInfo.u4codeLength + 1;
- if (isBranchTarget.length < length)
+ if (instructionMarks.length < length)
{
// Create a new boolean array.
- isBranchTarget = new boolean[length];
+ instructionMarks = new byte[length];
}
else
{
// Reset the boolean array.
for (int index = 0; index < length; index++)
{
- isBranchTarget[index] = false;
+ instructionMarks[index] = 0;
}
}
// The first instruction and the end of the code are always branch targets.
- isBranchTarget[0] = true;
- isBranchTarget[codeAttrInfo.u4codeLength] = true;
+ instructionMarks[0] = BRANCH_TARGET;
+ instructionMarks[codeAttrInfo.u4codeLength] = BRANCH_TARGET;
// Mark branch targets by going over all instructions.
codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
@@ -115,21 +202,63 @@ implements AttrInfoVisitor,
// Implementations for InstructionVisitor.
- public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
- public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
- public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+ public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+ {
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION;
+
+ byte opcode = simpleInstruction.opcode;
+ if (opcode == InstructionConstants.OP_RET ||
+ opcode == InstructionConstants.OP_IRETURN ||
+ opcode == InstructionConstants.OP_LRETURN ||
+ opcode == InstructionConstants.OP_FRETURN ||
+ opcode == InstructionConstants.OP_DRETURN ||
+ opcode == InstructionConstants.OP_ARETURN ||
+ opcode == InstructionConstants.OP_ATHROW)
+ {
+ instructionMarks[offset] |= BRANCH_ORIGIN;
+ }
+ }
+
+
+ public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+ {
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION;
+
+ // Check if the instruction is an initializer invocation.
+ isInitializer = false;
+ classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
+ if (isInitializer)
+ {
+ instructionMarks[offset] |= INITIALIZER;
+ }
+ }
+
+
+ public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+ {
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION;
+ }
public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
{
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+
// Mark the branch target.
- isBranchTarget[offset + branchInstruction.branchOffset] = true;
+ instructionMarks[offset + branchInstruction.branchOffset] |= BRANCH_TARGET;
}
public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
{
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+
// Mark the branch targets of the default jump offset.
- isBranchTarget[offset + tableSwitchInstruction.defaultOffset] = true;
+ instructionMarks[offset + tableSwitchInstruction.defaultOffset] |= BRANCH_TARGET;
// Mark the branch targets of the jump offsets.
markBranchTargets(offset,
@@ -140,8 +269,11 @@ implements AttrInfoVisitor,
public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
{
+ // Mark the instruction.
+ instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+
// Mark the branch targets of the default jump offset.
- isBranchTarget[offset + lookUpSwitchInstruction.defaultOffset] = true;
+ instructionMarks[offset + lookUpSwitchInstruction.defaultOffset] |= BRANCH_TARGET;
// Mark the branch targets of the jump offsets.
markBranchTargets(offset,
@@ -150,15 +282,35 @@ implements AttrInfoVisitor,
}
+ // Implementations for CpInfoVisitor.
+
+ public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
+ public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
+ public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
+ public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
+ public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
+ public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+ public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
+ public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
+ public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
+ public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+
+
+ public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+ {
+ isInitializer = methodrefCpInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+ }
+
+
// Implementations for ExceptionInfoVisitor.
public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
{
// Remap the code offsets. Note that the branch target array also has
// an entry for the first offset after the code, for u2endpc.
- isBranchTarget[exceptionInfo.u2startpc] = true;
- isBranchTarget[exceptionInfo.u2endpc] = true;
- isBranchTarget[exceptionInfo.u2handlerpc] = true;
+ instructionMarks[exceptionInfo.u2startpc] |= EXCEPTION_START;
+ instructionMarks[exceptionInfo.u2endpc] |= EXCEPTION_END;
+ instructionMarks[exceptionInfo.u2handlerpc] |= EXCEPTION_HANDLER;
}
@@ -172,7 +324,7 @@ implements AttrInfoVisitor,
{
for (int index = 0; index < length; index++)
{
- isBranchTarget[offset + jumpOffsets[index]] = true;
+ instructionMarks[offset + jumpOffsets[index]] |= BRANCH_TARGET;
}
}
}
diff --git a/src/proguard/optimize/peephole/ClassFileFinalizer.java b/src/proguard/optimize/peephole/ClassFileFinalizer.java
index a8c4bf3..5861b2f 100644
--- a/src/proguard/optimize/peephole/ClassFileFinalizer.java
+++ b/src/proguard/optimize/peephole/ClassFileFinalizer.java
@@ -1,4 +1,4 @@
-/* $Id: ClassFileFinalizer.java,v 1.7 2004/12/30 16:49:08 eric Exp $
+/* $Id: ClassFileFinalizer.java,v 1.8 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -27,7 +27,7 @@ import proguard.optimize.*;
/**
* This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
- * makes the class files it visits, and their class members, final, if possible.
+ * makes the class files it visits, and their methods, final, if possible.
*
* @author Eric Lafortune
*/
@@ -35,9 +35,36 @@ public class ClassFileFinalizer
implements ClassFileVisitor,
MemberInfoVisitor
{
+ private ClassFileVisitor extraClassFileVisitor;
+ private MemberInfoVisitor extraMemberInfoVisitor;
+
private MemberFinder memberFinder = new MemberFinder();
+ /**
+ * Creates a new ClassFileFinalizer.
+ */
+ public ClassFileFinalizer()
+ {
+ this(null, null);
+ }
+
+
+ /**
+ * Creates a new ClassFileFinalizer.
+ * @param extraClassFileVisitor an optional extra visitor for all finalized
+ * class files.
+ * @param extraMemberInfoVisitor an optional extra visitor for all finalized
+ * methods.
+ */
+ public ClassFileFinalizer(ClassFileVisitor extraClassFileVisitor,
+ MemberInfoVisitor extraMemberInfoVisitor)
+ {
+ this.extraClassFileVisitor = extraClassFileVisitor;
+ this.extraMemberInfoVisitor = extraMemberInfoVisitor;
+ }
+
+
// Implementations for ClassFileVisitor.
public void visitProgramClassFile(ProgramClassFile programClassFile)
@@ -52,6 +79,12 @@ public class ClassFileFinalizer
programClassFile.subClasses == null)
{
programClassFile.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
+
+ // Visit the class file, if required.
+ if (extraClassFileVisitor != null)
+ {
+ extraClassFileVisitor.visitProgramClassFile(programClassFile);
+ }
}
// Check all methods.
@@ -87,6 +120,12 @@ public class ClassFileFinalizer
!memberFinder.isOverriden(programClassFile, programMethodInfo)))))
{
programMethodInfo.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
+
+ // Visit the method, if required.
+ if (extraMemberInfoVisitor != null)
+ {
+ extraMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+ }
}
}
diff --git a/src/proguard/optimize/peephole/GetterSetterInliner.java b/src/proguard/optimize/peephole/GetterSetterInliner.java
index 34f4142..0fab145 100644
--- a/src/proguard/optimize/peephole/GetterSetterInliner.java
+++ b/src/proguard/optimize/peephole/GetterSetterInliner.java
@@ -1,4 +1,4 @@
-/* $Id: GetterSetterInliner.java,v 1.17 2005/06/11 13:13:16 eric Exp $
+/* $Id: GetterSetterInliner.java,v 1.18 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -44,8 +44,9 @@ implements InstructionVisitor,
new MyGetterSetterChecker());
private MemberFinder memberFinder = new MemberFinder();
- private CodeAttrInfoEditor codeAttrInfoEditor;
private boolean allowAccessModification;
+ private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
// Return values of the getter/setter checker.
@@ -57,17 +58,36 @@ implements InstructionVisitor,
/**
* Creates a new GetterSetterInliner.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
* @param allowAccessModification indicates whether the access modifiers of
* a field can be changed in order to inline
* its getter or setter.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
- public GetterSetterInliner(CodeAttrInfoEditor codeAttrInfoEditor,
- boolean allowAccessModification)
+ public GetterSetterInliner(boolean allowAccessModification,
+ CodeAttrInfoEditor codeAttrInfoEditor)
+ {
+ this(allowAccessModification, codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new GetterSetterInliner.
+ * @param allowAccessModification indicates whether the access modifiers of
+ * a field can be changed in order to inline
+ * its getter or setter.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all replaced
+ * store instructions.
+ */
+ public GetterSetterInliner(boolean allowAccessModification,
+ CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
{
- this.codeAttrInfoEditor = codeAttrInfoEditor;
this.allowAccessModification = allowAccessModification;
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -110,6 +130,12 @@ implements InstructionVisitor,
fieldrefCpInfoIndex).shrink();
codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, cpInstruction);
+ }
}
}
}
diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
new file mode 100644
index 0000000..7dc1e93
--- /dev/null
+++ b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
@@ -0,0 +1,232 @@
+/* $Id: GotoCommonCodeReplacer.java,v 1.2 2005/10/04 21:00:11 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2005 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttrInfo;
+import proguard.classfile.editor.CodeAttrInfoEditor;
+import proguard.classfile.instruction.*;
+
+/**
+ * This InstructionVisitor redirects unconditional branches so any common code
+ * is shared, and the code preceding the branch can be removed.
+ *
+ * @author Eric Lafortune
+ */
+public class GotoCommonCodeReplacer implements InstructionVisitor
+{
+ private static final boolean DEBUG = false;
+
+
+ private BranchTargetFinder branchTargetFinder;
+ private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
+
+
+ /**
+ * Creates a new GotoCommonCodeReplacer.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ */
+ public GotoCommonCodeReplacer(BranchTargetFinder branchTargetFinder,
+ CodeAttrInfoEditor codeAttrInfoEditor)
+ {
+ this(branchTargetFinder, codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new GotoCommonCodeReplacer.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all replaced
+ * goto instructions.
+ */
+ public GotoCommonCodeReplacer(BranchTargetFinder branchTargetFinder,
+ CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.branchTargetFinder = branchTargetFinder;
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
+ public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
+ public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
+ public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
+ public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+
+
+ public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+ {
+ // Check if the instruction is an unconditional goto instruction that
+ // isn't the target of a branch itself.
+ byte opcode = branchInstruction.opcode;
+ if ((opcode == InstructionConstants.OP_GOTO ||
+ opcode == InstructionConstants.OP_GOTO_W) &&
+ !branchTargetFinder.isBranchTarget(offset))
+ {
+ int branchOffset = branchInstruction.branchOffset;
+ int targetOffset = offset + branchOffset;
+
+ // Get the number of common bytes.
+ int commonCount = commonByteCodeCount(codeAttrInfo, offset, targetOffset);
+
+ if (commonCount > 0 &&
+ !exceptionBoundary(codeAttrInfo, offset, targetOffset))
+ {
+ if (DEBUG)
+ {
+ System.out.println("GotoCommonCodeReplacer: "+classFile.getName()+"."+methodInfo.getName(classFile)+" ("+commonCount+" instructions)");
+ }
+
+ // Delete the common instructions.
+ for (int delta = 0; delta <= commonCount; delta++)
+ {
+ int deleteOffset = offset - delta;
+ if (branchTargetFinder.isInstruction(deleteOffset))
+ {
+ codeAttrInfoEditor.replaceInstruction( deleteOffset, null);
+ codeAttrInfoEditor.insertBeforeInstruction(deleteOffset, null);
+ codeAttrInfoEditor.insertAfterInstruction( deleteOffset, null);
+
+ codeAttrInfoEditor.deleteInstruction(deleteOffset);
+ }
+ }
+
+ // Redirect the goto instruction, if it is still necessary.
+ int newBranchOffset = branchOffset - commonCount;
+ if (newBranchOffset != branchInstruction.length(offset))
+ {
+ Instruction newGotoInstruction =
+ new BranchInstruction(opcode, newBranchOffset);
+ codeAttrInfoEditor.replaceInstruction(offset,
+ newGotoInstruction);
+ }
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+ }
+ }
+ }
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Returns the number of common bytes preceding the given offsets,
+ * avoiding branches and exception blocks.
+ */
+ private int commonByteCodeCount(CodeAttrInfo codeAttrInfo, int offset1, int offset2)
+ {
+ // Find the block of common instructions preceding it.
+ byte[] code = codeAttrInfo.code;
+
+ int successfulDelta = 0;
+
+ for (int delta = 1;
+ delta <= offset1 &&
+ delta <= offset2 &&
+ offset2 - delta != offset1;
+ delta++)
+ {
+ int newOffset1 = offset1 - delta;
+ int newOffset2 = offset2 - delta;
+
+ // Is the code identical at both offsets?
+ if (code[newOffset1] != code[newOffset2])
+ {
+ break;
+ }
+
+ // Are there instructions at either offset but not both?
+ if (branchTargetFinder.isInstruction(newOffset1) ^
+ branchTargetFinder.isInstruction(newOffset2))
+ {
+ break;
+ }
+
+ // Are there instructions at both offsets?
+ if (branchTargetFinder.isInstruction(newOffset1) &&
+ branchTargetFinder.isInstruction(newOffset2))
+ {
+ // Are the offsets involved in some branches?
+ // Note that the preverifier also doesn't like
+ // initializer invocations to be moved around.
+ if (branchTargetFinder.isBranchOrigin(newOffset1) ||
+ branchTargetFinder.isBranchTarget(newOffset1) ||
+ branchTargetFinder.isExceptionStart(newOffset1) ||
+ branchTargetFinder.isExceptionEnd(newOffset1) ||
+ branchTargetFinder.isInitializer(newOffset1) ||
+ branchTargetFinder.isExceptionStart(newOffset2) ||
+ branchTargetFinder.isExceptionEnd(newOffset2))
+ {
+ break;
+ }
+
+ successfulDelta = delta;
+ }
+ }
+
+ return successfulDelta;
+ }
+
+
+ /**
+ * Returns the whether there is a boundary of an exception block between
+ * the given offsets (including both).
+ */
+ private boolean exceptionBoundary(CodeAttrInfo codeAttrInfo, int offset1, int offset2)
+ {
+ // Swap the offsets if the second one is smaller than the first one.
+ if (offset2 < offset1)
+ {
+ int offset = offset1;
+ offset1 = offset2;
+ offset2 = offset;
+ }
+
+ // Check if there is a boundary of an exception block.
+ for (int offset = offset1; offset <= offset2; offset++)
+ {
+ if (branchTargetFinder.isExceptionStart(offset) ||
+ branchTargetFinder.isExceptionEnd(offset))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java
similarity index 53%
copy from src/proguard/optimize/peephole/GotoReturnReplacer.java
copy to src/proguard/optimize/peephole/GotoGotoReplacer.java
index 42d6e6c..ecc8f6d 100644
--- a/src/proguard/optimize/peephole/GotoReturnReplacer.java
+++ b/src/proguard/optimize/peephole/GotoGotoReplacer.java
@@ -1,4 +1,4 @@
-/* $Id: GotoReturnReplacer.java,v 1.7 2005/06/11 13:13:16 eric Exp $
+/* $Id: GotoGotoReplacer.java,v 1.2 2005/10/04 21:40:37 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -26,24 +26,40 @@ import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
/**
- * This InstructionVisitor replaces unconditional branches to return instructions
- * by these same return instructions.
+ * This InstructionVisitor simplifies unconditional branches to other
+ * unconditional branches.
*
* @author Eric Lafortune
*/
-public class GotoReturnReplacer implements InstructionVisitor
+public class GotoGotoReplacer implements InstructionVisitor
{
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
- * Creates a new GotoReturnReplacer.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * Creates a new GotoGotoReplacer.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
- public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
+ public GotoGotoReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new GotoGotoReplacer.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all replaced
+ * goto instructions.
+ */
+ public GotoGotoReplacer(CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -63,28 +79,33 @@ public class GotoReturnReplacer implements InstructionVisitor
if (opcode == InstructionConstants.OP_GOTO ||
opcode == InstructionConstants.OP_GOTO_W)
{
- // Check if the goto instruction points to a return instruction.
- int targetOffset = offset + branchInstruction.branchOffset;
+ // Check if the goto instruction points to another simple goto
+ // instruction.
+ int branchOffset = branchInstruction.branchOffset;
+ int targetOffset = offset + branchOffset;
- if (!codeAttrInfoEditor.isModified(offset) &&
+ if (branchOffset != branchInstruction.length(offset) &&
+ !codeAttrInfoEditor.isModified(offset) &&
!codeAttrInfoEditor.isModified(targetOffset))
{
Instruction targetInstruction = InstructionFactory.create(codeAttrInfo.code,
targetOffset);
- switch (targetInstruction.opcode)
+ if (targetInstruction.opcode == InstructionConstants.OP_GOTO)
{
- case InstructionConstants.OP_IRETURN:
- case InstructionConstants.OP_LRETURN:
- case InstructionConstants.OP_FRETURN:
- case InstructionConstants.OP_DRETURN:
- case InstructionConstants.OP_ARETURN:
- case InstructionConstants.OP_RETURN:
- // Replace the goto instruction by the return instruction.
- Instruction returnInstruction =
- new SimpleInstruction(targetInstruction.opcode);
- codeAttrInfoEditor.replaceInstruction(offset,
- returnInstruction);
- break;
+ // Simplify the goto instruction.
+ int targetBranchOffset = ((BranchInstruction)targetInstruction).branchOffset;
+
+ Instruction newBranchInstruction =
+ new BranchInstruction(opcode,
+ (branchOffset + targetBranchOffset));
+ codeAttrInfoEditor.replaceInstruction(offset,
+ newBranchInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+ }
}
}
}
diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java
index 42d6e6c..9b0bd5b 100644
--- a/src/proguard/optimize/peephole/GotoReturnReplacer.java
+++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java
@@ -1,4 +1,4 @@
-/* $Id: GotoReturnReplacer.java,v 1.7 2005/06/11 13:13:16 eric Exp $
+/* $Id: GotoReturnReplacer.java,v 1.8 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -34,16 +34,32 @@ import proguard.classfile.instruction.*;
public class GotoReturnReplacer implements InstructionVisitor
{
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
* Creates a new GotoReturnReplacer.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new GotoReturnReplacer.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all replaced
+ * goto instructions.
+ */
+ public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -84,6 +100,13 @@ public class GotoReturnReplacer implements InstructionVisitor
new SimpleInstruction(targetInstruction.opcode);
codeAttrInfoEditor.replaceInstruction(offset,
returnInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+ }
+
break;
}
}
diff --git a/src/proguard/optimize/peephole/LoadStoreRemover.java b/src/proguard/optimize/peephole/LoadStoreRemover.java
index c98c01d..86c69c0 100644
--- a/src/proguard/optimize/peephole/LoadStoreRemover.java
+++ b/src/proguard/optimize/peephole/LoadStoreRemover.java
@@ -1,4 +1,4 @@
-/* $Id: LoadStoreRemover.java,v 1.8 2005/06/11 13:13:16 eric Exp $
+/* $Id: LoadStoreRemover.java,v 1.10 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -34,21 +34,41 @@ public class LoadStoreRemover implements InstructionVisitor
{
private BranchTargetFinder branchTargetFinder;
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
* Creates a new LoadStoreRemover.
- * @param branchTargetFinder a branch target finder that has been
- * initialized to indicate branch targets
- * in the visited code.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
public LoadStoreRemover(BranchTargetFinder branchTargetFinder,
- CodeAttrInfoEditor codeAttrInfoEditor)
+ CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.branchTargetFinder = branchTargetFinder;
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(branchTargetFinder, codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new LoadStoreRemover.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all deleted
+ * load instructions.
+ */
+ public LoadStoreRemover(BranchTargetFinder branchTargetFinder,
+ CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.branchTargetFinder = branchTargetFinder;
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -73,7 +93,7 @@ public class LoadStoreRemover implements InstructionVisitor
if (!codeAttrInfoEditor.isModified(offset) &&
!codeAttrInfoEditor.isModified(nextOffset) &&
- !branchTargetFinder.isBranchTarget(nextOffset))
+ !branchTargetFinder.isTarget(nextOffset))
{
// Is the next instruction a corresponding store instruction?
Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
@@ -89,6 +109,12 @@ public class LoadStoreRemover implements InstructionVisitor
// Delete both instructions.
codeAttrInfoEditor.deleteInstruction(offset);
codeAttrInfoEditor.deleteInstruction(nextOffset);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+ }
}
}
}
diff --git a/src/proguard/optimize/peephole/MethodPrivatizer.java b/src/proguard/optimize/peephole/MethodPrivatizer.java
index 0578abd..e2f8502 100644
--- a/src/proguard/optimize/peephole/MethodPrivatizer.java
+++ b/src/proguard/optimize/peephole/MethodPrivatizer.java
@@ -1,4 +1,4 @@
-/* $Id: MethodPrivatizer.java,v 1.5 2005/06/11 13:21:35 eric Exp $
+/* $Id: MethodPrivatizer.java,v 1.7 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -35,21 +35,48 @@ import proguard.optimize.NonPrivateMethodMarker;
public class MethodPrivatizer
implements MemberInfoVisitor
{
+ private MemberInfoVisitor extraMemberInfoVisitor;
+
+
+ /**
+ * Creates a new MethodPrivatizer.
+ */
+ public MethodPrivatizer()
+ {
+ this(null);
+ }
+
+
+ /**
+ * Creates a new MethodPrivatizer.
+ * @param extraMemberInfoVisitor an optional extra visitor for all privatized
+ * methods.
+ */
+ public MethodPrivatizer(MemberInfoVisitor extraMemberInfoVisitor)
+ {
+ this.extraMemberInfoVisitor = extraMemberInfoVisitor;
+ }
+
+
// Implementations for MemberInfoVisitor.
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
{
- int accessFlags = programMethodInfo.getAccessFlags();
-
// Is the method unmarked?
if (NonPrivateMethodMarker.canBeMadePrivate(programMethodInfo))
{
// Make the method private.
programMethodInfo.u2accessFlags =
- AccessUtil.replaceAccessFlags(accessFlags,
+ AccessUtil.replaceAccessFlags(programMethodInfo.u2accessFlags,
ClassConstants.INTERNAL_ACC_PRIVATE);
+
+ // Visit the method, if required.
+ if (extraMemberInfoVisitor != null)
+ {
+ extraMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+ }
}
}
diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java
index e018f25..4fe53cb 100644
--- a/src/proguard/optimize/peephole/NopRemover.java
+++ b/src/proguard/optimize/peephole/NopRemover.java
@@ -1,4 +1,4 @@
-/* $Id: NopRemover.java,v 1.6 2005/06/11 13:13:16 eric Exp $
+/* $Id: NopRemover.java,v 1.7 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -34,16 +34,32 @@ import proguard.classfile.visitor.*;
public class NopRemover implements InstructionVisitor
{
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
* Creates a new NopRemover.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
public NopRemover(CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new NopRemover.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all removed
+ * nop instructions.
+ */
+ public NopRemover(CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -63,6 +79,12 @@ public class NopRemover implements InstructionVisitor
!codeAttrInfoEditor.isModified(offset))
{
codeAttrInfoEditor.deleteInstruction(offset);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, simpleInstruction);
+ }
}
}
}
diff --git a/src/proguard/optimize/peephole/PushPopRemover.java b/src/proguard/optimize/peephole/PushPopRemover.java
index 16394e9..50f5939 100644
--- a/src/proguard/optimize/peephole/PushPopRemover.java
+++ b/src/proguard/optimize/peephole/PushPopRemover.java
@@ -1,4 +1,4 @@
-/* $Id: PushPopRemover.java,v 1.9 2005/06/11 13:13:16 eric Exp $
+/* $Id: PushPopRemover.java,v 1.11 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -36,21 +36,41 @@ public class PushPopRemover implements InstructionVisitor
{
private BranchTargetFinder branchTargetFinder;
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
* Creates a new PushPopRemover.
- * @param branchTargetFinder a branch target finder that has been
- * initialized to indicate branch targets
- * in the visited code.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
public PushPopRemover(BranchTargetFinder branchTargetFinder,
CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.branchTargetFinder = branchTargetFinder;
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(branchTargetFinder, codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new PushPopRemover.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all deleted
+ * push instructions.
+ */
+ public PushPopRemover(BranchTargetFinder branchTargetFinder,
+ CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.branchTargetFinder = branchTargetFinder;
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -89,7 +109,7 @@ public class PushPopRemover implements InstructionVisitor
case InstructionConstants.OP_LDC_W:
case InstructionConstants.OP_LDC2_W:
// All these simple instructions are pushing instructions.
- deleteWithSubsequentPop(codeAttrInfo, offset, simpleInstruction);
+ deleteWithSubsequentPop(classFile, methodInfo, codeAttrInfo, offset, simpleInstruction);
break;
}
}
@@ -100,7 +120,7 @@ public class PushPopRemover implements InstructionVisitor
variableInstruction.opcode != InstructionConstants.OP_RET)
{
// All load instructions are pushing instructions.
- deleteWithSubsequentPop(codeAttrInfo, offset, variableInstruction);
+ deleteWithSubsequentPop(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
}
}
@@ -111,7 +131,9 @@ public class PushPopRemover implements InstructionVisitor
* Deletes the given instruction and its subsequent compatible pop instruction,
* if any, and if the latter is not a branch target.
*/
- private void deleteWithSubsequentPop(CodeAttrInfo codeAttrInfo,
+ private void deleteWithSubsequentPop(ClassFile classFile,
+ MethodInfo methodInfo,
+ CodeAttrInfo codeAttrInfo,
int offset,
Instruction instruction)
{
@@ -121,7 +143,7 @@ public class PushPopRemover implements InstructionVisitor
if (!codeAttrInfoEditor.isModified(offset) &&
!codeAttrInfoEditor.isModified(nextOffset) &&
- !branchTargetFinder.isBranchTarget(nextOffset))
+ !branchTargetFinder.isTarget(nextOffset))
{
Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
nextOffset);
@@ -133,6 +155,12 @@ public class PushPopRemover implements InstructionVisitor
// Delete the pushing instruction and the pop instruction.
codeAttrInfoEditor.deleteInstruction(offset);
codeAttrInfoEditor.deleteInstruction(nextOffset);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ instruction.accept(classFile, methodInfo, codeAttrInfo, offset, extraInstructionVisitor);
+ }
}
}
}
diff --git a/src/proguard/optimize/peephole/SingleImplementationMarker.java b/src/proguard/optimize/peephole/SingleImplementationMarker.java
index 520e0d7..ce8abb0 100644
--- a/src/proguard/optimize/peephole/SingleImplementationMarker.java
+++ b/src/proguard/optimize/peephole/SingleImplementationMarker.java
@@ -1,4 +1,4 @@
-/* $Id: SingleImplementationMarker.java,v 1.7 2005/06/26 16:20:23 eric Exp $
+/* $Id: SingleImplementationMarker.java,v 1.8 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -39,7 +39,8 @@ implements ClassFileVisitor
private static final boolean DEBUG = false;
- private boolean allowAccessModification;
+ private boolean allowAccessModification;
+ private ClassFileVisitor extraClassFileVisitor;
/**
@@ -50,7 +51,23 @@ implements ClassFileVisitor
*/
public SingleImplementationMarker(boolean allowAccessModification)
{
+ this(allowAccessModification, null);
+ }
+
+
+ /**
+ * Creates a new SingleImplementationMarker.
+ * @param allowAccessModification indicates whether the access modifiers of
+ * a class can be changed in order to inline
+ * it.
+ * @param extraClassFileVisitor an optional extra visitor for all inlinable
+ * interfaces.
+ */
+ public SingleImplementationMarker(boolean allowAccessModification,
+ ClassFileVisitor extraClassFileVisitor)
+ {
this.allowAccessModification = allowAccessModification;
+ this.extraClassFileVisitor = extraClassFileVisitor;
}
@@ -129,9 +146,15 @@ implements ClassFileVisitor
{
System.out.println("Single implementation of ["+programClassFile.getName()+"]: ["+singleImplementationClassFile.getName()+"]");
}
-
+
// Mark the interface and its single implementation.
markSingleImplementation(programClassFile, singleImplementationClassFile);
+
+ // Visit the interface, if required.
+ if (extraClassFileVisitor != null)
+ {
+ singleImplementationClassFile.accept(extraClassFileVisitor);
+ }
}
diff --git a/src/proguard/optimize/peephole/StoreLoadReplacer.java b/src/proguard/optimize/peephole/StoreLoadReplacer.java
index c2aad40..0c4b699 100644
--- a/src/proguard/optimize/peephole/StoreLoadReplacer.java
+++ b/src/proguard/optimize/peephole/StoreLoadReplacer.java
@@ -1,4 +1,4 @@
-/* $Id: StoreLoadReplacer.java,v 1.9 2005/06/11 13:13:16 eric Exp $
+/* $Id: StoreLoadReplacer.java,v 1.11 2005/07/31 18:50:05 eric Exp $
*
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
*
@@ -39,21 +39,41 @@ public class StoreLoadReplacer implements InstructionVisitor
private BranchTargetFinder branchTargetFinder;
private CodeAttrInfoEditor codeAttrInfoEditor;
+ private InstructionVisitor extraInstructionVisitor;
/**
* Creates a new StoreLoadReplacer.
- * @param branchTargetFinder a branch target finder that has been
- * initialized to indicate branch targets
- * in the visited code.
- * @param codeAttrInfoEditor a code editor that can be used for
- * accumulating changes to the code.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
*/
public StoreLoadReplacer(BranchTargetFinder branchTargetFinder,
CodeAttrInfoEditor codeAttrInfoEditor)
{
- this.branchTargetFinder = branchTargetFinder;
- this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this(branchTargetFinder, codeAttrInfoEditor, null);
+ }
+
+
+ /**
+ * Creates a new StoreLoadReplacer.
+ * @param branchTargetFinder a branch target finder that has been
+ * initialized to indicate branch targets
+ * in the visited code.
+ * @param codeAttrInfoEditor a code editor that can be used for
+ * accumulating changes to the code.
+ * @param extraInstructionVisitor an optional extra visitor for all replaced
+ * store instructions.
+ */
+ public StoreLoadReplacer(BranchTargetFinder branchTargetFinder,
+ CodeAttrInfoEditor codeAttrInfoEditor,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.branchTargetFinder = branchTargetFinder;
+ this.codeAttrInfoEditor = codeAttrInfoEditor;
+ this.extraInstructionVisitor = extraInstructionVisitor;
}
@@ -79,7 +99,7 @@ public class StoreLoadReplacer implements InstructionVisitor
if (!codeAttrInfoEditor.isModified(offset) &&
!codeAttrInfoEditor.isModified(nextOffset) &&
- !branchTargetFinder.isBranchTarget(nextOffset))
+ !branchTargetFinder.isTarget(nextOffset))
{
// Is the next instruction a corresponding load instruction?
Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
@@ -107,6 +127,12 @@ public class StoreLoadReplacer implements InstructionVisitor
codeAttrInfoEditor.replaceInstruction(nextOffset,
storeInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ extraInstructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+ }
}
}
}
diff --git a/src/proguard/shrink/Shrinker.java b/src/proguard/shrink/Shrinker.java
new file mode 100644
index 0000000..8d7e6a8
--- /dev/null
+++ b/src/proguard/shrink/Shrinker.java
@@ -0,0 +1,135 @@
+/* $Id: Shrinker.java,v 1.1 2005/07/24 12:54:51 eric Exp $
+ *
+ * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+ *
+ * Copyright (c) 2002-2005 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.*;
+import proguard.classfile.ClassPool;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * This class shrinks class pools according to a given configuration.
+ *
+ * @author Eric Lafortune
+ */
+public class Shrinker
+{
+ private Configuration configuration;
+
+
+ /**
+ * Creates a new Shrinker.
+ */
+ public Shrinker(Configuration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+
+ /**
+ * Performs obfuscation of the given program class pool.
+ */
+ public ClassPool execute(ClassPool programClassPool,
+ ClassPool libraryClassPool) throws IOException
+ {
+ // Clean up any old visitor info.
+ programClassPool.classFilesAccept(new ClassFileCleaner());
+ libraryClassPool.classFilesAccept(new ClassFileCleaner());
+
+ // Create a visitor for marking the seeds.
+ UsageMarker usageMarker = configuration.whyAreYouKeeping == null ?
+ new UsageMarker() :
+ new ShortestUsageMarker();
+
+ ClassPoolVisitor classPoolvisitor =
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+ usageMarker,
+ usageMarker);
+ // Mark the seeds.
+ programClassPool.accept(classPoolvisitor);
+ libraryClassPool.accept(classPoolvisitor);
+
+ // Mark interfaces that have to be kept.
+ programClassPool.classFilesAccept(new InterfaceUsageMarker(usageMarker));
+
+ // Mark the inner class information that has to be kept.
+ programClassPool.classFilesAccept(new InnerUsageMarker(usageMarker));
+
+ if (configuration.whyAreYouKeeping != null)
+ {
+ System.out.println();
+
+ // Create a visitor for explaining classes and class members.
+ ShortestUsagePrinter shortestUsagePrinter =
+ new ShortestUsagePrinter((ShortestUsageMarker)usageMarker,
+ configuration.verbose);
+
+ ClassPoolVisitor whyClassPoolvisitor =
+ ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping,
+ shortestUsagePrinter,
+ shortestUsagePrinter);
+
+ // Mark the seeds.
+ programClassPool.accept(whyClassPoolvisitor);
+ libraryClassPool.accept(whyClassPoolvisitor);
+ }
+
+ if (configuration.printUsage != null)
+ {
+ PrintStream ps = isFile(configuration.printUsage) ?
+ new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printUsage))) :
+ System.out;
+
+ // Print out items that will be removed.
+ programClassPool.classFilesAcceptAlphabetically(
+ new UsagePrinter(usageMarker, true, ps));
+
+ if (ps != System.out)
+ {
+ ps.close();
+ }
+ }
+
+ // Discard unused program classes.
+ ClassPool newProgramClassPool = new ClassPool();
+ programClassPool.classFilesAccept(
+ new UsedClassFileFilter(usageMarker,
+ new MultiClassFileVisitor(
+ new ClassFileVisitor[] {
+ new ClassFileShrinker(usageMarker, 1024),
+ new ClassPoolFiller(newProgramClassPool, false)
+ })));
+
+ return newProgramClassPool;
+ }
+
+
+ /**
+ * Returns whether the given file is actually a file, or just a placeholder
+ * for the standard output.
+ */
+ private boolean isFile(File file)
+ {
+ return file.getPath().length() > 0;
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/proguard.git
More information about the pkg-java-commits
mailing list