[proguard] 01/02: Imported Upstream version 4.2
Emmanuel Bourg
ebourg-guest at moszumanska.debian.org
Thu Apr 10 09:00:39 UTC 2014
This is an automated email from the git hooks/post-receive script.
ebourg-guest pushed a commit to annotated tag debian/4.2-1
in repository proguard.
commit 783be490e9ebf1cae1e4b85f98be8a0f7499bd3a
Author: Sam Clegg <samo at debian.org>
Date: Thu Apr 10 10:41:58 2014 +0200
Imported Upstream version 4.2
---
docs/FAQ.html | 20 +-
docs/acknowledgements.html | 2 +-
docs/downloads.html | 26 +
docs/manual/ant.html | 6 +-
docs/manual/examples.html | 15 +-
docs/manual/limitations.html | 13 +-
docs/manual/troubleshooting.html | 184 +-
docs/quality.html | 2 +-
docs/title.html | 2 +-
examples/annotations/lib/annotations.jar | Bin 6123 -> 6123 bytes
examples/ant/proguard.xml | 11 +-
examples/midlets.pro | 3 +-
examples/proguardgui.pro | 17 +-
src/proguard/ProGuard.java | 2 +-
src/proguard/ant/ConfigurationTask.java | 4 +-
src/proguard/classfile/ClassConstants.java | 41 +-
.../classfile/editor/ClassReferenceFixer.java | 14 +-
.../classfile/editor/CodeAttributeComposer.java | 76 +-
.../classfile/editor/CodeAttributeEditor.java | 53 +-
.../classfile/editor/MethodInvocationFixer.java | 4 +-
src/proguard/classfile/editor/VariableEditor.java | 11 +-
.../classfile/editor/VariableSizeUpdater.java | 12 +-
src/proguard/classfile/io/ProgramClassReader.java | 2 +-
.../classfile/util/ClassReferenceInitializer.java | 5 +-
src/proguard/classfile/util/ClassUtil.java | 66 +-
.../classfile/util/DescriptorClassEnumeration.java | 164 +-
.../classfile/util/InternalTypeEnumeration.java | 152 +-
src/proguard/classfile/visitor/ClassPrinter.java | 4 +-
src/proguard/evaluation/BasicInvocationUnit.java | 6 +-
src/proguard/evaluation/TracedStack.java | 251 +--
src/proguard/evaluation/TracedVariables.java | 92 +-
src/proguard/gui/ClassSpecificationDialog.java | 30 +-
src/proguard/gui/GUIResources.properties | 2 +-
src/proguard/gui/MemberSpecificationDialog.java | 2 +-
src/proguard/gui/ProGuardGUI.java | 4 +-
src/proguard/gui/boilerplate.pro | 4 +-
src/proguard/io/DataEntryRenamer.java | 61 +-
src/proguard/obfuscate/ClassObfuscator.java | 2 +-
.../optimize/DuplicateInitializerFixer.java | 212 +++
.../DuplicateInitializerInvocationFixer.java | 123 ++
.../optimize/MethodDescriptorShrinker.java | 119 +-
src/proguard/optimize/Optimizer.java | 84 +-
src/proguard/optimize/ParameterShrinker.java | 26 +-
.../optimize/evaluation/EvaluationSimplifier.java | 1923 +++++++++++---------
.../optimize/evaluation/PartialEvaluator.java | 103 +-
.../optimize/evaluation/VariableOptimizer.java | 17 +-
.../optimize/info/ParameterUsageMarker.java | 13 +-
.../optimize/info/VariableUsageMarker.java | 13 -
.../peephole/InstructionSequenceConstants.java | 36 +-
src/proguard/optimize/peephole/MethodInliner.java | 85 +-
.../optimize/peephole/VariableShrinker.java | 24 +-
src/proguard/preverify/CodeSubroutineInliner.java | 167 +-
src/proguard/shrink/ClassShrinker.java | 76 +-
53 files changed, 2561 insertions(+), 1825 deletions(-)
diff --git a/docs/FAQ.html b/docs/FAQ.html
index 032bfa8..ef1c145 100644
--- a/docs/FAQ.html
+++ b/docs/FAQ.html
@@ -22,7 +22,8 @@
application?</a>
<li><a href="#jdk1.4">Does <b>ProGuard</b> work with Java 2? Java 5? Java
6?</a>
-<li><a href="#j2me">Does <b>ProGuard</b> work with Java Micro Edition?</a>
+<li><a href="#jme">Does <b>ProGuard</b> work with Java Micro Edition?</a>
+<li><a href="#blackberry">Does <b>ProGuard</b> work for Blackberry code?</a>
<li><a href="#ant">Does <b>ProGuard</b> have support for Ant?</a>
<li><a href="#gui">Does <b>ProGuard</b> come with a GUI?</a>
<li><a href="#forname">Does <b>ProGuard</b> handle <code>Class.forName</code>
@@ -80,8 +81,8 @@ class files at Java 6.
Apart from removing unused classes, fields, and methods in the shrinking step,
<b>ProGuard</b> can also perform optimizations at the bytecode level, inside
-methods. Thanks to techniques like control flow analysis, data flow analysis,
-partial evaluation, and liveness analysis, <b>ProGuard</b> can:
+and across methods. Thanks to techniques like control flow analysis, data flow
+analysis, partial evaluation, and liveness analysis, <b>ProGuard</b> can:
<ul>
<li>Evaluate constant expressions.
@@ -127,7 +128,7 @@ introduced some small differences in the class file format. Java 5 added
attributes for generics and for annotations. Java 6 introduced preverification
attributes. <b>ProGuard</b> handles all versions correctly.
-<a name="j2me"> </a>
+<a name="jme"> </a>
<h3>Does <b>ProGuard</b> work with Java Micro Edition?</h3>
Yes. <b>ProGuard</b> itself runs in Java Standard Edition, but you can freely
@@ -139,6 +140,17 @@ preverifier.
<b>ProGuard</b> also comes with an obfuscator plug-in for the JME Wireless
Toolkit.
+<a name="blackberry"> </a>
+<h3>Does <b>ProGuard</b> work with Blackberry code?</h3>
+
+It should. RIM's proprietary <code>rapc</code> compiler converts ordinary JME
+jar files into cod files that run on Blackberry devices. The compiler performs
+quite a few optimizations, but preprocessing the jar files with
+<b>ProGuard</b> can generally still reduce the final code size by a few
+percent. However, the <code>rapc</code> compiler also seems to contain some
+bugs. It sometimes fails on obfuscated code that is valid and accepted by other
+JME tools and VMs. Your mileage may therefore vary.
+
<a name="ant"> </a>
<h3>Does <b>ProGuard</b> have support for Ant?</h3>
diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html
index 07084fc..0d266fb 100644
--- a/docs/acknowledgements.html
+++ b/docs/acknowledgements.html
@@ -32,7 +32,7 @@ Sherington, David Sitsky, James Manning, Ptolemy Oberin, Frank-Michael Moser,
QZ Shines, Thomas Singer, Michele Puccini, Roman Bednarek, Natalia Pujol,
Daniel Sjöblom, Jan Filipsky, Charles Smith, Gerrit Telkamp, Noel
Grandin, Torbjörn Söderstedt, Clemens Eisserer, Clark Bassett,
-Eduard Welch, Dawid Weiss,
+Eduard Welch, Dawid Weiss, Andrew Wilson,
and many others. Thanks! Your feedback has been invaluable.
<p>
diff --git a/docs/downloads.html b/docs/downloads.html
index 53b647a..b607d43 100644
--- a/docs/downloads.html
+++ b/docs/downloads.html
@@ -42,6 +42,32 @@ interest too, because they typically contain any less urgent bug fixes
collected since the previous release.
<p>
+<h3><div>Mar 2008</div> Version 4.2</h3>
+<ul>
+<li>Refined data flow analysis in optimization step.
+<li>Fixed handling of exceptions when inlining subroutines.
+<li>Fixed inlining of incompatible code constructs between different java
+ versions.
+<li>Fixed computation of local variable frame size.
+<li>Fixed optimization of infinite loops.
+<li>Fixed optimization of subroutine invocations.
+<li>Fixed optimization of floating point remainder computations.
+<li>Fixed removal of unused parameters in method descriptors containing arrays
+ of longs or doubles.
+<li>Added undocumented java system properties
+ <code>maximum.inlined.code.length</code> (default is 8) and
+ <code>maximum.resulting.code.length</code> (defaults are 8000 for JSE and
+ 2000 for JME), for expert users who read release notes.
+<li>Fixed processing of generic types in Signature attributes in shrinking and
+ optimization steps.
+<li>Fixed processing of inner class names in Signature attributes in obfuscation
+ step.
+<li>Improved adapting resource file names following obfuscated class names.
+<li>Fixed interpretation of package names in GUI.
+<li>Fixed default settings for Xlets in GUI.
+<li>Updated documentation and examples.
+</ul>
+
<h3><div>Dec 2007</div> Version 4.1</h3>
<ul>
<li>Fixed shrinking of default annotation element values.
diff --git a/docs/manual/ant.html b/docs/manual/ant.html
index 4e9eb7e..b9c8cc6 100644
--- a/docs/manual/ant.html
+++ b/docs/manual/ant.html
@@ -362,14 +362,12 @@ elements:
<dd>Preserve the given optional Java bytecode attribute, with optional
wildcards. If no name is given, all attributes are preserved.</dd>
-<dt><a href="usage.html#adaptresourcefilenames"><code><b><adaptresourcefilenames</b></code></a>
- = "<a href="usage.html#filters"><i>filter</i></a>"
+<dt><a href="usage.html#adaptresourcefilenames"><code><b><adaptresourcefilenames filter = </b></code></a>"<a href="usage.html#filters"><i>filter</i></a>"
<code><b>/></b></code></dt>
<dd>Rename the specified resource files, based on the obfuscated names of the
corresponding class files.</dd>
-<dt><a href="usage.html#adaptresourcefilecontents"><code><b><adaptresourcefilecontents</b></code></a>
- = "<a href="usage.html#filters"><i>filter</i></a>"
+<dt><a href="usage.html#adaptresourcefilecontents"><code><b><adaptresourcefilecontents filter = </b></code></a>"<a href="usage.html#filters"><i>filter</i></a>"
<code><b>/></b></code></dt>
<dd>Update the contents of the specified resource files, based on the
obfuscated names of the class files.</dd>
diff --git a/docs/manual/examples.html b/docs/manual/examples.html
index 8f4fad9..9422a82 100644
--- a/docs/manual/examples.html
+++ b/docs/manual/examples.html
@@ -162,11 +162,6 @@ option.
<p>
If applicable, you should add options for processing <a href="#native">native
methods</a> and <a href="#resourcefiles">resource files</a>.
-<p>
-Please note the in-depth article <a
-href="http://developers.sun.com/techtopics/mobility/midp/ttips/proguard/">"Obfuscating
-MIDlet Suites with ProGuard"</a> at <a
-href="http://developers.sun.com/">developers.sun.com</a>.
<a name="applications"> </a>
<h3>All possible applications in the input jars</h3>
@@ -352,7 +347,7 @@ other obfuscators may rely on the original method names. It may therefore be
helpful to preserve them, in case these other obfuscators are ever used for
further obfuscation of the library.
<p>
-The "Exceptions" attribute is has to be preserved, so the compiler knows which
+The "Exceptions" attribute has to be preserved, so the compiler knows which
exceptions methods may throw.
<p>
The "InnerClasses" attribute (or more precisely, its source name part) has to
@@ -389,10 +384,10 @@ href="usage.html#keepclasseswithmembernames"><code>-keepclasseswithmembernames</
We don't want to preserve all classes or all native methods; we just want to
keep the relevant names from being obfuscated.
<p>
-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 href="callback">Callback
-methods</a> are discussed below as a typical example.
+ProGuard doesn't look at your native code, so it won'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
+href="callback">Callback methods</a> are discussed below as a typical example.
<a name="callback"> </a>
<h3>Processing callback methods</h3>
diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html
index 0867385..3318ea6 100644
--- a/docs/manual/limitations.html
+++ b/docs/manual/limitations.html
@@ -10,8 +10,8 @@
<h2>Limitations</h2>
-When using ProGuard, you should be aware of a few issues, all of which are
-easily avoided or resolved:
+When using ProGuard, you should be aware of a few technical issues, all of
+which are easily avoided or resolved:
<p>
<ul>
@@ -36,15 +36,6 @@ easily avoided or resolved:
<code>-dontoptimize</code> option.
<p>
-<li>ProGuard's optimization algorithms also remove all <b>empty busy-waiting
- loops</b>, unless they test on fields that are marked as
- <code>volatile</code>. The specifications of the Java Virtual Machine
- require that you always mark fields that are accessed across different
- threads without further synchronization as <code>volatile</code>.
- Otherwise, you'll have to switch off optimization using the
- <code>-dontoptimize</code> option.
- <p>
-
<li>If an input jar and a library jar contain classes in the <b>same
package</b>, the obfuscated output jar may contain class names that
overlap with class names in the library jar. This is most likely if the
diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html
index 47e871a..8669e37 100644
--- a/docs/manual/troubleshooting.html
+++ b/docs/manual/troubleshooting.html
@@ -34,7 +34,7 @@ ProGuard may print out some notes and non-fatal warnings:
with an option like "<code>-keep class MyClass</code>", or their
implementations with an option like "<code>-keep class * implements
MyClass</code>". You can switch off these notes by specifying the
- <code>-dontnote</code> option.</dd>
+ <a href="usage.html#dontnote"><code>-dontnote</code></a> option.</dd>
<dt><a name="dynamicalclassmember"><b>Note: ... accesses a field/method '...' dynamically</b></a></dt>
@@ -43,7 +43,7 @@ ProGuard may print out some notes and non-fatal warnings:
may need to figure out where the mentioned class members are defined and
keep them with an option like "<code>-keep class MyClass { MyFieldType
myField; }</code>". You can switch off these notes by specifying the
- <code>-dontnote</code> option.</dd>
+ <a href="usage.html#dontnote"><code>-dontnote</code></a> option.</dd>
<dt><a name="duplicateclass"><b>Note: duplicate definition of program/library class</b></a></dt>
@@ -52,8 +52,8 @@ ProGuard may print out some notes and non-fatal warnings:
the first definitions. The warning may be an indication of some problem
though, so it's advisable to remove the duplicates. A convenient way to do
so is by specifying filters on the input jars or library jars. You can
- switch off these notes by specifying the <code>-dontnote</code>
- option.</dd>
+ switch off these notes by specifying the <a
+ href="usage.html#dontnote"><code>-dontnote</code></a> option.</dd>
<dt><a name="duplicatezipentry"><b>Warning: can't write resource ... Duplicate zip entry</b></a></dt>
@@ -76,11 +76,12 @@ some more serious warnings:
<dd>If there are unresolved references to superclasses or interfaces, you most
likely forgot to specify an essential library. All libraries that are
referenced by your code should be specified, including the Java run-time
- library. For specifying libraries, use the <code>-libraryjars</code>
- option.
+ library. For specifying libraries, use the <a
+ href="usage.html#libraryjars"><code>-libraryjars</code></a> option.
<p>
If the class that is reported as missing is a non-public library class,
- you should specify the <code>-dontskipnonpubliclibraryclasses</code>
+ you should specify the <a
+ href="usage.html#dontskipnonpubliclibraryclasses"><code>-dontskipnonpubliclibraryclasses</code></a>
option. A common example is the class
<code>java.util.zip.ZipConstants</code>, which is used as an interface
class in some public classes, even though it is only package visible (in
@@ -88,8 +89,10 @@ some more serious warnings:
fundamental part of the class hierarchy).
<p>
If you're missing a library and you're absolutely sure it isn't used
- anyway, you can try your luck with the <code>-ignorewarnings</code>
- option, or even the <code>-dontwarn</code> option.</dd>
+ anyway, you can try your luck with the <a
+ href="usage.html#ignorewarnings"><code>-ignorewarnings</code></a> option,
+ or even the <a href="usage.html#dontwarn"><code>-dontwarn</code></a>
+ option.</dd>
<dt><a name="unresolvedclassmember"><b>Warning: can't find referenced field/method</b></a></dt>
@@ -101,7 +104,7 @@ some more serious warnings:
<p>
If the class member that is reported as missing is actually implemented in
a non-public library class, you should specify the
- <code>-dontskipnonpubliclibraryclasses</code> option. A common example is
+ <a href="usage.html#dontskipnonpubliclibraryclasses"><code>-dontskipnonpubliclibraryclasses</code></a> option. A common example is
the method <code>setLength(int)</code> in the public class
<code>java.lang.StringBuilder</code>. This method is actually defined in
the package visible superclass
@@ -110,7 +113,9 @@ some more serious warnings:
<p>
If your program classes reside in the same packages as library classes,
and refer to their package visible class members, then you should specify
- the <code>-dontskipnonpubliclibraryclassmembers</code> option.</dd>
+ the <a
+ href="usage.html#dontskipnonpubliclibraryclassmembers"><code>-dontskipnonpubliclibraryclassmembers</code></a>
+ option.</dd>
<dt><a name="unresolvedenclosingmethod"><b>Warning: can't find enclosing class/method</b></a></dt>
@@ -130,8 +135,9 @@ some more serious warnings:
your input classes are packaged correctly. Notably, class files that are
in the <code>WEB-INF/classes</code> directory in a war should be packaged
in a jar and put in the <code>WEB-INF/lib</code> directory. If you don't
- mind these classes not being written to the output, you can specify the
- <code>-ignorewarnings</code> option, or even the <code>-dontwarn</code>
+ mind these classes not being written to the output, you can specify the <a
+ href="usage.html#ignorewarnings"><code>-ignorewarnings</code></a> option,
+ or even the <a href="usage.html#dontwarn"><code>-dontwarn</code></a>
option.</dd>
<dt><a name="mappingconflict1"><b>Warning: ... is not being kept as ..., but remapped to ...</b></a></dt>
@@ -142,8 +148,9 @@ some more serious warnings:
specified in the configuration, but it has to be mapped to the other given
name, as specified in the mapping file. You should adapt your
configuration or your mapping file to remove the conflict. Alternatively,
- if you're sure the renaming won't hurt, you can specify the
- <code>-ignorewarnings</code> option, or even the <code>-dontwarn</code>
+ if you're sure the renaming won't hurt, you can specify the <a
+ href="usage.html#ignorewarnings"><code>-ignorewarnings</code></a> option,
+ or even the <a href="usage.html#dontwarn"><code>-dontwarn</code></a>
option.</dd>
<dt><a name="mappingconflict2"><b>Warning: field/method ... can't be mapped to ...</b></a></dt>
@@ -156,21 +163,32 @@ some more serious warnings:
initial obfuscation step. For instance, some new class may have been added
that extends two existing classes, introducing a conflict in the name
space of its class members. If you're sure the class member receiving
- another name than the one specified won't hurt, you can specify the
- <code>-ignorewarnings</code> option, or even the <code>-dontwarn</code>
- option. Note that you should always use the
- <code>-useuniqueclassmembernames</code> option in the initial obfuscation
- step, in order to reduce the risk of conflicts.</dd>
+ another name than the one specified won't hurt, you can specify the <a
+ href="usage.html#ignorewarnings"><code>-ignorewarnings</code></a> option,
+ or even the <a href="usage.html#dontwarn"><code>-dontwarn</code></a>
+ option. Note that you should always use the <a
+ href="usage.html#useuniqueclassmembernames"><code>-useuniqueclassmembernames</code></a>
+ option in the initial obfuscation step, in order to reduce the risk of
+ conflicts.</dd>
<dt><a name="keep"><b>Error: You have to specify '-keep' options</b></a></dt>
-<dd>You either forgot to specify <code>-keep</code> options, or you mistyped
- the class names. ProGuard has to know exactly what you want to keep: an
+<dd>You either forgot to specify <a
+ href="usage.html#keep"><code>-keep</code></a> options, or you mistyped the
+ class names. ProGuard has to know exactly what you want to keep: an
application, an applet, a servlet, a midlet,..., or any combination of
these. Without the proper seed specifications, ProGuard would shrink,
optimize, or obfuscate all class files away.</dd>
+<dt><a name="filename"><b>Error: Expecting class path separator ';' before 'Files\Java\</b>...<b>'</b> (in Windows)</a></dt>
+
+<dd>If the path of your run-time jar contains spaces, like in "Program Files",
+ you have to enclose it with single or double quotes, as explained in the
+ section on <a href="usage.html#filename">file names</a>. This is actually
+ true for all file names containing special character, on all
+ platforms.</dd>
+
<dt><a name="macosx"><b>Error: Can't read [</b>...<b>/lib/rt.jar] (No such file or directory)</b> (in MacOS X)</a></dt>
<dd>In MacOS X, the run-time classes may be in a different place than on most
@@ -223,13 +241,14 @@ there might be a couple of reasons:
<dt><a name="disappearingclasses"><b>Disappearing classes</b></a></dt>
<dd>If you are working on Windows and it looks like some classes have
- disappeared from your output, you should make sure you're not writing
- your output class files to a directory (or unpacking the output jar). On
+ disappeared from your output, you should make sure you're not writing your
+ output class files to a directory (or unpacking the output jar). On
platforms with case-insensitive file systems, such as Windows, unpacking
tools often let class files with similar lower-case and upper-case names
overwrite each other. If you really can't switch to a different operating
- system, you could consider using ProGuard's
- <code>-dontusemixedcaseclassnames</code> option.
+ system, you could consider using ProGuard's <a
+ href="usage.html#dontusemixedcaseclassnames"><code>-dontusemixedcaseclassnames</code></a>
+ option.
<p>
Also, you should make sure your class files are in directories that
correspond to their package names. ProGuard will read misplaced class
@@ -240,12 +259,12 @@ there might be a couple of reasons:
<dt><a name="notkept"><b>Classes or class members not being kept</b></a></dt>
-<dd>If ProGuard is not keeping the right classes or class members, make
- sure you are using fully qualified class names. If the package name of
- some class is missing, ProGuard won't match the elements that you might be
- expecting. It may help to double-check for typos too. You can use the
- <code>-printseeds</code> option to see which elements are being kept
- exactly.
+<dd>If ProGuard is not keeping the right classes or class members, make sure
+ you are using fully qualified class names. If the package name of some
+ class is missing, ProGuard won't match the elements that you might be
+ expecting. It may help to double-check for typos too. You can use the <a
+ href="usage.html#printseeds"><code>-printseeds</code></a> option to see
+ which elements are being kept exactly.
<p>
If you are using marker interfaces to keep other classes, the marker
interfaces themselves are probably being removed in the shrinking step.
@@ -278,21 +297,23 @@ produces errors, it's usually for a single reason:
on a platform with a case-insensitive file system, such as Windows. The
<code>preverify</code> tool always unpacks the jars, so class files with
similar lower-case and upper-case names overwrite each other. You can use
- ProGuard's <code>-dontusemixedcaseclassnames</code> option to work around
- this problem.
+ ProGuard's <a
+ href="usage.html#dontusemixedcaseclassnames"><code>-dontusemixedcaseclassnames</code></a>
+ option to work around this problem.
<p>
- If the above doesn't help, there is probably a bug in the optimization step
- of ProGuard. Make sure you are using the latest version. You should be able
- to work around the problem by using the <code>-dontoptimize</code> option.
- You can check the bug database to see if it is a known problem (often with
- a fix). Otherwise, please report it, preferably with the simplest example
- on which you can find ProGuard to fail.</dd>
+ If the above doesn't help, there is probably a bug in the optimization
+ step of ProGuard. Make sure you are using the latest version. You should
+ be able to work around the problem by using the <a
+ href="usage.html#dontoptimize"><code>-dontoptimize</code></a> option. You
+ can check the bug database to see if it is a known problem (often with a
+ fix). Otherwise, please report it, preferably with the simplest example on
+ which you can find ProGuard to fail.</dd>
</dl>
Note that it is no longer necessary to use an external preverifier. With the
-<code>-microedition</code> option, ProGuard will preverify the class files for
-Java Micro Edition.
+<a href="usage.html#microedition"><code>-microedition</code></a> option,
+ProGuard will preverify the class files for Java Micro Edition.
<p>
<a name="runtime"> </a>
@@ -320,26 +341,30 @@ might be several reasons:
the missing class dynamically. ProGuard can only detect constant name
arguments, like <code>Class.forName("mypackage.MyClass")</code>. For
variable name arguments like <code>Class.forName(someClass)</code>, you
- have to keep all possible classes using the <code>-keep</code> option,
- e.g. "<code>-keep class mypackage.MyClass</code>" or "<code>-keep class *
- implements mypackage.MyInterface</code>".</dd>
+ have to keep all possible classes using the appropriate <a
+ href="usage.html#keep"><code>-keep</code></a> option, e.g. "<code>-keep
+ class mypackage.MyClass</code>" or "<code>-keep class * implements
+ mypackage.MyInterface</code>".</dd>
<dt><a name="nosuchmethodexception"><b>NoSuchMethodException</b></a></dt>
<dd>Your code is probably calling something like
<code>myClass.getMethod</code>, trying to find some method dynamically.
Since ProGuard isn't detecting this (yet), you have to keep the missing
- method in using the <code>-keep</code> option, e.g. "<code>-keep class
- mypackage.MyClass { void myMethod(); }</code>".</dd>
+ method in using the appropriate <a
+ href="usage.html#keep"><code>-keep</code></a> option, e.g. "<code>-keep
+ class mypackage.MyClass { void myMethod(); }</code>".</dd>
<dt><a name="missingresourceexception"><b>MissingResourceException</b> or
<b>NullPointerException</b></a></dt>
<dd>Your processed code may be unable to find some resource files. ProGuard
- simply copies resource files over from the input jars to the
- output jars. Their names and contents remain unchanged, unless you specify
- the options <code>-adaptresourcefilenames</code> and/or
- <code>-adaptresourcefilecontents</code>.
+ simply copies resource files over from the input jars to the output jars.
+ Their names and contents remain unchanged, unless you specify the options
+ <a
+ href="usage.html#adaptresourcefilenames"><code>-adaptresourcefilenames</code></a>
+ and/or <a
+ href="usage.html#adaptresourcefilecontents"><code>-adaptresourcefilecontents</code></a>.
<p>
Note that directory entries in jar files aren't copied at all. If you
refer to any directories from your code, you should add them
@@ -362,17 +387,30 @@ might be several reasons:
<b>verification error</b> (in Java Micro Edition)</a></dt>
<dd>If you get such an error in Java Micro Edition, you may have forgotten to
- specify the <code>-microedition</code> option, so the processed class
- files are preverified properly.</dd>
+ specify the <a
+ href="usage.html#microedition"><code>-microedition</code></a> option, so
+ the processed class files are preverified properly.</dd>
+
+<dt><a name="disappearingloops"><b>Disappearing loops</b></a></dt>
+
+<dd>If your code contains empty busy-waiting loops, ProGuard's optimization
+ step may remove them. More specifically, this happens if a loop
+ continuously checks the value of a non-volatile field that is changed in a
+ different thread. The specifications of the Java Virtual Machine require
+ that you always mark fields that are accessed across different threads
+ without further synchronization as <code>volatile</code>. If this is not
+ possible for some reason, you'll have to switch off optimization using the
+ <a href="usage.html#dontoptimize"><code>-dontoptimize</code></a>
+ option.</dd>
<dt><a name="failingmidlets"><b>Failing midlets</b> (in Java Micro Edition)</a></dt>
-<dd>If your midlet simply won't start, you might try using the
- <code>-dontusemixedcaseclassnames</code> option. Even if it has been
- properly processed and then preverified on a case-sensitive file system,
- the device itself might not like the mixed-case class names. Notably, the
- Nokia N-Gage emulator works fine, but the actual device seems to exhibit
- this problem.</dd>
+<dd>If your midlet simply won't start, you might try using the <a
+ href="usage.html#dontusemixedcaseclassnames"><code>-dontusemixedcaseclassnames</code></a>
+ option. Even if it has been properly processed and then preverified on a
+ case-sensitive file system, the device itself might not like the
+ mixed-case class names. Notably, the Nokia N-Gage emulator works fine, but
+ the actual device seems to exhibit this problem.</dd>
<dt><a name="securityexception"><b>SecurityException: SHA1 digest error</b></a></dt>
@@ -398,23 +436,29 @@ might be several reasons:
<dd>If you get such a message in a Motorola or Sony Ericsson phone emulator,
it's because these emulators don't like packageless classes and/or
overloaded fields and methods. You can work around it by not using the
- options <b>-repackageclasses ''</b> and <b>-overloadaggressively</b>. If
- you're using the JME WTK plugin, you can adapt the configuration
+ options <code><a href="usage.html#repackageclasses">-repackageclasses</a>
+ ''</code> and <a
+ href="usage.html#overloadaggressively"><code>-overloadaggressively</code></a>.
+ If you're using the JME WTK plugin, you can adapt the configuration
<code>proguard/wtk/default.pro</code> that's inside the
<code>proguard.jar</code>.</dd>
<dt><a name="compilererror"><b>CompilerError: duplicate addition</b></a></dt>
<dd>You are probably compiling or running some code that has been obfuscated
- with the <code>-overloadaggressively</code> option. This option triggers a
- bug in <code>sun.tools.java.MethodSet.add</code> in Sun's JDK 1.2.2, which
- is used for (dynamic) compilation. You should then avoid this option.</dd>
+ with the <a
+ href="usage.html#overloadaggressively"><code>-overloadaggressively</code></a>
+ option. This option triggers a bug in
+ <code>sun.tools.java.MethodSet.add</code> in Sun's JDK 1.2.2, which is
+ used for (dynamic) compilation. You should then avoid this option.</dd>
<dt><a name="classformaterror"><b>ClassFormatError: repetitive field name/signature</b></a></dt>
<dd>You are probably processing some code that has been obfuscated before with
- the <code>-overloadaggressively</code> option. You should then use the
- same option again in the second processing round.</dd>
+ the <a
+ href="usage.html#overloadaggressively"><code>-overloadaggressively</code></a>
+ option. You should then use the same option again in the second processing
+ round.</dd>
<dt><a name="nosuchmethoderror"><b>NoSuchMethodError</b> or
<b>AbstractMethodError</b></a></dt>
@@ -436,10 +480,10 @@ might be several reasons:
<dd>Verification errors when executing a program are almost certainly the
result of a bug in the optimization step of ProGuard. Make sure you are
using the latest version. You should be able to work around the problem by
- using the <code>-dontoptimize</code> option. You can check the bug database
- to see if it is a known problem (often with a fix). Otherwise, please
- report it, preferably with the simplest example on which you can find
- ProGuard to fail.</dd>
+ using the <a href="usage.html#dontoptimize"><code>-dontoptimize</code></a>
+ option. You can check the bug database to see if it is a known problem
+ (often with a fix). Otherwise, please report it, preferably with the
+ simplest example on which ProGuard fails.</dd>
</dl>
diff --git a/docs/quality.html b/docs/quality.html
index c8ae514..fe927fe 100644
--- a/docs/quality.html
+++ b/docs/quality.html
@@ -31,7 +31,7 @@ full-screen size.
<p>
In addition, <b>ProGuard</b> is tested against a constantly growing test suite
-(over 400 tests at this time of writing). These small programs contain a wide
+(over 450 tests at this time of writing). These small programs contain a wide
range of common and uncommon constructs, in order to detect any regression
problems as soon as possible.
diff --git a/docs/title.html b/docs/title.html
index f9ee64f..cbad69f 100644
--- a/docs/title.html
+++ b/docs/title.html
@@ -10,7 +10,7 @@
<div class="title">
<h1><img src="title.gif" width="154" height="29" alt="ProGuard"></h1>
-<div>Version 4.1</div>
+<div>Version 4.2</div>
</div>
</body>
diff --git a/examples/annotations/lib/annotations.jar b/examples/annotations/lib/annotations.jar
index e6e440b..d2434cb 100644
Binary files a/examples/annotations/lib/annotations.jar and b/examples/annotations/lib/annotations.jar differ
diff --git a/examples/ant/proguard.xml b/examples/ant/proguard.xml
index c9ecedc..b7e1e25 100644
--- a/examples/ant/proguard.xml
+++ b/examples/ant/proguard.xml
@@ -11,18 +11,25 @@
<proguard printmapping="proguard.map"
overloadaggressively="on"
- repackageclasses=""
- allowaccessmodification="on">
+ repackageclasses="">
<!-- Specify the input jars, output jars, and library jars. -->
<injar file="lib/proguard.jar" />
+ <injar file="lib/proguardgui.jar" filter="!META-INF/**" />
+ <injar file="lib/retrace.jar" filter="!META-INF/**" />
+
<outjar file="examples/ant/proguard_out.jar" />
<libraryjar file="${java.home}/lib/rt.jar" />
<libraryjar file="/usr/local/java/ant1.6.5/lib/ant.jar" />
<libraryjar file="/usr/local/java/wtk2.1/wtklib/kenv.zip" />
+ <!-- Adapt the resource file names, based on the corresponding obfuscated
+ class names. -->
+
+ <adaptresourcefilenames filter="**.properties,**.gif,**.jpg" />
+
<!-- The main seeds: ProGuard and its companion tool ReTrace. -->
<keep access="public" name="proguard.ProGuard">
diff --git a/examples/midlets.pro b/examples/midlets.pro
index 9026760..bffc38e 100644
--- a/examples/midlets.pro
+++ b/examples/midlets.pro
@@ -3,7 +3,6 @@
# Usage:
# java -jar proguard.jar @midlets.pro
#
-# You should still apply the preverify tool after having processed your code.
# Specify the input jars, output jars, and library jars.
@@ -31,7 +30,7 @@
-allowaccessmodification
# On Windows, you can't use mixed case class names,
-# for the sake of the preverify tool.
+# should you still want to use the preverify tool.
#
# -dontusemixedcaseclassnames
diff --git a/examples/proguardgui.pro b/examples/proguardgui.pro
index aa9b9c7..1539274 100644
--- a/examples/proguardgui.pro
+++ b/examples/proguardgui.pro
@@ -10,8 +10,8 @@
# We'll filter out the Ant and WTK classes.
-injars ../lib/proguardgui.jar
--injars ../lib/proguard.jar(!META-INF/MANIFEST.MF,
- !proguard/ant/**,!proguard/wtk/**)
+-injars ../lib/proguard.jar(!META-INF/**,!proguard/ant/**,!proguard/wtk/**)
+-injars ../lib/retrace.jar (!META-INF/**)
-outjars proguardgui_out.jar
-libraryjars <java.home>/lib/rt.jar
@@ -21,7 +21,12 @@
# additional GUI files instead of all files.
#-applymapping proguard.map
-#-outjars proguardgui_out.jar(proguard/gui/**)
+#-injars ../lib/proguardgui.jar
+#-outjars proguardgui_out.jar
+#-libraryjars ../lib/proguard.jar(!proguard/ant/**,!proguard/wtk/**)
+#-libraryjars ../lib/retrace.jar
+#-libraryjars <java.home>/lib/rt.jar
+
# Allow methods with the same signature, except for the return type,
# to get the same obfuscation name.
@@ -32,6 +37,12 @@
-repackageclasses ''
+# Adapt the names of resource files, based on the corresponding obfuscated
+# class names. Notably, in this case, the GUI resource properties file will
+# have to be renamed.
+
+-adaptresourcefilenames
+
# The entry point: ProGuardGUI and its main method.
-keep public class proguard.gui.ProGuardGUI {
diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java
index a10dadf..0fcafca 100644
--- a/src/proguard/ProGuard.java
+++ b/src/proguard/ProGuard.java
@@ -37,7 +37,7 @@ import java.io.*;
*/
public class ProGuard
{
- public static final String VERSION = "ProGuard, version 4.1";
+ public static final String VERSION = "ProGuard, version 4.2";
private final Configuration configuration;
private ClassPool programClassPool = new ClassPool();
diff --git a/src/proguard/ant/ConfigurationTask.java b/src/proguard/ant/ConfigurationTask.java
index 2c8d094..2024455 100644
--- a/src/proguard/ant/ConfigurationTask.java
+++ b/src/proguard/ant/ConfigurationTask.java
@@ -173,14 +173,14 @@ public class ConfigurationTask extends Task
}
- public void addConfiguredAdaptResourceFileNames(FilterElement filterElement)
+ public void addConfiguredAdaptresourcefilenames(FilterElement filterElement)
{
configuration.adaptResourceFileNames = extendFilter(configuration.adaptResourceFileNames,
filterElement);
}
- public void addConfiguredAdaptResourceFileContents(FilterElement filterElement)
+ public void addConfiguredAdaptresourcefilecontents(FilterElement filterElement)
{
configuration.adaptResourceFileContents = extendFilter(configuration.adaptResourceFileContents,
filterElement);
diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java
index 908c98e..bf3d3ae 100644
--- a/src/proguard/classfile/ClassConstants.java
+++ b/src/proguard/classfile/ClassConstants.java
@@ -163,11 +163,12 @@ public interface ClassConstants
public static final int ELEMENT_VALUE_ANNOTATION = '@';
public static final int ELEMENT_VALUE_ARRAY = '[';
- public static final char EXTERNAL_PACKAGE_SEPARATOR = '.';
- public static final char INTERNAL_PACKAGE_SEPARATOR = '/';
- public static final char INNER_CLASS_SEPARATOR = '$';
- public static final char SPECIAL_CLASS_CHARACTER = '-';
- public static final char SPECIAL_MEMBER_SEPARATOR = '$';
+ public static final char EXTERNAL_PACKAGE_SEPARATOR = '.';
+ public static final char EXTERNAL_INNER_CLASS_SEPARATOR = '.';
+ public static final char INTERNAL_PACKAGE_SEPARATOR = '/';
+ public static final char INTERNAL_INNER_CLASS_SEPARATOR = '$';
+ public static final char SPECIAL_CLASS_CHARACTER = '-';
+ public static final char SPECIAL_MEMBER_SEPARATOR = '$';
public static final char EXTERNAL_METHOD_ARGUMENTS_OPEN = '(';
public static final char EXTERNAL_METHOD_ARGUMENTS_CLOSE = ')';
@@ -211,20 +212,22 @@ public interface ClassConstants
public static final String INTERNAL_METHOD_NAME_NEW_INSTANCE = "newInstance";
public static final String INTERNAL_METHOD_TYPE_NEW_INSTANCE = "()Ljava/lang/Object;";
- public static final char INTERNAL_TYPE_VOID = 'V';
- public static final char INTERNAL_TYPE_BOOLEAN = 'Z';
- public static final char INTERNAL_TYPE_BYTE = 'B';
- public static final char INTERNAL_TYPE_CHAR = 'C';
- public static final char INTERNAL_TYPE_SHORT = 'S';
- public static final char INTERNAL_TYPE_INT = 'I';
- public static final char INTERNAL_TYPE_LONG = 'J';
- public static final char INTERNAL_TYPE_FLOAT = 'F';
- public static final char INTERNAL_TYPE_DOUBLE = 'D';
- public static final char INTERNAL_TYPE_CLASS_START = 'L';
- public static final char INTERNAL_TYPE_CLASS_END = ';';
- public static final char INTERNAL_TYPE_ARRAY = '[';
- public static final char INTERNAL_TYPE_GENERIC_START = '<';
- public static final char INTERNAL_TYPE_GENERIC_END = '>';
+ public static final char INTERNAL_TYPE_VOID = 'V';
+ public static final char INTERNAL_TYPE_BOOLEAN = 'Z';
+ public static final char INTERNAL_TYPE_BYTE = 'B';
+ public static final char INTERNAL_TYPE_CHAR = 'C';
+ public static final char INTERNAL_TYPE_SHORT = 'S';
+ public static final char INTERNAL_TYPE_INT = 'I';
+ public static final char INTERNAL_TYPE_LONG = 'J';
+ public static final char INTERNAL_TYPE_FLOAT = 'F';
+ public static final char INTERNAL_TYPE_DOUBLE = 'D';
+ public static final char INTERNAL_TYPE_CLASS_START = 'L';
+ public static final char INTERNAL_TYPE_CLASS_END = ';';
+ public static final char INTERNAL_TYPE_ARRAY = '[';
+ public static final char INTERNAL_TYPE_GENERIC_VARIABLE_START = 'T';
+ public static final char INTERNAL_TYPE_GENERIC_START = '<';
+ public static final char INTERNAL_TYPE_GENERIC_BOUND = ':';
+ public static final char INTERNAL_TYPE_GENERIC_END = '>';
public static final String EXTERNAL_TYPE_JAVA_LANG_OBJECT = "java.lang.Object";
public static final String EXTERNAL_PACKAGE_JAVA_LANG = "java.lang.";
diff --git a/src/proguard/classfile/editor/ClassReferenceFixer.java b/src/proguard/classfile/editor/ClassReferenceFixer.java
index 029da5b..f32b991 100644
--- a/src/proguard/classfile/editor/ClassReferenceFixer.java
+++ b/src/proguard/classfile/editor/ClassReferenceFixer.java
@@ -309,7 +309,7 @@ implements ClassVisitor,
innerNameIndex != 0)
{
String newInnerName = clazz.getClassName(innerClassIndex);
- int index = newInnerName.lastIndexOf(ClassConstants.INNER_CLASS_SEPARATOR);
+ int index = newInnerName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR);
if (index >= 0)
{
innerClassesInfo.u2innerNameIndex =
@@ -490,12 +490,20 @@ implements ClassVisitor,
int index = 0;
while (descriptorClassEnumeration.hasMoreClassNames())
{
- String className = descriptorClassEnumeration.nextClassName();
- String fluff = descriptorClassEnumeration.nextFluff();
+ String className = descriptorClassEnumeration.nextClassName();
+ boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName();
+ String fluff = descriptorClassEnumeration.nextFluff();
String newClassName = newClassName(className,
referencedClasses[index++]);
+ // Strip the outer class name again, if it's an inner class.
+ if (isInnerClassName)
+ {
+ newClassName =
+ newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1);
+ }
+
newDescriptorBuffer.append(newClassName);
newDescriptorBuffer.append(fluff);
}
diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java
index 716cd81..7a2cb37 100644
--- a/src/proguard/classfile/editor/CodeAttributeComposer.java
+++ b/src/proguard/classfile/editor/CodeAttributeComposer.java
@@ -54,8 +54,11 @@ implements AttributeVisitor,
private static final int MAXIMUM_LEVELS = 32;
+ private static final int INVALID = -1;
+ private boolean allowExternalExceptionHandlers;
+
private int maximumCodeLength;
private int codeLength;
private int exceptionTableLength;
@@ -77,6 +80,26 @@ implements AttributeVisitor,
/**
+ * Creates a new CodeAttributeComposer that doesn't allow external exception
+ * handlers.
+ */
+ public CodeAttributeComposer()
+ {
+ this(false);
+ }
+
+
+ /**
+ * Creates a new CodeAttributeComposer that optionally allows external
+ * exception handlers.
+ */
+ public CodeAttributeComposer(boolean allowExternalExceptionHandlers)
+ {
+ this.allowExternalExceptionHandlers = allowExternalExceptionHandlers;
+ }
+
+
+ /**
* Starts a new code definition.
*/
public void reset()
@@ -122,6 +145,12 @@ implements AttributeVisitor,
instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1];
}
+ // Initialize the offset map.
+ for (int index = 0; index <= maximumCodeFragmentLength; index++)
+ {
+ instructionOffsetMap[level][index] = INVALID;
+ }
+
// Remember the location of the code fragment.
codeFragmentOffsets[level] = codeLength;
codeFragmentLengths[level] = maximumCodeFragmentLength;
@@ -258,6 +287,29 @@ implements AttributeVisitor,
maximumCodeLength += codeLength - codeFragmentOffsets[level] -
codeFragmentLengths[level];
+ // Try to remap the exception handlers that couldn't be remapped before.
+ if (allowExternalExceptionHandlers)
+ {
+ for (int index = 0; index < exceptionTableLength; index++)
+ {
+ ExceptionInfo exceptionInfo = exceptionTable[index];
+
+ // Unmapped exception handlers are still negated.
+ int handlerPC = -exceptionInfo.u2handlerPC;
+ if (handlerPC > 0)
+ {
+ if (instructionOffsetMap[level][handlerPC] < codeLength)
+ {
+ exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC);
+ }
+ else if (level == 0)
+ {
+ throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]");
+ }
+ }
+ }
+ }
+
level--;
}
@@ -412,9 +464,17 @@ implements AttributeVisitor,
{
// Remap the code offsets. Note that the instruction offset map also has
// an entry for the first offset after the code, for u2endPC.
- exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
- exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC);
- exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
+ exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
+ exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC);
+
+ // See if we can remap the handler right away. Unmapped exception
+ // handlers are negated, in order to mark them as external.
+ int handlerPC = exceptionInfo.u2handlerPC;
+ exceptionInfo.u2handlerPC =
+ allowExternalExceptionHandlers &&
+ instructionOffsetMap[level][handlerPC] >= codeLength ?
+ -handlerPC :
+ remapInstructionOffset(handlerPC);
}
@@ -561,10 +621,16 @@ implements AttributeVisitor,
if (oldInstructionOffset < 0 ||
oldInstructionOffset > codeFragmentLengths[level])
{
- throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment with length ["+codeFragmentLengths[level]+"]");
+ throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level);
+ }
+
+ int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset];
+ if (newInstructionOffset == INVALID)
+ {
+ throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level);
}
- return instructionOffsetMap[level][oldInstructionOffset];
+ return newInstructionOffset;
}
diff --git a/src/proguard/classfile/editor/CodeAttributeEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java
index 9b80d77..cda12a6 100644
--- a/src/proguard/classfile/editor/CodeAttributeEditor.java
+++ b/src/proguard/classfile/editor/CodeAttributeEditor.java
@@ -21,7 +21,6 @@
package proguard.classfile.editor;
import proguard.classfile.*;
-import proguard.classfile.visitor.ClassPrinter;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.preverification.*;
import proguard.classfile.attribute.preverification.visitor.*;
@@ -53,6 +52,7 @@ implements AttributeVisitor,
private static boolean DEBUG = true;
//*/
+ private boolean updateFrameSizes;
private int codeLength;
private boolean modified;
@@ -61,7 +61,7 @@ implements AttributeVisitor,
/*private*/public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
/*private*/public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
/*private*/public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
- private boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+ /*private*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
private int[] instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH];
private int newOffset;
@@ -74,6 +74,18 @@ implements AttributeVisitor,
private final InstructionWriter instructionWriter = new InstructionWriter();
+ public CodeAttributeEditor()
+ {
+ this(true);
+ }
+
+
+ public CodeAttributeEditor(boolean updateFrameSizes)
+ {
+ this.updateFrameSizes = updateFrameSizes;
+ }
+
+
/**
* Resets the accumulated code changes.
* @param codeLength the length of the code that will be edited next.
@@ -115,11 +127,6 @@ implements AttributeVisitor,
*/
public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
{
- if (DEBUG)
- {
- System.out.println("Inserting instruction before ["+instructionOffset+"]: "+instruction);
- }
-
if (instructionOffset < 0 ||
instructionOffset >= codeLength)
{
@@ -248,11 +255,6 @@ implements AttributeVisitor,
System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
- if (DEBUG)
- {
- method.accept(clazz, new ClassPrinter());
- }
-
throw ex;
}
}
@@ -278,8 +280,7 @@ implements AttributeVisitor,
performSimpleReplacements(codeAttribute);
// Update the maximum stack size and local variable frame size.
- stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
- variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+ updateFrameSizes(clazz, method, codeAttribute);
}
else
{
@@ -296,8 +297,7 @@ implements AttributeVisitor,
codeAttribute.u2exceptionTableLength);
// Update the maximum stack size and local variable frame size.
- stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
- variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+ updateFrameSizes(clazz, method, codeAttribute);
// Remap the line number table and the local variable table.
codeAttribute.attributesAccept(clazz, method, this);
@@ -308,6 +308,16 @@ implements AttributeVisitor,
}
+ private void updateFrameSizes(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ if (updateFrameSizes)
+ {
+ stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+ variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+ }
+ }
+
+
public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
{
// Remap all stack map entries.
@@ -414,6 +424,11 @@ implements AttributeVisitor,
if (replacementInstruction != null)
{
replacementInstruction.write(codeAttribute, offset);
+
+ if (DEBUG)
+ {
+ System.out.println(" Replaced "+replacementInstruction.toString(newOffset));
+ }
}
}
}
@@ -434,8 +449,7 @@ implements AttributeVisitor,
int oldLength = codeAttribute.u4codeLength;
// Make sure there is a sufficiently large instruction offset map.
- if (instructionOffsetMap == null ||
- instructionOffsetMap.length < oldLength + 1)
+ if (instructionOffsetMap.length < oldLength + 1)
{
instructionOffsetMap = new int[oldLength + 1];
}
@@ -870,8 +884,7 @@ implements AttributeVisitor,
*/
private int remapBranchOffset(int offset, int branchOffset)
{
- return remapInstructionOffset(offset + branchOffset) -
- remapInstructionOffset(offset);
+ return remapInstructionOffset(offset + branchOffset) - newOffset;
}
diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java
index 3def03e..21e3c1f 100644
--- a/src/proguard/classfile/editor/MethodInvocationFixer.java
+++ b/src/proguard/classfile/editor/MethodInvocationFixer.java
@@ -138,7 +138,7 @@ implements AttributeVisitor,
else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
{
int invokeinterfaceConstant =
- (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass)) + 1) << 8;
+ (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)) << 8;
// But is it not an interface invocation, or is the parameter
// size incorrect?
@@ -247,7 +247,7 @@ implements AttributeVisitor,
System.out.println(" Method = "+referencedMethod);
if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
{
- System.out.println(" Parameter size = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass)+1)));
+ System.out.println(" Parameter size = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass), false)));
}
System.out.println(" Replacement instruction = "+replacementInstruction.toString(offset));
}
diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java
index 06f9cb6..e2ee933 100644
--- a/src/proguard/classfile/editor/VariableEditor.java
+++ b/src/proguard/classfile/editor/VariableEditor.java
@@ -113,15 +113,8 @@ implements AttributeVisitor
int newVariableIndex = 0;
for (int oldVariableIndex = 0; oldVariableIndex < oldMaxLocals; oldVariableIndex++)
{
- if (oldVariableIndex >= deleted.length ||
- !deleted[oldVariableIndex])
- {
- variableMap[oldVariableIndex] = newVariableIndex++;
- }
- else
- {
- variableMap[oldVariableIndex] = -1;
- }
+ variableMap[oldVariableIndex] = deleted[oldVariableIndex] ?
+ -1 : newVariableIndex++;
}
// Set the map.
diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/src/proguard/classfile/editor/VariableSizeUpdater.java
index 7ca609e..7568a42 100644
--- a/src/proguard/classfile/editor/VariableSizeUpdater.java
+++ b/src/proguard/classfile/editor/VariableSizeUpdater.java
@@ -57,18 +57,14 @@ implements AttributeVisitor,
// method.getName(clazz).equals("abc");
// The minimum variable size is determined by the arguments.
- codeAttribute.u2maxLocals = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
-
- // Non-static methods also have the 'this' variable.
- if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
- {
- codeAttribute.u2maxLocals++;
- }
+ codeAttribute.u2maxLocals =
+ ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
if (DEBUG)
{
System.out.println("VariableSizeUpdater: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
- System.out.println("Max locals: "+codeAttribute.u2maxLocals+" <- parameters");
+ System.out.println(" Max locals: "+codeAttribute.u2maxLocals+" <- parameters");
}
// Go over all instructions.
diff --git a/src/proguard/classfile/io/ProgramClassReader.java b/src/proguard/classfile/io/ProgramClassReader.java
index 493208f..36bdced 100644
--- a/src/proguard/classfile/io/ProgramClassReader.java
+++ b/src/proguard/classfile/io/ProgramClassReader.java
@@ -491,7 +491,7 @@ implements ClassVisitor,
// Read the parameter annotations of the given parameter.
int u2annotationsCount = dataInput.readUnsignedShort();
- Annotation[] annotations = new Annotation[u2annotationsCount];
+ Annotation[] annotations = new Annotation[u2annotationsCount];
for (int index = 0; index < u2annotationsCount; index++)
{
diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java
index 12b1ab2..8e324bd 100644
--- a/src/proguard/classfile/util/ClassReferenceInitializer.java
+++ b/src/proguard/classfile/util/ClassReferenceInitializer.java
@@ -426,6 +426,8 @@ implements ClassVisitor,
DescriptorClassEnumeration enumeration =
new DescriptorClassEnumeration(descriptor);
+ enumeration.nextFluff();
+
if (enumeration.hasMoreClassNames())
{
return findClass(enumeration.nextClassName());
@@ -453,7 +455,8 @@ implements ClassVisitor,
for (int index = 0; index < classCount; index++)
{
- String name = enumeration.nextClassName();
+ String fluff = enumeration.nextFluff();
+ String name = enumeration.nextClassName();
Clazz referencedClass = findClass(name);
diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java
index db3faeb..02486c5 100644
--- a/src/proguard/classfile/util/ClassUtil.java
+++ b/src/proguard/classfile/util/ClassUtil.java
@@ -34,9 +34,6 @@ public class ClassUtil
{
private static final String EMPTY_STRING = "";
- private static final InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration();
- private static final ExternalTypeEnumeration externalTypeEnumeration = new ExternalTypeEnumeration();
-
/**
* Checks whether the given class magic number is correct.
@@ -435,7 +432,8 @@ public class ClassUtil
*/
public static int internalMethodParameterCount(String internalMethodDescriptor)
{
- internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+ InternalTypeEnumeration internalTypeEnumeration =
+ new InternalTypeEnumeration(internalMethodDescriptor);
int counter = 0;
while (internalTypeEnumeration.hasMoreTypes())
@@ -452,7 +450,7 @@ public class ClassUtil
/**
* Returns the size taken up on the stack by the parameters of the given
* internal method descriptor. This accounts for long and double parameters
- * taking up two spaces.
+ * taking up two entries.
* @param internalMethodDescriptor the internal method descriptor,
* e.g. "<code>(ID)Z</code>".
* @return the size taken up on the stack,
@@ -460,9 +458,49 @@ public class ClassUtil
*/
public static int internalMethodParameterSize(String internalMethodDescriptor)
{
- internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+ return internalMethodParameterSize(internalMethodDescriptor, true);
+ }
+
+
+ /**
+ * Returns the size taken up on the stack by the parameters of the given
+ * internal method descriptor. This accounts for long and double parameters
+ * taking up two entries, and a non-static method taking up an additional
+ * entry.
+ * @param internalMethodDescriptor the internal method descriptor,
+ * e.g. "<code>(ID)Z</code>".
+ * @param accessFlags the access flags of the method,
+ * e.g. 0.
+ * @return the size taken up on the stack,
+ * e.g. 4.
+ */
+ public static int internalMethodParameterSize(String internalMethodDescriptor,
+ int accessFlags)
+ {
+ return internalMethodParameterSize(internalMethodDescriptor,
+ (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0);
+ }
+
+
+ /**
+ * Returns the size taken up on the stack by the parameters of the given
+ * internal method descriptor. This accounts for long and double parameters
+ * taking up two spaces, and a non-static method taking up an additional
+ * entry.
+ * @param internalMethodDescriptor the internal method descriptor,
+ * e.g. "<code>(ID)Z</code>".
+ * @param isStatic specifies whether the method is static,
+ * e.g. false.
+ * @return the size taken up on the stack,
+ * e.g. 4.
+ */
+ public static int internalMethodParameterSize(String internalMethodDescriptor,
+ boolean isStatic)
+ {
+ InternalTypeEnumeration internalTypeEnumeration =
+ new InternalTypeEnumeration(internalMethodDescriptor);
- int size = 0;
+ int size = isStatic ? 0 : 1;
while (internalTypeEnumeration.hasMoreTypes())
{
String internalType = internalTypeEnumeration.nextType();
@@ -477,7 +515,7 @@ public class ClassUtil
/**
* Returns the size taken up on the stack by the given internal type.
* The size is 1, except for long and double types, for which it is 2,
- * and for the void type, for which 0 is returned
+ * and for the void type, for which 0 is returned.
* @param internalType the internal type,
* e.g. "<code>I</code>".
* @return the size taken up on the stack,
@@ -683,7 +721,9 @@ public class ClassUtil
*/
public static String externalMethodName(String externalMethodNameAndArguments)
{
- externalTypeEnumeration.setDescriptor(externalMethodNameAndArguments);
+ ExternalTypeEnumeration externalTypeEnumeration =
+ new ExternalTypeEnumeration(externalMethodNameAndArguments);
+
return externalTypeEnumeration.methodName();
}
@@ -704,7 +744,9 @@ public class ClassUtil
StringBuffer internalMethodDescriptor = new StringBuffer();
internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
- externalTypeEnumeration.setDescriptor(externalMethodNameAndArguments);
+ ExternalTypeEnumeration externalTypeEnumeration =
+ new ExternalTypeEnumeration(externalMethodNameAndArguments);
+
while (externalTypeEnumeration.hasMoreTypes())
{
internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType()));
@@ -1025,7 +1067,9 @@ public class ClassUtil
{
StringBuffer externalMethodNameAndArguments = new StringBuffer();
- internalTypeEnumeration.setDescriptor(internalMethodDescriptor);
+ InternalTypeEnumeration internalTypeEnumeration =
+ new InternalTypeEnumeration(internalMethodDescriptor);
+
while (internalTypeEnumeration.hasMoreTypes())
{
externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType()));
diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java
index 336ea7c..f64065d 100644
--- a/src/proguard/classfile/util/DescriptorClassEnumeration.java
+++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java
@@ -24,42 +24,26 @@ import proguard.classfile.ClassConstants;
/**
* A <code>DescriptorClassEnumeration</code> provides an enumeration of all
- * classes mentioned in a given descriptor string.
- * <p>
- * A <code>DescriptorClassEnumeration</code> object can be reused for processing
- * different subsequent descriptors, by means of the <code>setDescriptor</code>
- * method.
+ * classes mentioned in a given descriptor or signature.
*
* @author Eric Lafortune
*/
public class DescriptorClassEnumeration
{
- private String descriptor;
- private int index;
+ private String descriptor;
+ private int index;
+ private int nestingLevel;
+ private boolean isInnerClassName;
+ private String accumulatedClassName;
- public DescriptorClassEnumeration(String descriptor)
- {
- setDescriptor(descriptor);
- }
-
- DescriptorClassEnumeration()
- {
- }
-
-
- void setDescriptor(String descriptor)
+ /**
+ * Creates a new DescriptorClassEnumeration for the given descriptor.
+ */
+ public DescriptorClassEnumeration(String descriptor)
{
this.descriptor = descriptor;
-
- reset();
- }
-
-
- public void reset()
- {
- index = 0;
}
@@ -71,11 +55,13 @@ public class DescriptorClassEnumeration
{
int count = 0;
- while (nextClassNameStartIndex() >= 0)
+ nextFluff();
+ while (hasMoreClassNames())
{
count++;
- nextClassNameEndIndex();
+ nextClassName();
+ nextFluff();
}
index = 0;
@@ -84,60 +70,122 @@ public class DescriptorClassEnumeration
}
+ /**
+ * Returns whether the enumeration can provide more class names from the
+ * descriptor.
+ */
public boolean hasMoreClassNames()
{
- return index >= 0 && nextClassNameStartIndex() >= 0;
+ return index < descriptor.length();
}
+ /**
+ * Returns the next fluff (surrounding class names) from the descriptor.
+ */
public String nextFluff()
{
int fluffStartIndex = index;
- int fluffEndIndex = nextClassNameStartIndex() + 1;
- // There may be fluff at the end of the descriptor.
- if (fluffEndIndex == 0)
+ // Find the first token marking the start of a class name 'L' or '.'.
+ loop: while (index < descriptor.length())
{
- fluffEndIndex = descriptor.length();
+ switch (descriptor.charAt(index++))
+ {
+ case ClassConstants.INTERNAL_TYPE_GENERIC_START:
+ {
+ nestingLevel++;
+ break;
+ }
+ case ClassConstants.INTERNAL_TYPE_GENERIC_END:
+ {
+ nestingLevel--;
+ break;
+ }
+ case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND:
+ {
+ continue loop;
+ }
+ case ClassConstants.INTERNAL_TYPE_CLASS_START:
+ {
+ // We've found the start of an ordinary class name.
+ nestingLevel += 2;
+ isInnerClassName = false;
+ break loop;
+ }
+ case ClassConstants.INTERNAL_TYPE_CLASS_END:
+ {
+ nestingLevel -= 2;
+ break;
+ }
+ case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR:
+ {
+ // We've found the start of an inner class name in a signature.
+ isInnerClassName = true;
+ break loop;
+ }
+ case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START:
+ {
+ // We've found the start of a type identifier. Skip to the end.
+ while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END);
+ break;
+ }
+ }
+
+ if (nestingLevel == 1 &&
+ descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END)
+ {
+ // We're at the start of a type parameter. Skip to the start
+ // of the bounds.
+ while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND);
+ }
}
- return descriptor.substring(fluffStartIndex, fluffEndIndex);
+ return descriptor.substring(fluffStartIndex, index);
}
+ /**
+ * Returns the next class name from the descriptor.
+ */
public String nextClassName()
{
- int classNameStartIndex = nextClassNameStartIndex() + 1;
- int classNameEndIndex = nextClassNameEndIndex();
-
- return descriptor.substring(classNameStartIndex, classNameEndIndex);
- }
-
-
- private int nextClassNameStartIndex()
- {
- index = descriptor.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START, index);
+ int classNameStartIndex = index;
- return index;
- }
-
-
- private int nextClassNameEndIndex()
- {
- while (++index < descriptor.length())
+ // Find the first token marking the end of a class name '<' or ';'.
+ loop: while (true)
{
- char c = descriptor.charAt(index);
- if (c == ClassConstants.INTERNAL_TYPE_CLASS_END ||
- c == ClassConstants.INTERNAL_TYPE_GENERIC_START)
+ switch (descriptor.charAt(index))
{
- return index;
+ case ClassConstants.INTERNAL_TYPE_GENERIC_START:
+ case ClassConstants.INTERNAL_TYPE_CLASS_END:
+ {
+ break loop;
+ }
}
+
+ index++;
}
- throw new IllegalArgumentException("Missing class name terminator in descriptor ["+descriptor+"]");
+ String className = descriptor.substring(classNameStartIndex, index);
+
+ // Recompose the inner class name if necessary.
+ accumulatedClassName = isInnerClassName ?
+ accumulatedClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR + className :
+ className;
+
+ return accumulatedClassName;
}
+ /**
+ * Returns whether the most recently returned class name was a recomposed
+ * inner class name from a signature.
+ */
+ public boolean isInnerClassName()
+ {
+ return isInnerClassName;
+ }
/**
@@ -153,8 +201,8 @@ public class DescriptorClassEnumeration
System.out.println(" Fluff: ["+enumeration.nextFluff()+"]");
while (enumeration.hasMoreClassNames())
{
- System.out.println(" Name: ["+enumeration.nextClassName()+"]");
- System.out.println(" Fluff: ["+enumeration.nextFluff()+"]");
+ System.out.println(" Name: ["+enumeration.nextClassName()+"]");
+ System.out.println(" Fluff: ["+enumeration.nextFluff()+"]");
}
}
catch (Exception ex)
diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java
index e425b9b..f4f9b26 100644
--- a/src/proguard/classfile/util/InternalTypeEnumeration.java
+++ b/src/proguard/classfile/util/InternalTypeEnumeration.java
@@ -25,12 +25,9 @@ import proguard.classfile.ClassConstants;
/**
* An <code>InternalTypeEnumeration</code> provides an enumeration of all
- * parameter types listed in a given internal descriptor string. The return type
- * can retrieved separately.
- * <p>
- * A <code>InternalTypeEnumeration</code> object can be reused for processing
- * different subsequent descriptors, by means of the <code>setDescriptor</code>
- * method.
+ * parameter types listed in a given internal method descriptor or signature.
+ * The signature can also be a class signature. The return type of a method
+ * descriptor can retrieved separately.
*
* @author Eric Lafortune
*/
@@ -40,69 +37,138 @@ public class InternalTypeEnumeration
private int index;
+ /**
+ * Creates a new InternalTypeEnumeration for the given method descriptor.
+ */
public InternalTypeEnumeration(String descriptor)
{
- setDescriptor(descriptor);
- }
-
-
- public InternalTypeEnumeration()
- {
- }
-
-
- void setDescriptor(String descriptor)
- {
this.descriptor = descriptor;
-
- reset();
- }
-
-
- public void reset()
- {
- index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN) + 1;
-
- if (index < 1)
- {
- throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]");
- }
+ this.index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN) + 1;
}
+ /**
+ * Returns whether the enumeration can provide more types from the method
+ * descriptor.
+ */
public boolean hasMoreTypes()
{
- return descriptor.charAt(index) != ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE;
+ return index < descriptor.length() &&
+ descriptor.charAt(index) != ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE;
}
+ /**
+ * Returns the next type from the method descriptor.
+ */
public String nextType()
{
int startIndex = index;
- // Include all leading array characters.
- while (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_ARRAY)
+ int nestingLevel = 0;
+ boolean parsingRawType = true;
+ boolean parsingArrayPrefix;
+ do
{
- index++;
- }
+ parsingArrayPrefix = false;
- // Class types consist of an entire string.
- if (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_CLASS_START)
- {
- index = descriptor.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END,
- index + 1);
- if (index < 0)
+ char c = descriptor.charAt(index++);
+
+ if (parsingRawType)
+ {
+ // Parse an array character, primitive type, or a token
+ // marking the beginning of an identifier (for a class or
+ // a variable type).
+ switch (c)
+ {
+ case ClassConstants.INTERNAL_TYPE_GENERIC_START:
+ {
+ parsingRawType = false;
+ nestingLevel++;
+ break;
+ }
+ case ClassConstants.INTERNAL_TYPE_GENERIC_END:
+ {
+ parsingRawType = false;
+ nestingLevel--;
+ break;
+ }
+ case ClassConstants.INTERNAL_TYPE_ARRAY:
+ {
+ parsingArrayPrefix = true;
+ break;
+ }
+ case ClassConstants.INTERNAL_TYPE_CLASS_START:
+ case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START:
+ {
+ parsingRawType = false;
+ nestingLevel += 2;
+ break;
+ }
+ }
+ }
+ else
{
- throw new IllegalArgumentException("Missing closing class type in descriptor ["+descriptor+"]");
+ // Parse the identifier, or a token marking its end.
+ switch (c)
+ {
+ case ClassConstants.INTERNAL_TYPE_CLASS_END:
+ parsingRawType = true;
+ nestingLevel -= 2;
+
+ // Are we at the start of a type parameter?
+ if (nestingLevel == 1 &&
+ descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END)
+ {
+ parsingRawType = false;
+ }
+ break;
+ case ClassConstants.INTERNAL_TYPE_GENERIC_START:
+ parsingRawType = true;
+ nestingLevel++;
+ break;
+ case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND:
+ parsingRawType = true;
+ break;
+ }
}
}
+ while (nestingLevel > 0 || parsingArrayPrefix);
- return descriptor.substring(startIndex, ++index);
+ return descriptor.substring(startIndex, index);
}
+ /**
+ * Returns the return type from the descriptor, assuming it's a method
+ * descriptor.
+ */
public String returnType()
{
return descriptor.substring(descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE) + 1);
}
+
+
+ /**
+ * A main method for testing the type enumeration.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ System.out.println("Descriptor ["+args[0]+"]");
+ InternalTypeEnumeration enumeration = new InternalTypeEnumeration(args[0]);
+
+ while (enumeration.hasMoreTypes())
+ {
+ System.out.println(" Type ["+enumeration.nextType()+"]");
+ }
+
+ System.out.println(" Return type ["+enumeration.returnType()+"]");
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
}
diff --git a/src/proguard/classfile/visitor/ClassPrinter.java b/src/proguard/classfile/visitor/ClassPrinter.java
index 9538b28..84677c3 100644
--- a/src/proguard/classfile/visitor/ClassPrinter.java
+++ b/src/proguard/classfile/visitor/ClassPrinter.java
@@ -556,7 +556,7 @@ implements ClassVisitor,
public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
{
println(visitorInfo(runtimeVisibleParameterAnnotationsAttribute) +
- " Runtime visible parameter annotations attribute:");
+ " Runtime visible parameter annotations attribute (parameter count = " + runtimeVisibleParameterAnnotationsAttribute.u2parametersCount + "):");
indent();
runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
@@ -567,7 +567,7 @@ implements ClassVisitor,
public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
{
println(visitorInfo(runtimeInvisibleParameterAnnotationsAttribute) +
- " Runtime invisible parameter annotations attribute:");
+ " Runtime invisible parameter annotations attribute (parameter count = " + runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount + "):");
indent();
runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
diff --git a/src/proguard/evaluation/BasicInvocationUnit.java b/src/proguard/evaluation/BasicInvocationUnit.java
index 5c5270a..3979fe2 100644
--- a/src/proguard/evaluation/BasicInvocationUnit.java
+++ b/src/proguard/evaluation/BasicInvocationUnit.java
@@ -63,11 +63,7 @@ implements InvocationUnit,
(method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
// Count the number of parameters, taking into account their categories.
- int parameterSize = ClassUtil.internalMethodParameterSize(descriptor);
- if (!isStatic)
- {
- parameterSize++;
- }
+ int parameterSize = ClassUtil.internalMethodParameterSize(descriptor, isStatic);
// Reuse the existing parameters object, ensuring the right size.
variables.reset(parameterSize);
diff --git a/src/proguard/evaluation/TracedStack.java b/src/proguard/evaluation/TracedStack.java
index 7bc5320..4e1abe6 100644
--- a/src/proguard/evaluation/TracedStack.java
+++ b/src/proguard/evaluation/TracedStack.java
@@ -30,37 +30,34 @@ import proguard.evaluation.value.Value;
* 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
*/
public class TracedStack extends Stack
{
private Value producerValue;
- private Value collectedProducerValue;
- private final Stack producerStack;
- private final Stack consumerStack;
+ private Stack producerStack;
+ /**
+ * Creates a new TracedStack with a given maximum size.
+ */
public TracedStack(int maxSize)
{
super(maxSize);
producerStack = new Stack(maxSize);
- consumerStack = new Stack(maxSize);
}
+ /**
+ * Creates a new TracedStack that is a copy of the given TracedStack.
+ */
public TracedStack(TracedStack tracedStack)
{
super(tracedStack);
producerStack = new Stack(tracedStack.producerStack);
- consumerStack = new Stack(tracedStack.consumerStack);
}
@@ -75,22 +72,6 @@ public class TracedStack extends Stack
/**
- * Sets the initial Value with which all values stored along with load
- * instructions will be generalized.
- */
- public void setCollectedProducerValue(Value collectedProducerValue)
- {
- this.collectedProducerValue = collectedProducerValue;
- }
-
-
- public Value getCollectedProducerValue()
- {
- return collectedProducerValue;
- }
-
-
- /**
* 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.
@@ -138,42 +119,6 @@ public class TracedStack extends Stack
}
- /**
- * 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 getTopConsumerValue(int index)
- {
- return ((MutableValue)consumerStack.getTop(index)).getContainedValue();
- }
-
-
- /**
- * 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 consumer value to set.
- */
- public void setTopConsumerValue(int index, Value value)
- {
- ((MutableValue)consumerStack.getTop(index)).setContainedValue(value);
- consumerStack.setTop(index, new MutableValue());
- }
-
-
// Implementations for Stack.
public void reset(int size)
@@ -181,7 +126,6 @@ public class TracedStack extends Stack
super.reset(size);
producerStack.reset(size);
- consumerStack.reset(size);
}
public void copy(TracedStack other)
@@ -189,15 +133,13 @@ public class TracedStack extends Stack
super.copy(other);
producerStack.copy(other.producerStack);
- consumerStack.copy(other.consumerStack);
}
public boolean generalize(TracedStack other)
{
return
super.generalize(other) |
- producerStack.generalize(other.producerStack) |
- consumerStack.generalize(other.consumerStack);
+ producerStack.generalize(other.producerStack);
}
public void clear()
@@ -205,7 +147,6 @@ public class TracedStack extends Stack
super.clear();
producerStack.clear();
- consumerStack.clear();
}
public void removeTop(int index)
@@ -213,19 +154,18 @@ public class TracedStack extends Stack
super.removeTop(index);
producerStack.removeTop(index);
- consumerStack.removeTop(index);
}
public void push(Value value)
{
super.push(value);
- tracePush();
+ producerPush();
// Account for the extra space required by Category 2 values.
if (value.isCategory2())
{
- tracePush();
+ producerPush();
}
}
@@ -233,12 +173,12 @@ public class TracedStack extends Stack
{
Value value = super.pop();
- tracePop();
+ producerPop();
// Account for the extra space required by Category 2 values.
if (value.isCategory2())
{
- tracePop();
+ producerPop();
}
return value;
@@ -248,120 +188,100 @@ public class TracedStack extends Stack
{
super.pop1();
- tracePop();
+ producerPop();
}
public void pop2()
{
super.pop2();
- tracePop();
- tracePop();
+ producerPop();
+ producerPop();
}
public void dup()
{
super.dup();
- producerGeneralize(0);
- producerStack.dup();
-
- consumerPop();
- consumerPush();
- consumerPush();
+ producerPop();
+ producerPush();
+ producerPush();
}
public void dup_x1()
{
super.dup_x1();
- producerGeneralize(0);
- producerStack.dup_x1();
-
- consumerPop();
- consumerPush();
- consumerStack.swap();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
+ producerPush();
}
public void dup_x2()
{
super.dup_x2();
- producerGeneralize(0);
- producerStack.dup_x2();
-
- consumerPop();
- consumerPush();
- consumerStack.dup_x2();
- consumerStack.pop();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
}
public void dup2()
{
super.dup2();
- producerGeneralize(0);
- producerGeneralize(1);
- producerStack.dup2();
-
- consumerPop();
- consumerPop();
- consumerPush();
- consumerPush();
- consumerPush();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
}
public void dup2_x1()
{
super.dup2_x1();
- producerGeneralize(0);
- producerGeneralize(1);
- producerStack.dup2_x1();
-
- consumerPop();
- consumerPop();
- consumerPush();
- consumerPush();
- consumerStack.dup2_x1();
- consumerStack.pop2();
- consumerPush();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
}
public void dup2_x2()
{
super.dup2_x2();
- producerGeneralize(0);
- producerGeneralize(1);
- producerStack.dup2_x2();
-
- consumerPop();
- consumerPop();
- consumerPush();
- consumerPush();
- consumerStack.dup2_x2();
- consumerStack.pop2();
- consumerPush();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
+ producerPush();
}
public void swap()
{
super.swap();
- producerGeneralize(0);
- producerGeneralize(1);
- producerStack.swap();
-
- consumerPop();
- consumerPop();
- consumerPush();
- consumerPush();
+ producerPop();
+ producerPop();
+ producerPush();
+ producerPush();
}
@@ -378,16 +298,14 @@ public class TracedStack extends Stack
TracedStack other = (TracedStack)object;
return super.equals(object) &&
- this.producerStack.equals(other.producerStack) &&
- this.consumerStack.equals(other.consumerStack);
+ this.producerStack.equals(other.producerStack);
}
public int hashCode()
{
return super.hashCode() ^
- producerStack.hashCode() ^
- consumerStack.hashCode();
+ producerStack.hashCode();
}
@@ -399,13 +317,10 @@ public class TracedStack extends Stack
{
Value value = this.values[index];
Value producerValue = producerStack.getBottom(index);
- Value consumerValue = consumerStack.getBottom(index);
buffer = buffer.append('[')
.append(producerValue == null ? "empty" : producerValue.toString())
.append('>')
.append(value == null ? "empty" : value.toString())
- .append('>')
- .append(consumerValue == null ? "empty" : consumerValue.toString())
.append(']');
}
@@ -415,58 +330,14 @@ public class TracedStack extends Stack
// Small utility methods.
- private void tracePush()
- {
- producerPush();
- consumerPush();
- }
-
-
private void producerPush()
{
producerStack.push(producerValue);
}
- private void consumerPush()
- {
- consumerStack.push(new MutableValue());
- }
-
-
- private void tracePop()
- {
- producerPop();
- consumerPop();
- }
-
-
private void producerPop()
{
- 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);
- }
+ producerStack.pop();
}
}
diff --git a/src/proguard/evaluation/TracedVariables.java b/src/proguard/evaluation/TracedVariables.java
index b921d2a..e515101 100644
--- a/src/proguard/evaluation/TracedVariables.java
+++ b/src/proguard/evaluation/TracedVariables.java
@@ -23,8 +23,8 @@ package proguard.evaluation;
import proguard.evaluation.value.Value;
/**
- * This Variables class saves additional information with variables, 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
@@ -42,27 +42,29 @@ public class TracedVariables extends Variables
private Value producerValue;
- private Value collectedProducerValue;
- private final Variables producerVariables;
- private final Variables consumerVariables;
+ private Variables producerVariables;
private int initializationIndex;
+ /**
+ * Creates a new TracedVariables with a given size.
+ */
public TracedVariables(int size)
{
super(size);
producerVariables = new Variables(size);
- consumerVariables = new Variables(size);
}
+ /**
+ * Creates a new TracedVariables that is a copy of the given TracedVariables.
+ */
public TracedVariables(TracedVariables tracedVariables)
{
super(tracedVariables);
producerVariables = new Variables(tracedVariables.producerVariables);
- consumerVariables = new Variables(tracedVariables.consumerVariables);
}
@@ -76,21 +78,6 @@ public class TracedVariables extends Variables
/**
- * Sets the initial Value with which all values stored along with load
- * instructions will be generalized.
- */
- public void setCollectedProducerValue(Value collectedProducerValue)
- {
- this.collectedProducerValue = collectedProducerValue;
- }
-
- public Value getCollectedProducerValue()
- {
- return collectedProducerValue;
- }
-
-
- /**
* Resets the initialization index.
*/
public void resetInitialization()
@@ -98,6 +85,10 @@ public class TracedVariables extends Variables
initializationIndex = NONE;
}
+
+ /**
+ * Returns the most recent initialization index since it has been reset.
+ */
public int getInitializationIndex()
{
return initializationIndex;
@@ -127,30 +118,6 @@ public class TracedVariables extends Variables
}
- /**
- * Gets the consumer Value for the specified variable, without disturbing it.
- * @param index the variable index.
- * @return the producer value of the given variable.
- */
- public Value getConsumerValue(int index)
- {
- return ((MutableValue)consumerVariables.getValue(index)).getContainedValue();
- }
-
-
- /**
- * Sets the specified consumer Value for the given variable, without
- * disturbing it.
- * @param index the variable index.
- * @param value the consumer value to set.
- */
- public void setConsumerValue(int index, Value value)
- {
- ((MutableValue)consumerVariables.getValue(index)).setContainedValue(value);
- consumerVariables.store(index, new MutableValue());
- }
-
-
// Implementations for Variables.
public void reset(int size)
@@ -158,7 +125,6 @@ public class TracedVariables extends Variables
super.reset(size);
producerVariables.reset(size);
- consumerVariables.reset(size);
}
public void initialize(TracedVariables other)
@@ -166,7 +132,6 @@ public class TracedVariables extends Variables
super.initialize(other);
producerVariables.initialize(other.producerVariables);
- consumerVariables.initialize(other.consumerVariables);
}
public boolean generalize(TracedVariables other,
@@ -184,12 +149,10 @@ public class TracedVariables extends Variables
if (values[index] == null)
{
producerVariables.values[index] = null;
- consumerVariables.values[index] = null;
if (clearConflictingOtherVariables)
{
other.producerVariables.values[index] = null;
- other.consumerVariables.values[index] = null;
}
}
}
@@ -215,31 +178,11 @@ public class TracedVariables extends Variables
// Store the producer value in its producer variable.
producerVariables.store(index, producerValue);
- // Reserve a space for the consumer value.
- MutableValue mutableValue = new MutableValue();
- consumerVariables.store(index, mutableValue);
-
// Account for the extra space required by Category 2 values.
if (value.isCategory2())
{
producerVariables.store(index+1, producerValue);
- consumerVariables.store(index+1, mutableValue);
- }
- }
-
- public Value load(int index)
- {
- // Load and accumulate the producer value of the variable.
- if (collectedProducerValue != null)
- {
- collectedProducerValue = collectedProducerValue.generalize(producerVariables.load(index));
}
-
- // Generalize the consumer value of the variable.
- ((MutableValue)consumerVariables.load(index)).generalizeContainedValue(producerValue);
-
- // Return the value itself.
- return super.load(index);
}
@@ -256,16 +199,14 @@ public class TracedVariables extends Variables
TracedVariables other = (TracedVariables)object;
return super.equals(object) &&
- this.producerVariables.equals(other.producerVariables) /*&&
- this.consumerVariables.equals(other.consumerVariables)*/;
+ this.producerVariables.equals(other.producerVariables);
}
public int hashCode()
{
return super.hashCode() ^
- producerVariables.hashCode() /*^
- consumerVariables.hashCode()*/;
+ producerVariables.hashCode();
}
@@ -277,13 +218,10 @@ public class TracedVariables extends Variables
{
Value value = this.values[index];
Value producerValue = producerVariables.getValue(index);
- Value consumerValue = consumerVariables.getValue(index);
buffer = buffer.append('[')
.append(producerValue == null ? "empty" : producerValue.toString())
.append('>')
.append(value == null ? "empty" : value.toString())
- .append('>')
- .append(consumerValue == null ? "empty" : consumerValue.toString())
.append(']');
}
diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java
index 8c8ba9f..794d96c 100644
--- a/src/proguard/gui/ClassSpecificationDialog.java
+++ b/src/proguard/gui/ClassSpecificationDialog.java
@@ -55,9 +55,9 @@ final class ClassSpecificationDialog extends JDialog
private final JRadioButton keepClassMembersRadioButton = new JRadioButton(msg("keepClassMembers"));
private final JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(msg("keepClassesWithMembers"));
- private final JRadioButton allowShrinkingRadioButton = new JRadioButton(msg("allowShrinking"));
- private final JRadioButton allowOptimizationRadioButton = new JRadioButton(msg("allowOptimization"));
- private final JRadioButton allowObfuscationRadioButton = new JRadioButton(msg("allowObfuscation"));
+ private final JCheckBox allowShrinkingCheckBox = new JCheckBox(msg("allowShrinking"));
+ private final JCheckBox allowOptimizationCheckBox = new JCheckBox(msg("allowOptimization"));
+ private final JCheckBox allowObfuscationCheckBox = new JCheckBox(msg("allowObfuscation"));
private final JRadioButton[] publicRadioButtons;
@@ -179,9 +179,9 @@ final class ClassSpecificationDialog extends JDialog
allowOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
msg("allowTitle")));
- allowOptionPanel.add(tip(allowShrinkingRadioButton, "allowShrinkingTip"), constraintsLastStretch);
- allowOptionPanel.add(tip(allowOptimizationRadioButton, "allowOptimizationTip"), constraintsLastStretch);
- allowOptionPanel.add(tip(allowObfuscationRadioButton, "allowObfuscationTip"), constraintsLastStretch);
+ allowOptionPanel.add(tip(allowShrinkingCheckBox, "allowShrinkingTip"), constraintsLastStretch);
+ allowOptionPanel.add(tip(allowOptimizationCheckBox, "allowOptimizationTip"), constraintsLastStretch);
+ allowOptionPanel.add(tip(allowObfuscationCheckBox, "allowObfuscationTip"), constraintsLastStretch);
// Create the access panel.
JPanel accessPanel = new JPanel(layout);
@@ -241,8 +241,8 @@ final class ClassSpecificationDialog extends JDialog
{
boolean visible = !allowOptionPanel.isVisible();
- allowOptionPanel.setVisible(visible);
- annotationTypePanel.setVisible(visible);
+ allowOptionPanel .setVisible(visible);
+ annotationTypePanel .setVisible(visible);
extendsAnnotationTypePanel.setVisible(visible);
advancedButton.setText(msg(visible ? "basic" : "advanced"));
@@ -292,7 +292,7 @@ final class ClassSpecificationDialog extends JDialog
mainPanel.add(okButton, okButtonConstraints);
mainPanel.add(cancelButton, cancelButtonConstraints);
- getContentPane().add(mainPanel);
+ getContentPane().add(new JScrollPane(mainPanel));
}
@@ -361,9 +361,9 @@ final class ClassSpecificationDialog extends JDialog
keepOptionRadioButton.setSelected(true);
// Set the allow radio buttons.
- allowShrinkingRadioButton .setSelected(allowShrinking);
- allowOptimizationRadioButton.setSelected(allowOptimization);
- allowObfuscationRadioButton .setSelected(allowObfuscation);
+ allowShrinkingCheckBox .setSelected(allowShrinking);
+ allowOptimizationCheckBox.setSelected(allowOptimization);
+ allowObfuscationCheckBox .setSelected(allowObfuscation);
setClassSpecification(keepSpecification);
}
@@ -409,9 +409,9 @@ final class ClassSpecificationDialog extends JDialog
{
boolean markClasses = !keepClassMembersRadioButton .isSelected();
boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
- boolean allowShrinking = allowShrinkingRadioButton .isSelected();
- boolean allowOptimization = allowOptimizationRadioButton .isSelected();
- boolean allowObfuscation = allowObfuscationRadioButton .isSelected();
+ boolean allowShrinking = allowShrinkingCheckBox .isSelected();
+ boolean allowOptimization = allowOptimizationCheckBox .isSelected();
+ boolean allowObfuscation = allowObfuscationCheckBox .isSelected();
return new KeepSpecification(markClasses,
markConditionally,
diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties
index 4acb6ac..9fa29b1 100644
--- a/src/proguard/gui/GUIResources.properties
+++ b/src/proguard/gui/GUIResources.properties
@@ -25,7 +25,7 @@ preverification = Preverification
#
# Panel titles.
#
-welcome = Welcome to ProGuard, version 4.1
+welcome = Welcome to ProGuard, version 4.2
options = Options
keepAdditional = Keep additional classes and class members
keepNamesAdditional = Keep additional class names and class member names
diff --git a/src/proguard/gui/MemberSpecificationDialog.java b/src/proguard/gui/MemberSpecificationDialog.java
index 32243a7..31b6b4e 100644
--- a/src/proguard/gui/MemberSpecificationDialog.java
+++ b/src/proguard/gui/MemberSpecificationDialog.java
@@ -265,7 +265,7 @@ final class MemberSpecificationDialog extends JDialog
mainPanel.add(okButton, okButtonConstraints);
mainPanel.add(cancelButton, cancelButtonConstraints);
- getContentPane().add(mainPanel);
+ getContentPane().add(new JScrollPane(mainPanel));
}
diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java
index 991ecf9..6d3705a 100644
--- a/src/proguard/gui/ProGuardGUI.java
+++ b/src/proguard/gui/ProGuardGUI.java
@@ -1092,8 +1092,8 @@ public class ProGuardGUI extends JFrame
configuration.overloadAggressively = overloadAggressivelyCheckBox .isSelected();
configuration.useUniqueClassMemberNames = useUniqueClassMemberNamesCheckBox .isSelected();
configuration.useMixedCaseClassNames = useMixedCaseClassNamesCheckBox .isSelected();
- configuration.flattenPackageHierarchy = flattenPackageHierarchyCheckBox .isSelected() ? ClassUtil.externalClassName(flattenPackageHierarchyTextField .getText()) : null;
- configuration.repackageClasses = repackageClassesCheckBox .isSelected() ? ClassUtil.externalClassName(repackageClassesTextField .getText()) : null;
+ configuration.flattenPackageHierarchy = flattenPackageHierarchyCheckBox .isSelected() ? ClassUtil.internalClassName(flattenPackageHierarchyTextField .getText()) : null;
+ configuration.repackageClasses = repackageClassesCheckBox .isSelected() ? ClassUtil.internalClassName(repackageClassesTextField .getText()) : null;
configuration.keepAttributes = keepAttributesCheckBox .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField .getText()) : null;
configuration.newSourceFileAttribute = newSourceFileAttributeCheckBox .isSelected() ? newSourceFileAttributeTextField .getText() : null;
configuration.adaptResourceFileNames = adaptResourceFileNamesCheckBox .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileNamesTextField .getText()) : null;
diff --git a/src/proguard/gui/boilerplate.pro b/src/proguard/gui/boilerplate.pro
index 4a1ac2b..cd6b975 100644
--- a/src/proguard/gui/boilerplate.pro
+++ b/src/proguard/gui/boilerplate.pro
@@ -13,8 +13,8 @@
# Keep - Midlets. Keep all extensions of javax.microedition.midlet.MIDlet.
-keep public class * extends javax.microedition.midlet.MIDlet
-# Keep - Xlets. Keep all extensions of java.tv.xlet.Xlet.
--keep public class * extends java.tv.xlet.Xlet
+# Keep - Xlets. Keep all extensions of javax.tv.xlet.Xlet.
+-keep public class * extends javax.tv.xlet.Xlet
# Keep - Library. Keep all public and protected classes, fields, and methods.
-keep public class * {
diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryRenamer.java
index 97872f5..43bb246 100644
--- a/src/proguard/io/DataEntryRenamer.java
+++ b/src/proguard/io/DataEntryRenamer.java
@@ -48,31 +48,58 @@ public class DataEntryRenamer implements DataEntryReader
public void read(DataEntry dataEntry) throws IOException
{
- String dataEntryName = dataEntry.getName();
+ // Delegate to the actual data entry reader.
+ dataEntryReader.read(renamedDataEntry(dataEntry));
+ }
- int suffixIndex = dataEntryName.lastIndexOf('.');
- String className = suffixIndex > 0 ?
- dataEntryName.substring(0, suffixIndex) :
- dataEntryName;
+ /**
+ * Create a renamed data entry, if possible.
+ */
+ private DataEntry renamedDataEntry(DataEntry dataEntry)
+ {
+ String dataEntryName = dataEntry.getName();
- // Find the class corrsponding to the data entry.
- Clazz clazz = classPool.getClass(className);
- if (clazz != null)
+ // Try to find a corresponding class name by removing increasingly
+ // long suffixes,
+ for (int suffixIndex = dataEntryName.length() - 1;
+ suffixIndex > 0;
+ suffixIndex--)
{
- // Rename the data entry if necessary.
- String newClassName = clazz.getName();
- if (!className.equals(newClassName))
+ char c = dataEntryName.charAt(suffixIndex);
+ if (!Character.isJavaIdentifierPart(c))
{
- String newDataEntryName = suffixIndex > 0 ?
- newClassName + dataEntryName.substring(suffixIndex) :
- newClassName;
+ // Stop looking at the first package separator.
+ if (c == ClassConstants.INTERNAL_PACKAGE_SEPARATOR)
+ {
+ break;
+ }
- dataEntry = new RenamedDataEntry(dataEntry, newDataEntryName);
+ // Chop off the suffix.
+ String className = dataEntryName.substring(0, suffixIndex);
+
+ // Is there a class corresponding to the data entry?
+ Clazz clazz = classPool.getClass(className);
+ if (clazz != null)
+ {
+ // Did the class get a new name?
+ String newClassName = clazz.getName();
+ if (!className.equals(newClassName))
+ {
+ // Return a renamed data entry.
+ String newDataEntryName = suffixIndex > 0 ?
+ newClassName + dataEntryName.substring(suffixIndex) :
+ newClassName;
+
+ return new RenamedDataEntry(dataEntry, newDataEntryName);
+ }
+
+ // Otherwise stop looking.
+ break;
+ }
}
}
- // Delegate to the actual data entry reader.
- dataEntryReader.read(dataEntry);
+ return dataEntry;
}
}
diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/src/proguard/obfuscate/ClassObfuscator.java
index 3833551..320ba85 100644
--- a/src/proguard/obfuscate/ClassObfuscator.java
+++ b/src/proguard/obfuscate/ClassObfuscator.java
@@ -130,7 +130,7 @@ implements ClassVisitor,
// the outer class prefix, if any, or it may be the fixed base
// package, if classes are to be repackaged.
String newPackagePrefix = newClassName != null ?
- newClassName + ClassConstants.INNER_CLASS_SEPARATOR :
+ newClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR :
newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName()));
// Come up with a new class name.
diff --git a/src/proguard/optimize/DuplicateInitializerFixer.java b/src/proguard/optimize/DuplicateInitializerFixer.java
new file mode 100644
index 0000000..50e23b0
--- /dev/null
+++ b/src/proguard/optimize/DuplicateInitializerFixer.java
@@ -0,0 +1,212 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 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.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.editor.ConstantPoolEditor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This MemberVisitor adds an additional parameter to the duplicate
+ * initialization methods that it visits.
+ */
+public class DuplicateInitializerFixer
+extends SimplifiedVisitor
+implements MemberVisitor,
+ AttributeVisitor
+{
+ private static final boolean DEBUG = false;
+
+ private static final char[] TYPES = new char[]
+ {
+ ClassConstants.INTERNAL_TYPE_BYTE,
+ ClassConstants.INTERNAL_TYPE_CHAR,
+ ClassConstants.INTERNAL_TYPE_SHORT,
+ ClassConstants.INTERNAL_TYPE_INT,
+ ClassConstants.INTERNAL_TYPE_BOOLEAN
+ };
+
+
+ private final MemberVisitor extraFixedInitializerVisitor;
+
+ private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+
+
+ /**
+ * Creates a new DuplicateInitializerFixer.
+ */
+ public DuplicateInitializerFixer()
+ {
+ this(null);
+ }
+
+
+ /**
+ * Creates a new DuplicateInitializerFixer with an extra visitor.
+ * @param extraFixedInitializerVisitor an optional extra visitor for all
+ * initializers that have been fixed.
+ */
+ public DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor)
+ {
+ this.extraFixedInitializerVisitor = extraFixedInitializerVisitor;
+ }
+
+
+ // Implementations for MemberVisitor.
+
+ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+ {
+ // Is it a class instance initializer?
+ String name = programMethod.getName(programClass);
+ if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+ {
+ // Is there already another initializer with the same descriptor?
+ String descriptor = programMethod.getDescriptor(programClass);
+ Method similarMethod = programClass.findMethod(name, descriptor);
+ if (!programMethod.equals(similarMethod))
+ {
+ // Should this initializer be preserved?
+ if (!KeepMarker.isKept(programMethod))
+ {
+ // Fix the other initializer.
+ programMethod = (ProgramMethod)similarMethod;
+ }
+
+ int index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+
+ // Try to find a new, unique descriptor.
+ for (int typeIndex = 0; typeIndex < TYPES.length; typeIndex++)
+ {
+ String newDescriptor =
+ descriptor.substring(0, index) +
+ TYPES[typeIndex] +
+ descriptor.substring(index);
+
+ // Is the new initializer descriptor unique?
+ if (programClass.findMethod(name, newDescriptor) == null)
+ {
+ if (DEBUG)
+ {
+ System.out.println("DuplicateInitializerFixer:");
+ System.out.println(" ["+programClass.getName()+"]: "+name+descriptor+" -> "+newDescriptor);
+ }
+
+ // Update the descriptor.
+ programMethod.u2descriptorIndex =
+ constantPoolEditor.addUtf8Constant(programClass,
+ newDescriptor);
+
+ // Fix the local variable frame size, the method
+ // signature, and the parameter annotations, if
+ // necessary.
+ programMethod.attributesAccept(programClass,
+ this);
+
+ // Visit the initializer, if required.
+ if (extraFixedInitializerVisitor != null)
+ {
+ extraFixedInitializerVisitor.visitProgramMethod(programClass, programMethod);
+ }
+
+ // We're done with this constructor.
+ return;
+ }
+ }
+
+ throw new IllegalStateException("Can't find unique constructor descriptor for ["+
+ programClass.getName()+"."+
+ programMethod.getName(programClass)+
+ programMethod.getDescriptor(programClass)+"]");
+ }
+ }
+ }
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ // The minimum variable size is determined by the arguments.
+ int maxLocals =
+ ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
+
+ if (codeAttribute.u2maxLocals < maxLocals)
+ {
+ codeAttribute.u2maxLocals = maxLocals;
+ }
+ }
+
+
+ public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+ {
+ String descriptor = method.getDescriptor(clazz);
+ int descriptorIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+ String signature = clazz.getString(signatureAttribute.u2signatureIndex);
+ int signatureIndex = signature.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+
+ String newSignature = signature.substring(0, signatureIndex) +
+ descriptor.charAt(descriptorIndex - 1) +
+ signature.substring(signatureIndex);
+
+ // Update the signature.
+ signatureAttribute.u2signatureIndex =
+ constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+ newSignature);
+ }
+
+
+public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+ {
+
+ // Update the number of parameters.
+ int oldParametersCount = parameterAnnotationsAttribute.u2parametersCount++;
+
+ if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null ||
+ parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u2parametersCount)
+ {
+ int[] annotationsCounts = new int[parameterAnnotationsAttribute.u2parametersCount];
+ Annotation[][] annotations = new Annotation[parameterAnnotationsAttribute.u2parametersCount][];
+
+ System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount,
+ 0,
+ annotationsCounts,
+ 0,
+ oldParametersCount);
+
+ System.arraycopy(parameterAnnotationsAttribute.parameterAnnotations,
+ 0,
+ annotations,
+ 0,
+ oldParametersCount);
+
+ parameterAnnotationsAttribute.u2parameterAnnotationsCount = annotationsCounts;
+ parameterAnnotationsAttribute.parameterAnnotations = annotations;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java
new file mode 100644
index 0000000..49111b1
--- /dev/null
+++ b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java
@@ -0,0 +1,123 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 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.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This AttributeVisitor adds an additional integer parameter to the tweaked
+ * initialization method invocations that it visits.
+ */
+public class DuplicateInitializerInvocationFixer
+extends SimplifiedVisitor
+implements AttributeVisitor,
+ InstructionVisitor,
+ ConstantVisitor,
+ MemberVisitor
+{
+ private static final boolean DEBUG = false;
+
+ private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
+
+ private String descriptor;
+ private boolean hasBeenFixed;
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+
+ // Reset the code changes.
+ codeAttributeEditor.reset(codeAttribute.u4codeLength);
+
+ // Fix any duplicate constructor invocations.
+ codeAttribute.instructionsAccept(clazz,
+ method,
+ this);
+
+ // Apply all accumulated changes to the code.
+ codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+ {
+ if (constantInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL)
+ {
+ hasBeenFixed = false;
+ clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+
+ if (hasBeenFixed)
+ {
+ Instruction extraInstruction =
+ new SimpleInstruction(InstructionConstants.OP_ICONST_0);
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ extraInstruction);
+
+ if (DEBUG)
+ {
+ System.out.println("DuplicateInitializerInvocationFixer:");
+ System.out.println(" Inserting "+extraInstruction.toString()+" before "+constantInstruction.toString(offset));
+ }
+ }
+ }
+ }
+
+
+ // Implementations for ConstantVisitor.
+
+ public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+ {
+ // Check the referenced constructor descriptor.
+ descriptor = methodrefConstant.getType(clazz);
+ methodrefConstant.referencedMemberAccept(this);
+ }
+
+
+ // Implementations for MemberVisitor.
+
+ public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {}
+
+
+ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+ {
+ hasBeenFixed = !descriptor.equals(programMethod.getDescriptor(programClass));
+ }
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/src/proguard/optimize/MethodDescriptorShrinker.java
index 7216d17..0b2c1da 100644
--- a/src/proguard/optimize/MethodDescriptorShrinker.java
+++ b/src/proguard/optimize/MethodDescriptorShrinker.java
@@ -21,7 +21,7 @@
package proguard.optimize;
import proguard.classfile.*;
-import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.*;
import proguard.classfile.attribute.annotation.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.editor.ConstantPoolEditor;
@@ -79,34 +79,20 @@ implements MemberVisitor,
{
// Update the descriptor if it has any unused parameters.
String descriptor = programMethod.getDescriptor(programClass);
- String newDescriptor = shrinkDescriptor(programClass, programMethod);
+ String newDescriptor = shrinkDescriptor(programMethod, descriptor);
+
if (!descriptor.equals(newDescriptor))
{
- // Shrink the parameter annotations.
+ // Shrink the signature and parameter annotations.
programMethod.attributesAccept(programClass, this);
- String name = programMethod.getName(programClass);
- String newName;
-
- // Is it a class instance initializer?
- if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
- {
- // Is there already another initializer with the same descriptor?
- if (programClass.findMethod(name, newDescriptor) != null)
- {
- // Cancel the shrinking. Mark all parameters.
- ParameterUsageMarker.markUsedParameters(programMethod, -1L);
+ String name = programMethod.getName(programClass);
+ String newName = name;
- return;
- }
-
- // We have to keep the initializer's name.
- newName = name;
- }
- else
+ // Append a code, if the method isn't a class instance initializer.
+ if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
{
- // Create a unique name.
- newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
+ newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
}
if (DEBUG)
@@ -126,10 +112,13 @@ implements MemberVisitor,
constantPoolEditor.addUtf8Constant(programClass, newName);
}
- // Clear the unused referenced classes.
- shrinkReferencedClasses(programClass, programMethod);
+ // Update the referenced classes.
+ programMethod.referencedClasses =
+ shrinkReferencedClasses(programMethod,
+ descriptor,
+ programMethod.referencedClasses);
- // Update the descriptor.
+ // Finally, update the descriptor itself.
programMethod.u2descriptorIndex =
constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
@@ -147,6 +136,25 @@ implements MemberVisitor,
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+ public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+ {
+ // Compute the new signature.
+ String signature = clazz.getString(signatureAttribute.u2signatureIndex);
+ String newSignature = shrinkDescriptor(method, signature);
+
+ // Update the signature.
+ signatureAttribute.u2signatureIndex =
+ constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+ newSignature);
+
+ // Update the referenced classes.
+ signatureAttribute.referencedClasses =
+ shrinkReferencedClasses(method,
+ signature,
+ signatureAttribute.referencedClasses);
+ }
+
+
public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
{
int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
@@ -195,10 +203,9 @@ implements MemberVisitor,
// Small utility methods.
/**
- * Returns a shrunk descriptor of the given method.
+ * Returns a shrunk descriptor or signature of the given method.
*/
- public static String shrinkDescriptor(ProgramClass clazz,
- ProgramMethod method)
+ private String shrinkDescriptor(Method method, String descriptor)
{
// All parameters of non-static methods are shifted by one in the local
// variable frame.
@@ -208,17 +215,11 @@ implements MemberVisitor,
// Go over the parameters.
InternalTypeEnumeration internalTypeEnumeration =
- new InternalTypeEnumeration(method.getDescriptor(clazz));
+ new InternalTypeEnumeration(descriptor);
StringBuffer newDescriptorBuffer = new StringBuffer();
newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
- if (DEBUG)
- {
- System.out.println("MethodDescriptorShrinker: "+method.getName(clazz)+method.getDescriptor(clazz));
- System.out.println(" parameter size = " + ParameterUsageMarker.getParameterSize(method));
- }
-
while (internalTypeEnumeration.hasMoreTypes())
{
String type = internalTypeEnumeration.nextType();
@@ -228,7 +229,7 @@ implements MemberVisitor,
}
else if (DEBUG)
{
- System.out.println(" Deleting parameter #"+parameterIndex+" ("+type+")");
+ System.out.println(" Deleting parameter #"+parameterIndex+" ["+type+"]");
}
parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
@@ -244,11 +245,10 @@ implements MemberVisitor,
/**
* Shrinks the array of referenced classes of the given method.
*/
- private static void shrinkReferencedClasses(ProgramClass clazz,
- ProgramMethod method)
+ private Clazz[] shrinkReferencedClasses(Method method,
+ String descriptor,
+ Clazz[] referencedClasses)
{
- Clazz[] referencedClasses = method.referencedClasses;
-
if (referencedClasses != null)
{
// All parameters of non-static methods are shifted by one in the local
@@ -261,27 +261,28 @@ implements MemberVisitor,
int newReferencedClassIndex = 0;
// Go over the parameters.
- String descriptor = method.getDescriptor(clazz);
InternalTypeEnumeration internalTypeEnumeration =
new InternalTypeEnumeration(descriptor);
while (internalTypeEnumeration.hasMoreTypes())
{
- String type = internalTypeEnumeration.nextType();
- if (ClassUtil.isInternalArrayType(type))
- {
- type = ClassUtil.internalTypeFromArrayType(type);
- }
+ // Consider the classes referenced by this parameter type.
+ String type = internalTypeEnumeration.nextType();
+ int classCount = new DescriptorClassEnumeration(type).classCount();
- if (ClassUtil.isInternalClassType(type))
+ if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
{
- if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+ // Copy the referenced classes.
+ for (int counter = 0; counter < classCount; counter++)
{
referencedClasses[newReferencedClassIndex++] =
- referencedClasses[referencedClassIndex];
+ referencedClasses[referencedClassIndex++];
}
-
- referencedClassIndex++;
+ }
+ else
+ {
+ // Skip the referenced classes.
+ referencedClassIndex += classCount;
}
parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
@@ -289,17 +290,11 @@ implements MemberVisitor,
// Also look at the return value.
String type = internalTypeEnumeration.returnType();
- if (ClassUtil.isInternalArrayType(type))
- {
- type = ClassUtil.internalTypeFromArrayType(type);
- }
-
- if (ClassUtil.isInternalClassType(type))
+ int count = new DescriptorClassEnumeration(type).classCount();
+ for (int counter = 0; counter < count; counter++)
{
referencedClasses[newReferencedClassIndex++] =
- referencedClasses[referencedClassIndex];
-
- referencedClassIndex++;
+ referencedClasses[referencedClassIndex++];
}
// Clear the unused entries.
@@ -308,5 +303,7 @@ implements MemberVisitor,
referencedClasses[newReferencedClassIndex++] = null;
}
}
+
+ return referencedClasses;
}
}
diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java
index 6e40409..aac86eb 100644
--- a/src/proguard/optimize/Optimizer.java
+++ b/src/proguard/optimize/Optimizer.java
@@ -80,6 +80,7 @@ public class Optimizer
MemberCounter constantFieldCounter = new MemberCounter();
MemberCounter constantMethodCounter = new MemberCounter();
MemberCounter descriptorShrinkCounter = new MemberCounter();
+ MemberCounter initializerFixCounter = new MemberCounter();
MemberCounter parameterShrinkCounter = new MemberCounter();
MemberCounter variableShrinkCounter = new MemberCounter();
ExceptionCounter exceptionCounter = new ExceptionCounter();
@@ -168,6 +169,28 @@ public class Optimizer
new OptimizationInfoMemberFilter(
new ParameterUsageMarker())));
+ // Mark all methods that have side effects.
+ programClassPool.accept(new SideEffectMethodMarker());
+
+// System.out.println("Optimizer.execute: before evaluation simplification");
+// programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
+
+ // Perform partial evaluation for filling out fields, method parameters,
+ // and method return values.
+ programClassPool.classesAccept(new AllMethodVisitor(
+ new AllAttributeVisitor(
+ new PartialEvaluator(new SpecificValueFactory(), new StoringInvocationUnit(), false))));
+
+ // Simplify based on partial evaluation.
+ // Also remove unused parameters from the stack before method invocations,
+ // and make method invocations static when possible, for
+ // MethodDescriptorShrinker, MethodStaticizer.
+ programClassPool.classesAccept(new AllMethodVisitor(
+ new AllAttributeVisitor(
+ new EvaluationSimplifier(
+ new PartialEvaluator(new SpecificValueFactory(), new LoadingInvocationUnit(), false),
+ pushCounter, branchCounter, deletedCounter, addedCounter))));
+
// Shrink the parameters in the method descriptors.
programClassPool.classesAccept(new AllMethodVisitor(
new OptimizationInfoMemberFilter(
@@ -179,32 +202,16 @@ public class Optimizer
new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC,
new MethodStaticizer(staticMethodCounter)))));
- // Remove all unused parameters and variables from the code, for
- // MethodDescriptorShrinker, MethodStaticizer.
- programClassPool.classesAccept(new AllMethodVisitor(
- new AllAttributeVisitor(
- new ParameterShrinker(parameterShrinkCounter))));
-
- // Fix invocations of methods that have become static, for
- // MethodStaticizer.
- programClassPool.classesAccept(new AllMethodVisitor(
- new AllAttributeVisitor(
- new MethodInvocationFixer())));
-
// Fix all references to class members, for MethodDescriptorShrinker.
+ // This operation also updates the stack sizes.
programClassPool.classesAccept(new MemberReferenceFixer());
- // Mark all methods that have side effects.
- programClassPool.accept(new SideEffectMethodMarker());
-
-// System.out.println("Optimizer.execute: before evaluation simplification");
-// programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
-
- // Perform partial evaluation for filling out fields, method parameters,
- // and method return values.
+ // Remove all unused parameters from the byte code, shifting all
+ // remaining variables, for MethodDescriptorShrinker, MethodStaticizer.
+ // This operation also updates the local variable frame sizes.
programClassPool.classesAccept(new AllMethodVisitor(
new AllAttributeVisitor(
- new PartialEvaluator(new SpecificValueFactory(), new UnusedParameterInvocationUnit(new StoringInvocationUnit()), false))));
+ new ParameterShrinker(parameterShrinkCounter))));
// Count the write-only fields, and the constant fields and methods.
programClassPool.classesAccept(new MultiClassVisitor(
@@ -218,15 +225,6 @@ public class Optimizer
new ConstantMemberFilter(constantMethodCounter)),
}));
- // Simplify based on partial evaluation.
- // Also remove unused parameters from the stack before method invocations,
- // for MethodDescriptorShrinker, MethodStaticizer.
- programClassPool.classesAccept(new AllMethodVisitor(
- new AllAttributeVisitor(
- new EvaluationSimplifier(
- new PartialEvaluator(new SpecificValueFactory(), new UnusedParameterInvocationUnit(new LoadingInvocationUnit()), false),
- pushCounter, branchCounter, deletedCounter, addedCounter))));
-
// // Specializing the class member descriptors seems to increase the
// // class file size, on average.
// // Specialize all class member descriptors.
@@ -282,12 +280,12 @@ public class Optimizer
// Inline methods that are only invoked once.
programClassPool.classesAccept(new AllMethodVisitor(
new AllAttributeVisitor(
- new MethodInliner(configuration.allowAccessModification, true, inliningCounter))));
+ new MethodInliner(configuration.microEdition, configuration.allowAccessModification, true, inliningCounter))));
// Inline short methods.
programClassPool.classesAccept(new AllMethodVisitor(
new AllAttributeVisitor(
- new MethodInliner(configuration.allowAccessModification, false, inliningCounter))));
+ new MethodInliner(configuration.microEdition, configuration.allowAccessModification, false, inliningCounter))));
// Mark all class members that can not be made private.
programClassPool.classesAccept(new NonPrivateMemberMarker());
@@ -337,6 +335,24 @@ public class Optimizer
new GotoReturnReplacer( codeAttributeEditor, peepholeCounter),
})))));
+ // Tweak the descriptors of duplicate initializers.
+ DuplicateInitializerFixer duplicateInitializerFixer =
+ new DuplicateInitializerFixer(initializerFixCounter);
+
+ programClassPool.classesAccept(new AllMethodVisitor(
+ duplicateInitializerFixer));
+
+ if (initializerFixCounter.getCount() > 0)
+ {
+ // Fix all invocations of tweaked initializers.
+ programClassPool.classesAccept(new AllMethodVisitor(
+ new AllAttributeVisitor(
+ new DuplicateInitializerInvocationFixer())));
+
+ // Fix all references to tweaked initializers.
+ programClassPool.classesAccept(new MemberReferenceFixer());
+ }
+
// Remove unnecessary exception handlers.
programClassPool.classesAccept(new AllMethodVisitor(
new AllAttributeVisitor(
@@ -366,8 +382,8 @@ public class Optimizer
int writeOnlyFieldCount = writeOnlyFieldCounter .getCount();
int constantFieldCount = constantFieldCounter .getCount();
int constantMethodCount = constantMethodCounter .getCount();
- int descriptorShrinkCount = descriptorShrinkCounter .getCount();
- int parameterShrinkCount = parameterShrinkCounter .getCount();
+ int descriptorShrinkCount = descriptorShrinkCounter .getCount() - initializerFixCounter.getCount();
+ int parameterShrinkCount = parameterShrinkCounter .getCount() - initializerFixCounter.getCount();
int variableShrinkCount = variableShrinkCounter .getCount();
int exceptionCount = exceptionCounter .getCount();
int inliningCount = inliningCounter .getCount();
diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java
index e56941c..8c6ba27 100644
--- a/src/proguard/optimize/ParameterShrinker.java
+++ b/src/proguard/optimize/ParameterShrinker.java
@@ -24,7 +24,7 @@ import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.editor.VariableEditor;
-import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.util.*;
import proguard.classfile.visitor.MemberVisitor;
import proguard.optimize.info.ParameterUsageMarker;
@@ -76,25 +76,31 @@ implements AttributeVisitor
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
- if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
+ // Get the original parameter size that was saved.
+ int oldParameterSize = ParameterUsageMarker.getParameterSize(method);
+
+ // Compute the new parameter size from the shrunk descriptor.
+ int newParameterSize =
+ ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
+
+ if (oldParameterSize > newParameterSize)
{
// Get the total size of the local variable frame.
- int variablesSize = codeAttribute.u2maxLocals;
-
- // The descriptor may have been been shrunk already, so get the
- // original parameter size.
- int parameterSize = ParameterUsageMarker.getParameterSize(method);
+ int maxLocals = codeAttribute.u2maxLocals;
if (DEBUG)
{
System.out.println("ParameterShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
- System.out.println(" parameter size = " + parameterSize);
+ System.out.println(" Old parameter size = " + oldParameterSize);
+ System.out.println(" New parameter size = " + newParameterSize);
+ System.out.println(" Max locals = " + maxLocals);
}
// Delete unused variables from the local variable frame.
- variableEditor.reset(variablesSize);
+ variableEditor.reset(maxLocals);
- for (int parameterIndex = 0; parameterIndex < parameterSize; parameterIndex++)
+ for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++)
{
// Is the variable not required as a parameter?
if (!ParameterUsageMarker.isParameterUsed(method, parameterIndex))
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
index 314279d..a542976 100644
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -21,15 +21,16 @@
package proguard.optimize.evaluation;
import proguard.classfile.*;
-import proguard.classfile.visitor.ClassPrinter;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.evaluation.*;
import proguard.evaluation.value.*;
-import proguard.optimize.info.SideEffectInstructionChecker;
+import proguard.optimize.info.*;
/**
* This AttributeVisitor simplifies the code attributes that it visits, based
@@ -59,10 +60,17 @@ implements AttributeVisitor,
private final PartialEvaluator partialEvaluator;
private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
- private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
+ private final MyProducerMarker producerMarker = new MyProducerMarker();
+ private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer();
+ private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false);
- private boolean[] isNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
- private boolean[] isSimplified = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+ private boolean[][] variablesNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_VARIABLES_SIZE];
+ private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE];
+ private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE];
+ private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+ private boolean[] instructionsSimplified = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+
+ private int maxMarkedOffset;
/**
@@ -166,22 +174,24 @@ implements AttributeVisitor,
codeAttribute.instructionsAccept(clazz, method, this);
+
// Mark all essential instructions that have been encountered as used.
if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
+ maxMarkedOffset = -1;
+
// The invocation of the "super" or "this" <init> method inside a
// constructor is always necessary.
int superInitializationOffset = partialEvaluator.superInitializationOffset();
if (superInitializationOffset != PartialEvaluator.NONE)
{
- if (DEBUG_ANALYSIS) System.out.print(superInitializationOffset+"(super.<init>),");
+ if (DEBUG_ANALYSIS) System.out.print("(super.<init>)");
- isNecessary[superInitializationOffset] = true;
+ markInstruction(superInitializationOffset);
}
// Also mark infinite loops and instructions that cause side effects.
- int offset = 0;
- do
+ for (int offset = 0; offset < codeLength; offset++)
{
if (partialEvaluator.isTraced(offset))
{
@@ -190,10 +200,10 @@ implements AttributeVisitor,
// Mark that the instruction is necessary if it is an infinite loop.
if (instruction.opcode == InstructionConstants.OP_GOTO &&
- partialEvaluator.branchTargets(offset).instructionOffsetValue().instructionOffset(0) == offset)
+ ((BranchInstruction)instruction).branchOffset == 0)
{
- if (DEBUG_ANALYSIS) System.out.print(offset+"(infinite loop),");
- isNecessary[offset] = true;
+ if (DEBUG_ANALYSIS) System.out.print("(infinite loop)");
+ markInstruction(offset);
}
// Mark that the instruction is necessary if it has side effects.
@@ -203,338 +213,183 @@ implements AttributeVisitor,
offset,
instruction))
{
- if (DEBUG_ANALYSIS) System.out.print(offset+",");
- isNecessary[offset] = true;
+ markInstruction(offset);
}
}
-
- offset++;
}
- while (offset < 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.
+ // Globally mark instructions and their produced variables and stack
+ // entries on which necessary instructions depend.
+ // Instead of doing this recursively, we loop across all instructions,
+ // starting at the highest previously unmarked instruction that has
+ // been been marked.
if (DEBUG_ANALYSIS) System.out.println("Usage marking:");
- int lowestNecessaryOffset = codeLength;
- offset = codeLength - 1;
- do
+ while (maxMarkedOffset >= 0)
{
- int nextOffset = offset - 1;
+ int offset = maxMarkedOffset;
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
- {
- lowestNecessaryOffset = offset;
- }
+ maxMarkedOffset = offset - 1;
- // Check if this instruction is a branch origin from a branch that
- // straddles some marked code.
- nextOffset = markStraddlingBranches(offset,
- partialEvaluator.branchTargets(offset),
- true,
- lowestNecessaryOffset,
- nextOffset);
-
- // Mark the producers on which this instruction depends.
- if (isNecessary[offset] &&
- !isSimplified[offset])
+ if (partialEvaluator.isTraced(offset))
{
- nextOffset = markProducers(offset, nextOffset);
- }
+ if (isInstructionNecessary(offset) &&
+ !isInstructionSimplified(offset))
+ {
+ Instruction instruction = InstructionFactory.create(codeAttribute.code,
+ offset);
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
- {
- lowestNecessaryOffset = offset;
- }
+ instruction.accept(clazz, method, codeAttribute, offset, producerMarker);
+ }
- // Check if this instruction is a branch target from a branch that
- // straddles some marked code.
- nextOffset = markStraddlingBranches(offset,
- partialEvaluator.branchOrigins(offset),
- false,
- lowestNecessaryOffset,
- nextOffset);
+ // Check if this instruction is a branch origin from a branch
+ // that straddles some marked code.
+ markStraddlingBranches(offset,
+ partialEvaluator.branchTargets(offset),
+ true);
+
+ // Check if this instruction is a branch target from a branch
+ // that straddles some marked code.
+ markStraddlingBranches(offset,
+ partialEvaluator.branchOrigins(offset),
+ false);
+ }
if (DEBUG_ANALYSIS)
{
- if (nextOffset >= offset)
+ if (maxMarkedOffset > offset)
{
- System.out.println();
+ System.out.println(" -> "+maxMarkedOffset);
}
}
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
- {
- lowestNecessaryOffset = offset;
- }
-
- // Update the index of the instruction to be investigated next.
- offset = nextOffset;
}
- while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
- // Insert pop instructions before unmarked popping instructions,
- // if required to keep the stack consistent.
- if (DEBUG_ANALYSIS) System.out.println("Unmarked pop fixing:");
-
- // Also figure out the offset of the last dup/swap instruction.
- int highestDupOffset = -1;
+ // Mark variable initializations, even if they aren't strictly necessary.
+ // 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: ");
- offset = codeLength - 1;
- do
+ for (int offset = 0; offset < codeLength; offset++)
{
+ // Is it an initialization that hasn't been marked yet, and whose
+ // corresponding variable is necessary?
if (partialEvaluator.isTraced(offset) &&
- !isNecessary[offset] &&
- !isSimplified[offset])
+ !isInstructionNecessary(offset))
{
- Instruction instruction = InstructionFactory.create(codeAttribute.code,
- offset);
-
- // Make sure any non-dup/swap instructions are always consistent
- // at this offset.
- if (!isDupOrSwap(instruction))
- {
- // Make sure any popping instructions are always
- // consistent after this offset.
- fixPopInstruction(clazz,
- codeAttribute,
- offset,
- instruction);
- }
- else if (highestDupOffset < 0)
+ int variableIndex = partialEvaluator.initializedVariable(offset);
+ if (variableIndex >= 0 &&
+ isVariableNecessaryAfter(offset, codeAttribute.u4codeLength, variableIndex))
{
- // Remember the offset of the last dup/swap instruction.
- highestDupOffset = offset;
+ markInstruction(offset);
}
}
-
- offset--;
}
- while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
- // Insert dup instructions where necessary, to keep the stack consistent.
- boolean updated;
- do
- {
- if (DEBUG_ANALYSIS) System.out.println("Dup marking:");
+ // Locally fix instructions, in order to keep the stack consistent.
+ if (DEBUG_ANALYSIS) System.out.println("Stack consistency fixing:");
- // Repeat going over all instructions, as long as dup/swap
- // instructions are updated.
- updated = false;
-
- offset = highestDupOffset;
- while (offset >= 0)
- {
- if (partialEvaluator.isTraced(offset))
- {
- Instruction instruction = InstructionFactory.create(codeAttribute.code,
- offset);
-
- // Make sure any dup/swap instructions are always consistent
- // at this offset.
- if (isDupOrSwap(instruction))
- {
- updated |= fixDupInstruction(clazz,
- codeAttribute,
- offset,
- instruction);
- }
- }
-
- offset--;
- }
- }
- while (updated);
- if (DEBUG_ANALYSIS) System.out.println();
+ maxMarkedOffset = codeLength - 1;
+ while (maxMarkedOffset >= 0)
+ {
+ int offset = maxMarkedOffset;
- // Insert pop instructions after marked pushing instructions,
- // if required to keep the stack consistent.
- if (DEBUG_ANALYSIS) System.out.println("Marked push fixing:");
+ maxMarkedOffset = offset - 1;
- offset = codeLength - 1;
- do
- {
- if (//partialEvaluator.isTraced(offset) &&
- isNecessary[offset] &&
- !isSimplified[offset])
+ if (partialEvaluator.isTraced(offset))
{
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
- // Make sure any non-dup/swap instructions are always consistent
- // at this offset.
- if (!isDupOrSwap(instruction))
- {
- // Make sure any pushing instructions are always
- // consistent after this offset.
- fixPushInstruction(clazz,
- codeAttribute,
- offset,
- instruction);
- }
- }
+ instruction.accept(clazz, method, codeAttribute, offset, stackConsistencyFixer);
- offset--;
+ // Check if this instruction is a branch origin from a branch
+ // that straddles some marked code.
+ markStraddlingBranches(offset,
+ partialEvaluator.branchTargets(offset),
+ true);
+
+ // Check if this instruction is a branch target from a branch
+ // that straddles some marked code.
+ markStraddlingBranches(offset,
+ partialEvaluator.branchOrigins(offset),
+ false);
+ }
}
- while (offset >= 0);
if (DEBUG_ANALYSIS) System.out.println();
- // Mark unmarked pop instructions after dup instructions,
- // if required to keep the stack consistent.
- // This is mainly required to fix "synchronized(C.class)" constructs
- // as compiled by jikes and by the Eclipse compiler:
- // ...
- // dup
- // ifnonnull ...
- // pop
- // ...
- if (DEBUG_ANALYSIS) System.out.println("Pop marking:");
+ // Replace traced but unmarked backward branches by infinite loops.
+ // The virtual machine's verification step is not smart enough to see
+ // the code isn't reachable, and may complain otherwise.
+ // Any clearly unreachable code will still be removed elsewhere.
+ if (DEBUG_ANALYSIS) System.out.println("Infinite loop fixing:");
- offset = codeLength - 1;
- do
+ for (int offset = 0; offset < codeLength; offset++)
{
- if (//partialEvaluator.isTraced(offset) &&
- isNecessary[offset] &&
- !isSimplified[offset] &&
- !codeAttributeEditor.isModified(offset))
+ // Is a traced but unmarked backward branch, without an unmarked
+ // straddling forward branch? Note that this is still a heuristic.
+ if (partialEvaluator.isTraced(offset) &&
+ !isInstructionNecessary(offset) &&
+ isAllSmallerThanOrEqual(partialEvaluator.branchTargets(offset),
+ offset) &&
+ !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset),
+ offset))
{
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
- if (isDupOrSwap(instruction))
- {
- markConsumingPopInstructions(clazz,
- codeAttribute,
- offset,
- instruction);
- }
- }
- offset--;
+ replaceByInfiniteLoop(clazz, offset, instruction);
+ }
}
- 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:");
-
- lowestNecessaryOffset = codeLength;
- offset = codeLength - 1;
+ // Delete all instructions that are not used.
+ int offset = 0;
do
{
- int nextOffset = offset - 1;
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
- {
- lowestNecessaryOffset = offset;
- }
-
- // Check if this instruction is a branch origin from a branch that
- // straddles some marked code.
- nextOffset = markAndSimplifyStraddlingBranches(offset,
- partialEvaluator.branchTargets(offset),
- lowestNecessaryOffset,
- nextOffset);
-
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
+ Instruction instruction = InstructionFactory.create(codeAttribute.code,
+ offset);
+ if (!isInstructionNecessary(offset))
{
- lowestNecessaryOffset = offset;
- }
+ codeAttributeEditor.deleteInstruction(offset);
- // Check if this instruction is a branch target from a branch that
- // straddles some marked code.
- nextOffset = markAndSimplifyStraddlingBranches(partialEvaluator.branchOrigins(offset),
- offset,
- lowestNecessaryOffset,
- nextOffset);
+ codeAttributeEditor.insertBeforeInstruction(offset, null);
+ codeAttributeEditor.replaceInstruction(offset, null);
+ codeAttributeEditor.insertAfterInstruction(offset, null);
- if (DEBUG_ANALYSIS)
- {
- if (nextOffset >= offset)
+ // Visit the instruction, if required.
+ if (extraDeletedInstructionVisitor != null)
{
- System.out.println();
+ instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor);
}
}
- // Update the lowest index of all marked instructions higher up.
- if (isNecessary[offset])
- {
- lowestNecessaryOffset = offset;
- }
-
- // Update the index of the instruction to be investigated next.
- offset = nextOffset;
- }
- while (offset >= 0);
- if (DEBUG_ANALYSIS) System.out.println();
-
-
- // Mark variable initializations, even if they aren't strictly necessary.
- // 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: ");
-
- offset = 0;
- do
- {
- // Is it an initialization that hasn't been marked yet, and whose
- // corresponding variable is used for storage?
- int variableIndex = partialEvaluator.initializedVariable(offset);
- if (variableIndex >= 0 &&
- !isNecessary[offset] &&
- isVariableReferenced(codeAttribute, offset+1, variableIndex))
- {
- if (DEBUG_ANALYSIS) System.out.println(offset +",");
-
- // Figure out what kind of initialization value has to be stored.
- int pushComputationalType = partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).computationalType();
- increaseStackSize(offset, pushComputationalType, false);
- }
-
- offset++;
+ offset += instruction.length(offset);
}
while (offset < codeLength);
- if (DEBUG_ANALYSIS) System.out.println();
+
if (DEBUG_RESULTS)
{
System.out.println("Simplification results:");
+
offset = 0;
do
{
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
- System.out.println((isNecessary[offset] ? " + " : " - ")+instruction.toString(offset));
+ System.out.println((isInstructionNecessary(offset) ? " + " : " - ")+instruction.toString(offset));
if (partialEvaluator.isTraced(offset))
{
- 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 stack: "+stackProducerOffsets);
- }
-
int initializationOffset = partialEvaluator.initializationOffset(offset);
if (initializationOffset != PartialEvaluator.NONE)
{
@@ -547,6 +402,12 @@ implements AttributeVisitor,
System.out.println(" has overall been branching to "+branchTargets);
}
+ boolean deleted = codeAttributeEditor.deleted[offset];
+ if (isInstructionNecessary(offset) && deleted)
+ {
+ System.out.println(" is deleted");
+ }
+
Instruction preInsertion = codeAttributeEditor.preInsertions[offset];
if (preInsertion != null)
{
@@ -564,9 +425,6 @@ implements AttributeVisitor,
{
System.out.println(" is followed by: "+postInsertion);
}
-
- //System.out.println(" Vars: "+vars[offset]);
- //System.out.println(" Stack: "+stacks[offset]);
}
offset += instruction.length(offset);
@@ -574,357 +432,494 @@ implements AttributeVisitor,
while (offset < codeLength);
}
- // Delete all instructions that are not used.
- offset = 0;
- do
- {
- Instruction instruction = InstructionFactory.create(codeAttribute.code,
- offset);
- if (!isNecessary[offset])
- {
- codeAttributeEditor.deleteInstruction(offset);
-
- codeAttributeEditor.insertBeforeInstruction(offset, null);
- codeAttributeEditor.replaceInstruction(offset, null);
- codeAttributeEditor.insertAfterInstruction(offset, null);
-
- // Visit the instruction, if required.
- if (extraDeletedInstructionVisitor != null)
- {
- instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor);
- }
- }
-
- offset += instruction.length(offset);
- }
- while (offset < codeLength);
-
// Apply all accumulated changes to the code.
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
}
/**
- * 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.
+ * This InstructionVisitor marks the producing instructions and produced
+ * variables and stack entries of the instructions that it visits.
+ * Simplified method arguments are ignored.
*/
- private int markProducers(int consumerOffset,
- int nextOffset)
+ private class MyProducerMarker
+ extends SimplifiedVisitor
+ implements InstructionVisitor
{
- if (DEBUG_ANALYSIS) System.out.print(consumerOffset);
+ // Implementations for InstructionVisitor.
- // Mark all instructions whose variable values are used.
- nextOffset = markProducers(partialEvaluator.varProducerOffsets(consumerOffset), nextOffset);
+ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+ {
+ markStackProducers(clazz, offset, instruction);
+ }
- // Mark all instructions whose stack values are used.
- nextOffset = markProducers(partialEvaluator.stackProducerOffsets(consumerOffset), nextOffset);
- // Mark the initializer of the variables and stack entries used by the
- // instruction.
- nextOffset = markProducer(partialEvaluator.initializationOffset(consumerOffset), nextOffset);
+ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+ {
+ switch (simpleInstruction.opcode)
+ {
+ case InstructionConstants.OP_DUP:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 0);
+ break;
+ case InstructionConstants.OP_DUP_X1:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 1);
+ conditionallyMarkStackEntryProducers(offset, 2, 0);
+ break;
+ case InstructionConstants.OP_DUP_X2:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 1);
+ conditionallyMarkStackEntryProducers(offset, 2, 2);
+ conditionallyMarkStackEntryProducers(offset, 3, 0);
+ break;
+ case InstructionConstants.OP_DUP2:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 1);
+ conditionallyMarkStackEntryProducers(offset, 2, 0);
+ conditionallyMarkStackEntryProducers(offset, 3, 1);
+ break;
+ case InstructionConstants.OP_DUP2_X1:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 1);
+ conditionallyMarkStackEntryProducers(offset, 2, 2);
+ conditionallyMarkStackEntryProducers(offset, 3, 0);
+ conditionallyMarkStackEntryProducers(offset, 4, 1);
+ break;
+ case InstructionConstants.OP_DUP2_X2:
+ conditionallyMarkStackEntryProducers(offset, 0, 0);
+ conditionallyMarkStackEntryProducers(offset, 1, 1);
+ conditionallyMarkStackEntryProducers(offset, 2, 2);
+ conditionallyMarkStackEntryProducers(offset, 3, 3);
+ conditionallyMarkStackEntryProducers(offset, 4, 0);
+ conditionallyMarkStackEntryProducers(offset, 5, 1);
+ break;
+ case InstructionConstants.OP_SWAP:
+ conditionallyMarkStackEntryProducers(offset, 0, 1);
+ conditionallyMarkStackEntryProducers(offset, 1, 0);
+ break;
+ default:
+ markStackProducers(clazz, offset, simpleInstruction);
+ break;
+ }
+ }
- if (DEBUG_ANALYSIS) System.out.print(",");
- return nextOffset;
- }
+ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+ {
+ // Is the variable being loaded (or incremented)?
+ if (variableInstruction.opcode < InstructionConstants.OP_ISTORE)
+ {
+ markVariableProducers(offset, variableInstruction.variableIndex);
+ }
+ else
+ {
+ markStackProducers(clazz, offset, variableInstruction);
+ }
+ }
- /**
- * Marks the instructions at the given offsets.
- * @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 markProducers(InstructionOffsetValue producerOffsets,
- int nextOffset)
- {
- if (producerOffsets != null)
+ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
- int offsetCount = producerOffsets.instructionOffsetCount();
- for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
+ // Mark the initializer invocation, if this is a 'new' instruction.
+ if (constantInstruction.opcode == InstructionConstants.OP_NEW)
{
- // Has the other instruction been marked yet?
- int offset = producerOffsets.instructionOffset(offsetIndex);
- nextOffset = markProducer(offset, nextOffset);
+ markInitialization(offset);
}
+
+ markStackProducers(clazz, offset, constantInstruction);
}
- return nextOffset;
+
+ public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+ {
+ // Explicitly mark the produced stack entry of a 'jsr' instruction,
+ // because the consuming 'astore' instruction of the subroutine is
+ // cleared every time it is traced.
+ if (branchInstruction.opcode == InstructionConstants.OP_JSR ||
+ branchInstruction.opcode == InstructionConstants.OP_JSR_W)
+ {
+ markStackEntryAfter(offset, 0);
+ }
+ else
+ {
+ markStackProducers(clazz, offset, branchInstruction);
+ }
+ }
}
/**
- * Marks the instruction at the given offset.
- * @param producerOffset the offsets of the producer 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.
+ * This InstructionVisitor fixes instructions locally, popping any unused
+ * produced stack entries after marked instructions, and popping produced
+ * stack entries and pushing missing stack entries instead of unmarked
+ * instructions.
*/
- private int markProducer(int producerOffset, int nextOffset)
+ private class MyStackConsistencyFixer
+ extends SimplifiedVisitor
+ implements InstructionVisitor
{
- if (producerOffset > PartialEvaluator.AT_METHOD_ENTRY &&
- !isNecessary[producerOffset])
+ // Implementations for InstructionVisitor.
+
+ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
- if (DEBUG_ANALYSIS) System.out.print("["+producerOffset +"]");
+ // Has the instruction been marked?
+ if (isInstructionNecessary(offset))
+ {
+ // Check all stack entries that are popped.
+ // Typical case: a freshly marked variable initialization that
+ // requires some value on the stack.
+ int popCount = instruction.stackPopCount(clazz);
+ if (popCount > 0)
+ {
+ TracedStack tracedStack =
+ partialEvaluator.getStackBefore(offset);
+
+ int top = tracedStack.size() - 1;
- // Mark it.
- isNecessary[producerOffset] = true;
+ int requiredPushCount = 0;
+ for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
+ {
+ // Is the stack entry required by other consumers?
+ if (!isStackSimplifiedBefore(offset, top - stackIndex) &&
+ !isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex))
+ {
+ // Remember to push it.
+ requiredPushCount++;
+ }
+ }
+
+ // Push some necessary stack entries.
+ if (requiredPushCount > 0)
+ {
+ if (DEBUG_ANALYSIS) System.out.print(" Inserting before marked consumer "+instruction.toString(offset));
- // Restart at this instruction if it has a higher offset.
- if (nextOffset < producerOffset)
+ increaseStackSize(offset, tracedStack.getTop(0).computationalType(), false);
+ }
+ }
+
+ // Check all stack entries that are pushed.
+ // Typical case: a return value that wasn't really required and
+ // that should be popped.
+ int pushCount = instruction.stackPushCount(clazz);
+ if (pushCount > 0)
+ {
+ TracedStack tracedStack =
+ partialEvaluator.getStackAfter(offset);
+
+ int top = tracedStack.size() - 1;
+
+ int requiredPopCount = 0;
+ for (int stackIndex = 0; stackIndex < pushCount; stackIndex++)
+ {
+ // Is the stack entry required by consumers?
+ if (!isStackEntryNecessaryAfter(offset, top - stackIndex))
+ {
+ // Remember to pop it.
+ requiredPopCount++;
+ }
+ }
+
+ // Pop the unnecessary stack entries.
+ if (requiredPopCount > 0)
+ {
+ if (DEBUG_ANALYSIS) System.out.print(" Inserting after marked producer "+instruction.toString(offset));
+
+ decreaseStackSize(offset, requiredPopCount, false, false);
+ }
+ }
+ }
+ else
{
- if (DEBUG_ANALYSIS) System.out.print("!");
+ // Check all stack entries that would be popped.
+ // Typical case: a stack value that is required elsewhere and
+ // that still has to be popped.
+ int popCount = instruction.stackPopCount(clazz);
+ if (popCount > 0)
+ {
+ TracedStack tracedStack =
+ partialEvaluator.getStackBefore(offset);
+
+ int top = tracedStack.size() - 1;
- nextOffset = producerOffset;
+ int expectedPopCount = 0;
+ for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
+ {
+ // Is the stack entry required by other consumers?
+ if (isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex))
+ {
+ // Remember to pop it.
+ expectedPopCount++;
+ }
+ }
+
+ // Pop the unnecessary stack entries.
+ if (expectedPopCount > 0)
+ {
+ if (DEBUG_ANALYSIS) System.out.print(" Replacing unmarked consumer "+instruction.toString(offset));
+
+ decreaseStackSize(offset, expectedPopCount, true, true);
+ }
+ }
+
+ // Check all stack entries that would be pushed.
+ // Typical case: never.
+ int pushCount = instruction.stackPushCount(clazz);
+ if (pushCount > 0)
+ {
+ TracedStack tracedStack =
+ partialEvaluator.getStackAfter(offset);
+
+ int top = tracedStack.size() - 1;
+
+ int expectedPushCount = 0;
+ for (int stackIndex = 0; stackIndex < pushCount; stackIndex++)
+ {
+ // Is the stack entry required by consumers?
+ if (isStackEntryNecessaryAfter(offset, top - stackIndex))
+ {
+ // Remember to push it.
+ expectedPushCount++;
+ }
+ }
+
+ // Push some necessary stack entries.
+ if (expectedPushCount > 0)
+ {
+ if (DEBUG_ANALYSIS) System.out.print(" Replacing unmarked producer "+instruction.toString(offset));
+
+ increaseStackSize(offset, tracedStack.getTop(0).computationalType(), true);
+ }
+ }
+ }
+ }
+
+
+ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+ {
+ if (isInstructionNecessary(offset) &&
+ isDupOrSwap(simpleInstruction))
+ {
+ fixDupInstruction(clazz, codeAttribute, offset, simpleInstruction);
+ }
+ else
+ {
+ visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
}
}
+ }
- return nextOffset;
+
+ // Small utility methods.
+
+ /**
+ * Marks the variable and the corresponding producing instructions
+ * of the consumer at the given offset.
+ * @param consumerOffset the offset of the consumer.
+ * @param variableIndex the index of the variable to be marked.
+ */
+ private void markVariableProducers(int consumerOffset,
+ int variableIndex)
+ {
+ TracedVariables tracedVariables =
+ partialEvaluator.getVariablesBefore(consumerOffset);
+
+ // Mark the producer of the loaded value.
+ markVariableProducers(tracedVariables.getProducerValue(variableIndex).instructionOffsetValue(),
+ variableIndex);
}
/**
- * 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 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.
+ * Marks the variable and its producing instructions at the given offsets.
+ * @param producerOffsets the offsets of the producers to be marked.
+ * @param variableIndex the index of the variable to be marked.
*/
- private int markStraddlingBranches(int index,
- InstructionOffsetValue branchOffsets,
- boolean isPointingToTargets,
- int lowestNecessaryOffset,
- int nextOffset)
+ private void markVariableProducers(InstructionOffsetValue producerOffsets,
+ int variableIndex)
{
- if (branchOffsets != null)
+ if (producerOffsets != null)
{
- // Loop over all branch origins.
- int branchCount = branchOffsets.instructionOffsetCount();
- for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+ int offsetCount = producerOffsets.instructionOffsetCount();
+ for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
- // Is the branch straddling any necessary instructions?
- int branch = branchOffsets.instructionOffset(branchIndex);
+ // Make sure the variable and the instruction are marked
+ // at the producing offset.
+ int offset = producerOffsets.instructionOffset(offsetIndex);
- // Is the offset pointing to a branch origin or to a branch target?
- nextOffset = isPointingToTargets ?
- markStraddlingBranch(index, branch, lowestNecessaryOffset, nextOffset) :
- markStraddlingBranch(branch, index, lowestNecessaryOffset, nextOffset);
+ markVariableAfter(offset, variableIndex);
+ markInstruction(offset);
}
}
-
- 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 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.
+ * Marks the stack entries and their producing instructions of the
+ * consumer at the given offset.
+ * @param clazz the containing class.
+ * @param consumerOffset the offset of the consumer.
+ * @param consumer the consumer of the stack entries.
*/
- private int markStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryOffset,
- int nextOffset)
+ private void markStackProducers(Clazz clazz,
+ int consumerOffset,
+ Instruction consumer)
{
- // Has the branch origin been marked yet, and is it straddling the
- // lowest necessary instruction?
- if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryOffset))
+ // Mark the producers of the popped values.
+ int popCount = consumer.stackPopCount(clazz);
+ for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
{
- if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+ markStackEntryProducers(consumerOffset, stackIndex);
+ }
+ }
- // Mark the branch origin.
- isNecessary[branchOrigin] = true;
- // Restart at the branch origin if it has a higher offset.
- if (nextOffset < branchOrigin)
- {
- if (DEBUG_ANALYSIS) System.out.print("!");
+ /**
+ * Marks the stack entry and the corresponding producing instructions
+ * of the consumer at the given offset, if the stack entry of the
+ * consumer is marked.
+ * @param consumerOffset the offset of the consumer.
+ * @param consumerStackIndex the index of the stack entry to be checked
+ * (counting from the top).
+ * @param producerStackIndex the index of the stack entry to be marked
+ * (counting from the top).
+ */
+ private void conditionallyMarkStackEntryProducers(int consumerOffset,
+ int consumerStackIndex,
+ int producerStackIndex)
+ {
+ int top = partialEvaluator.getStackAfter(consumerOffset).size() - 1;
- nextOffset = branchOrigin;
- }
+ if (isStackEntryNecessaryAfter(consumerOffset, top - consumerStackIndex))
+ {
+ markStackEntryProducers(consumerOffset, producerStackIndex);
}
-
- 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 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.
+ * Marks the stack entry and the corresponding producing instructions
+ * of the consumer at the given offset.
+ * @param consumerOffset the offset of the consumer.
+ * @param stackIndex the index of the stack entry to be marked
+ * (counting from the top).
*/
- private int markAndSimplifyStraddlingBranches(int branchOrigin,
- InstructionOffsetValue branchTargets,
- int lowestNecessaryOffset,
- int nextOffset)
+ private void markStackEntryProducers(int consumerOffset,
+ int stackIndex)
{
- 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);
+ TracedStack tracedStack =
+ partialEvaluator.getStackBefore(consumerOffset);
- if (!isStraddlingBranch(branchOrigin,
- branchTarget,
- lowestNecessaryOffset))
- {
- return nextOffset;
- }
- }
+ int stackBottomIndex = tracedStack.size() - 1 - stackIndex;
- nextOffset = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTargets.instructionOffset(0),
- lowestNecessaryOffset,
- nextOffset);
- }
+ if (!isStackSimplifiedBefore(consumerOffset, stackBottomIndex))
+ {
+ markStackEntryProducers(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(),
+ stackBottomIndex);
}
-
- 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 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.
+ * Marks the stack entry and its producing instructions at the given
+ * offsets.
+ * @param producerOffsets the offsets of the producers to be marked.
+ * @param stackIndex the index of the stack entry to be marked
+ * (counting from the bottom).
*/
- private int markAndSimplifyStraddlingBranches(InstructionOffsetValue branchOrigins,
- int branchTarget,
- int lowestNecessaryOffset,
- int nextOffset)
+ private void markStackEntryProducers(InstructionOffsetValue producerOffsets,
+ int stackIndex)
{
- if (branchOrigins != null)
+ if (producerOffsets != null)
{
- // Loop over all branch origins.
- int branchCount = branchOrigins.instructionOffsetCount();
- for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+ int offsetCount = producerOffsets.instructionOffsetCount();
+ for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
- // Is the branch straddling any necessary instructions?
- int branchOrigin = branchOrigins.instructionOffset(branchIndex);
+ // Make sure the stack entry and the instruction are marked
+ // at the producing offset.
+ int offset = producerOffsets.instructionOffset(offsetIndex);
- nextOffset = markAndSimplifyStraddlingBranch(branchOrigin,
- branchTarget,
- lowestNecessaryOffset,
- nextOffset);
+ markStackEntryAfter(offset, stackIndex);
+ markInstruction(offset);
}
}
-
- 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 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.
+ * Marks the stack entry and its initializing instruction
+ * ('invokespecial *.<init>') for the given 'new' instruction offset.
+ * @param newInstructionOffset the offset of the 'new' instruction.
*/
- private int markAndSimplifyStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryOffset,
- int nextOffset)
+ private void markInitialization(int newInstructionOffset)
{
- // Has the branch origin been marked yet, and is it straddling the
- // lowest necessary instruction?
- if (!isNecessary[branchOrigin] &&
- isStraddlingBranch(branchOrigin, branchTarget, lowestNecessaryOffset))
- {
- if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+ int initializationOffset =
+ partialEvaluator.initializationOffset(newInstructionOffset);
- // Mark the branch origin.
- isNecessary[branchOrigin] = true;
+ TracedStack tracedStack =
+ partialEvaluator.getStackAfter(newInstructionOffset);
- // Replace the branch instruction by a simple branch instrucion.
- Instruction replacementInstruction =
- new BranchInstruction(InstructionConstants.OP_GOTO_W,
- branchTarget - branchOrigin).shrink();
+ markStackEntryAfter(initializationOffset, tracedStack.size() - 1);
+ markInstruction(initializationOffset);
+ }
- codeAttributeEditor.replaceInstruction(branchOrigin,
- replacementInstruction);
- // Restart at the branch origin if it has a higher offset.
- if (nextOffset < branchOrigin)
+ /**
+ * Marks the branch instructions of straddling branches, if they straddle
+ * some code that has been marked.
+ * @param instructionOffset 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.
+ */
+ private void markStraddlingBranches(int instructionOffset,
+ InstructionOffsetValue branchOffsets,
+ boolean isPointingToTargets)
+ {
+ if (branchOffsets != null)
+ {
+ // Loop over all branch offsets.
+ int branchCount = branchOffsets.instructionOffsetCount();
+ for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
{
- if (DEBUG_ANALYSIS) System.out.print("!");
+ // Is the branch straddling forward any necessary instructions?
+ int branchOffset = branchOffsets.instructionOffset(branchIndex);
- nextOffset = branchOrigin;
+ // Is the offset pointing to a branch origin or to a branch target?
+ if (isPointingToTargets)
+ {
+ markStraddlingBranch(instructionOffset,
+ branchOffset,
+ instructionOffset,
+ branchOffset);
+ }
+ else
+ {
+ markStraddlingBranch(instructionOffset,
+ branchOffset,
+ branchOffset,
+ instructionOffset);
+ }
}
}
-
- 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 lowestNecessaryOffset the lowest offset of all instructions marked
- * so far.
- */
- private boolean isStraddlingBranch(int branchOrigin,
- int branchTarget,
- int lowestNecessaryOffset)
+ private void markStraddlingBranch(int instructionOffsetStart,
+ int instructionOffsetEnd,
+ int branchOrigin,
+ int branchTarget)
{
- return branchOrigin <= lowestNecessaryOffset ^
- branchTarget <= lowestNecessaryOffset;
+ if (!isInstructionNecessary(branchOrigin) &&
+ isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd))
+ {
+ if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+
+ // Mark the branch instruction.
+ markInstruction(branchOrigin);
+ }
}
@@ -935,31 +930,29 @@ implements AttributeVisitor,
* @param codeAttribute the code that is being checked.
* @param dupOffset the offset of the dup/swap instruction.
* @param instruction the dup/swap instruction.
- * @return whether the instruction is updated.
*/
- private boolean fixDupInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int dupOffset,
- Instruction instruction)
+ private void fixDupInstruction(Clazz clazz,
+ CodeAttribute codeAttribute,
+ int dupOffset,
+ Instruction instruction)
{
- byte oldOpcode = instruction.opcode;
- byte newOpcode = 0;
- boolean present = false;
+ int top = partialEvaluator.getStackAfter(dupOffset).size() - 1;
+
+ byte oldOpcode = instruction.opcode;
+ byte newOpcode = 0;
// Simplify the popping instruction if possible.
switch (oldOpcode)
{
case InstructionConstants.OP_DUP:
{
- boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, 0, false);
- boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, 1, false);
+ boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0);
+ boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 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)
@@ -971,16 +964,14 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_DUP_X1:
{
- boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, 0, false);
- boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, 1, false);
- boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, 2, false);
+ boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0);
+ boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1);
+ boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2);
// Should either the original element or the copy be present?
if (stackEntryPresent0 ||
stackEntryPresent2)
{
- present = true;
-
// Should the copy be present?
if (stackEntryPresent2)
{
@@ -1004,17 +995,15 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_DUP_X2:
{
- boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, 0, false);
- boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, 1, false);
- boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, 2, false);
- boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, 3, false);
+ boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0);
+ boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1);
+ boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2);
+ boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3);
// Should either the original element or the copy be present?
if (stackEntryPresent0 ||
stackEntryPresent3)
{
- present = true;
-
// Should the copy be present?
if (stackEntryPresent3)
{
@@ -1035,7 +1024,7 @@ implements AttributeVisitor,
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 at ["+dupOffset +"]");
+ throw new UnsupportedOperationException("Can't handle dup_x2 instruction moving original element across two elements at ["+dupOffset +"]");
}
}
}
@@ -1043,15 +1032,13 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_DUP2:
{
- boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, 0, 1, false);
- boolean stackEntriesPresent23 = isStackEntriesNecessaryAfter(dupOffset, 2, 3, false);
+ boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1);
+ boolean stackEntriesPresent23 = isStackEntriesNecessaryAfter(dupOffset, top - 2, top - 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)
@@ -1063,16 +1050,14 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_DUP2_X1:
{
- boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, 0, 1, false);
- boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, 2, false);
- boolean stackEntriesPresent34 = isStackEntriesNecessaryAfter(dupOffset, 3, 4, false);
+ boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1);
+ boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2);
+ boolean stackEntriesPresent34 = isStackEntriesNecessaryAfter(dupOffset, top - 3, top - 4);
// Should either the original element or the copy be present?
if (stackEntriesPresent01 ||
stackEntriesPresent34)
{
- present = true;
-
// Should the copy be present?
if (stackEntriesPresent34)
{
@@ -1087,7 +1072,7 @@ implements AttributeVisitor,
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 at ["+dupOffset +"]");
+ throw new UnsupportedOperationException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]");
}
}
}
@@ -1095,17 +1080,15 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_DUP2_X2:
{
- boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, 0, 1, false);
- boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, 2, false);
- boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, 3, false);
- boolean stackEntriesPresent45 = isStackEntriesNecessaryAfter(dupOffset, 4, 5, false);
+ boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1);
+ boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2);
+ boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3);
+ boolean stackEntriesPresent45 = isStackEntriesNecessaryAfter(dupOffset, top - 4, top - 5);
// Should either the original element or the copy be present?
if (stackEntriesPresent01 ||
stackEntriesPresent45)
{
- present = true;
-
// Should the copy be present?
if (stackEntriesPresent45)
{
@@ -1121,7 +1104,7 @@ implements AttributeVisitor,
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 at ["+dupOffset +"]");
+ throw new UnsupportedOperationException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]");
}
}
}
@@ -1129,15 +1112,13 @@ implements AttributeVisitor,
}
case InstructionConstants.OP_SWAP:
{
- boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, 0, false);
- boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, 1, false);
+ boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0);
+ boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1);
// Will either element be present?
if (stackEntryPresent0 ||
stackEntryPresent1)
{
- present = true;
-
// Will both elements be present?
if (stackEntryPresent0 &&
stackEntryPresent1)
@@ -1149,166 +1130,33 @@ implements AttributeVisitor,
}
}
- boolean updated = false;
-
- // Actually replace the instruction with the new opcode, if any.
- if (present)
+ if (newOpcode == 0)
{
- // If this is the first pass, note that the instruction is updated.
- if (!isNecessary[dupOffset])
- {
- updated = true;
-
- // Mark that the instruction is necessary.
- isNecessary[dupOffset] = true;
- }
-
- if (newOpcode == 0)
- {
- // Delete the instruction.
- codeAttributeEditor.deleteInstruction(dupOffset);
-
- if (DEBUG_ANALYSIS) System.out.println(" Marking but deleting instruction "+instruction.toString(dupOffset));
- }
- else if (newOpcode == oldOpcode)
- {
- // Leave the instruction unchanged.
- codeAttributeEditor.undeleteInstruction(dupOffset);
+ // Delete the instruction.
+ codeAttributeEditor.deleteInstruction(dupOffset);
- if (DEBUG_ANALYSIS) System.out.println(" Marking unchanged instruction "+instruction.toString(dupOffset));
- }
- else
+ if (extraDeletedInstructionVisitor != null)
{
- // Replace the instruction.
- Instruction replacementInstruction = new SimpleInstruction(newOpcode);
- codeAttributeEditor.replaceInstruction(dupOffset,
- replacementInstruction);
-
- if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
+ extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null);
}
- }
- return updated;
- }
-
-
- /**
- * Pops the stack after the given necessary instruction, if it pushes an
- * entry that is not used at all.
- * @param clazz the class that is being checked.
- * @param codeAttribute the code that is being checked.
- * @param producerOffset the offset of the producer instruction.
- * @param instruction the producer instruction.
- */
- private void fixPushInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int producerOffset,
- Instruction instruction)
- {
- int pushCount = instruction.stackPushCount(clazz);
- if (pushCount > 0)
- {
- boolean stackEntryPresent0 = isStackEntryNecessaryAfter(producerOffset, 0, false);
-
- if (!stackEntryPresent0)
- {
- if (instruction.opcode != InstructionConstants.OP_JSR &&
- instruction.opcode != InstructionConstants.OP_JSR_W)
- {
- // Make sure the pushed value is popped again,
- // right after this instruction.
- decreaseStackSize(producerOffset, pushCount, false, false);
-
- if (DEBUG_ANALYSIS) System.out.println(" Popping unused value right after "+instruction.toString(producerOffset));
- }
- }
+ if (DEBUG_ANALYSIS) System.out.println(" Marking but deleting instruction "+instruction.toString(dupOffset));
}
- }
-
-
- /**
- * Pops the stack before the given unnecessary instruction, if the stack
- * contains an entry that it would have popped.
- * @param clazz the class that is being checked.
- * @param codeAttribute the code that is being checked.
- * @param consumerOffset the offset of the consumer instruction.
- * @param instruction the consumer instruction.
- */
- private void fixPopInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int consumerOffset,
- Instruction instruction)
- {
- int popCount = instruction.stackPopCount(clazz);
- if (popCount > 0)
+ else if (newOpcode == oldOpcode)
{
- if (partialEvaluator.stackProducerOffsets(consumerOffset).contains(PartialEvaluator.AT_CATCH_ENTRY) ||
- (isStackEntryNecessaryBefore(consumerOffset, 0, false) &&
- !isStackEntryNecessaryBefore(consumerOffset, 0, true)))
- {
- // Is the consumer a simple pop or pop2 instruction?
- if (isPop(instruction))
- {
- if (DEBUG_ANALYSIS) System.out.println(" Popping value again at "+instruction.toString(consumerOffset));
-
- // Simply mark the pop or pop2 instruction.
- isNecessary[consumerOffset] = true;
- }
- else
- {
- if (DEBUG_ANALYSIS) System.out.println(" Popping value instead of "+instruction.toString(consumerOffset));
+ // Leave the instruction unchanged.
+ codeAttributeEditor.undeleteInstruction(dupOffset);
- // Make sure the pushed value is popped again,
- // right before this instruction.
- decreaseStackSize(consumerOffset, popCount, true, true);
- }
- }
+ if (DEBUG_ANALYSIS) System.out.println(" Marking unchanged instruction "+instruction.toString(dupOffset));
}
- }
-
-
- /**
- * Marks pop and pop2 instructions that pop stack entries produced by the
- * given instruction.
- * @param clazz the class that is being checked.
- * @param codeAttribute the code that is being checked.
- * @param producerOffset the offset of the producer instruction.
- * @param producerInstruction the producer instruction.
- */
- private void markConsumingPopInstructions(Clazz clazz,
- CodeAttribute codeAttribute,
- int producerOffset,
- Instruction producerInstruction)
- {
- // Loop over all pushed stack entries.
- int pushCount = producerInstruction.stackPushCount(clazz);
- for (int stackIndex = 0; stackIndex < pushCount; stackIndex++)
+ else
{
- // Loop over all consumers of this entry.
- InstructionOffsetValue consumerOffsets =
- partialEvaluator.getStackAfter(producerOffset).getTopConsumerValue(stackIndex).instructionOffsetValue();
-
- int consumerCount = consumerOffsets.instructionOffsetCount();
- for (int consumerIndex = 0; consumerIndex < consumerCount; consumerIndex++)
- {
- int consumerOffset = consumerOffsets.instructionOffset(consumerIndex);
-
- // Is the consumer not necessary yet?
- if (!isNecessary[consumerOffset])
- {
- Instruction consumerInstruction =
- InstructionFactory.create(codeAttribute.code, consumerOffset);
-
- // Is the consumer a simple pop or pop2 instruction?
- if (isPop(consumerInstruction))
- {
- // Mark it.
- isNecessary[consumerOffset] = true;
+ // Replace the instruction.
+ Instruction replacementInstruction = new SimpleInstruction(newOpcode);
+ codeAttributeEditor.replaceInstruction(dupOffset,
+ replacementInstruction);
- if (DEBUG_ANALYSIS) System.out.println(" Marking "+consumerInstruction.toString(consumerOffset)+" due to "+producerInstruction.toString(producerOffset));
- }
- }
- }
+ if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
}
}
@@ -1327,30 +1175,32 @@ implements AttributeVisitor,
boolean delete)
{
// Mark this instruction.
- isNecessary[offset] = true;
+ markInstruction(offset);
// Create a simple push instrucion.
-
Instruction replacementInstruction =
new SimpleInstruction(pushOpcode(computationalType));
- // Insert the pop or push instruction.
- codeAttributeEditor.insertBeforeInstruction(offset,
- replacementInstruction);
-
- if (extraAddedInstructionVisitor != null)
- {
- replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
- }
+ if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
// Delete the original instruction if necessary.
if (delete)
{
- codeAttributeEditor.deleteInstruction(offset);
+ if (DEBUG_ANALYSIS) System.out.println(" Deleting original instruction at "+offset);
- if (extraDeletedInstructionVisitor != null)
+ // Replace the push instruction.
+ codeAttributeEditor.replaceInstruction(offset,
+ replacementInstruction);
+ }
+ else
+ {
+ // Insert the push instruction.
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ replacementInstruction);
+
+ if (extraAddedInstructionVisitor != null)
{
- extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null);
+ replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
}
}
}
@@ -1392,7 +1242,7 @@ implements AttributeVisitor,
boolean delete)
{
// Mark this instruction.
- isNecessary[offset] = true;
+ markInstruction(offset);
boolean after = !before;
@@ -1410,6 +1260,8 @@ implements AttributeVisitor,
Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+ if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
+
// Insert the pop instruction.
codeAttributeEditor.replaceInstruction(offset,
replacementInstruction);
@@ -1433,6 +1285,8 @@ implements AttributeVisitor,
Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+ if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
+
// Insert the pop instruction.
codeAttributeEditor.insertBeforeInstruction(offset,
replacementInstruction);
@@ -1457,6 +1311,8 @@ implements AttributeVisitor,
Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+ if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
+
// Insert the pop instruction.
codeAttributeEditor.insertAfterInstruction(offset,
replacementInstruction);
@@ -1471,7 +1327,7 @@ implements AttributeVisitor,
if (remainingPopCount > 0)
{
- throw new IllegalArgumentException("Unsupported stack size reduction ["+popCount+"]");
+ throw new UnsupportedOperationException("Unsupported stack size reduction ["+popCount+"]");
}
}
@@ -1506,7 +1362,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_I2B:
case InstructionConstants.OP_I2C:
case InstructionConstants.OP_I2S:
- replaceIntegerPushInstruction(offset);
+ replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
break;
case InstructionConstants.OP_LALOAD:
@@ -1525,7 +1381,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_I2L:
case InstructionConstants.OP_F2L:
case InstructionConstants.OP_D2L:
- replaceLongPushInstruction(offset);
+ replaceLongPushInstruction(clazz, offset, simpleInstruction);
break;
case InstructionConstants.OP_FALOAD:
@@ -1538,7 +1394,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_I2F:
case InstructionConstants.OP_L2F:
case InstructionConstants.OP_D2F:
- replaceFloatPushInstruction(offset);
+ replaceFloatPushInstruction(clazz, offset, simpleInstruction);
break;
case InstructionConstants.OP_DALOAD:
@@ -1551,11 +1407,11 @@ implements AttributeVisitor,
case InstructionConstants.OP_I2D:
case InstructionConstants.OP_L2D:
case InstructionConstants.OP_F2D:
- replaceDoublePushInstruction(offset);
+ replaceDoublePushInstruction(clazz, offset, simpleInstruction);
break;
case InstructionConstants.OP_AALOAD:
- replaceReferencePushInstruction(offset);
+ replaceReferencePushInstruction(clazz, offset, simpleInstruction );
break;
}
}
@@ -1573,7 +1429,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_ILOAD_1:
case InstructionConstants.OP_ILOAD_2:
case InstructionConstants.OP_ILOAD_3:
- replaceIntegerPushInstruction(offset);
+ replaceIntegerPushInstruction(clazz, offset, variableInstruction);
break;
case InstructionConstants.OP_LLOAD:
@@ -1581,7 +1437,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_LLOAD_1:
case InstructionConstants.OP_LLOAD_2:
case InstructionConstants.OP_LLOAD_3:
- replaceLongPushInstruction(offset);
+ replaceLongPushInstruction(clazz, offset, variableInstruction);
break;
case InstructionConstants.OP_FLOAD:
@@ -1589,7 +1445,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_FLOAD_1:
case InstructionConstants.OP_FLOAD_2:
case InstructionConstants.OP_FLOAD_3:
- replaceFloatPushInstruction(offset);
+ replaceFloatPushInstruction(clazz, offset, variableInstruction);
break;
case InstructionConstants.OP_DLOAD:
@@ -1597,7 +1453,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_DLOAD_1:
case InstructionConstants.OP_DLOAD_2:
case InstructionConstants.OP_DLOAD_3:
- replaceDoublePushInstruction(offset);
+ replaceDoublePushInstruction(clazz, offset, variableInstruction);
break;
case InstructionConstants.OP_ALOAD:
@@ -1605,7 +1461,7 @@ implements AttributeVisitor,
case InstructionConstants.OP_ALOAD_1:
case InstructionConstants.OP_ALOAD_2:
case InstructionConstants.OP_ALOAD_3:
- replaceReferencePushInstruction(offset);
+ replaceReferencePushInstruction(clazz, offset, variableInstruction);
break;
}
@@ -1621,13 +1477,18 @@ implements AttributeVisitor,
{
case InstructionConstants.OP_GETSTATIC:
case InstructionConstants.OP_GETFIELD:
- replaceAnyPushInstruction(offset);
+ replaceAnyPushInstruction(clazz, offset, constantInstruction);
break;
case InstructionConstants.OP_INVOKEVIRTUAL:
case InstructionConstants.OP_INVOKESPECIAL:
case InstructionConstants.OP_INVOKESTATIC:
case InstructionConstants.OP_INVOKEINTERFACE:
+ clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
+ new ReferencedMemberVisitor(
+ new MyUnusedParameterSimplifier(offset,
+ constantInstruction)));
+
if (constantInstruction.stackPushCount(clazz) > 0 &&
!sideEffectInstructionChecker.hasSideEffects(clazz,
method,
@@ -1635,12 +1496,13 @@ implements AttributeVisitor,
offset,
constantInstruction))
{
- replaceAnyPushInstruction(offset);
+ replaceAnyPushInstruction(clazz, offset, constantInstruction);
}
+
break;
case InstructionConstants.OP_CHECKCAST:
- replaceReferencePushInstruction(offset);
+ replaceReferencePushInstruction(clazz, offset, constantInstruction);
break;
}
}
@@ -1660,11 +1522,11 @@ implements AttributeVisitor,
case InstructionConstants.OP_JSR:
case InstructionConstants.OP_JSR_W:
- replaceJsrInstruction(offset, branchInstruction);
+ replaceJsrInstruction(clazz, offset, branchInstruction);
break;
default:
- replaceBranchInstruction(offset, branchInstruction);
+ replaceBranchInstruction(clazz, offset, branchInstruction);
break;
}
}
@@ -1676,12 +1538,69 @@ implements AttributeVisitor,
if (partialEvaluator.isTraced(offset))
{
// First try to simplify it to a simple branch.
- replaceBranchInstruction(offset, switchInstruction);
+ replaceBranchInstruction(clazz, offset, switchInstruction);
// Otherwise make sure all branch targets are valid.
- if (!isSimplified[offset])
+ if (!isInstructionSimplified(offset))
+ {
+ replaceSwitchInstruction(clazz, offset, switchInstruction);
+ }
+ }
+ }
+
+
+ private class MyUnusedParameterSimplifier
+ extends SimplifiedVisitor
+ implements MemberVisitor
+ {
+ private int invocationOffset;
+ private ConstantInstruction invocationInstruction;
+
+
+ private MyUnusedParameterSimplifier(int invocationOffset,
+ ConstantInstruction invocationInstruction)
+ {
+ this.invocationOffset = invocationOffset;
+ this.invocationInstruction = invocationInstruction;
+ }
+
+
+ // Implementations for MemberVisitor.
+
+ public void visitAnyMember(Clazz clazz, Member member) {}
+
+
+ public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+ {
+ // Get the total size of the parameters.
+ int parameterSize = ParameterUsageMarker.getParameterSize(programMethod);
+
+ // Make the method invocation static, if possible.
+ if (!ParameterUsageMarker.isParameterUsed(programMethod, 0))
+ {
+ replaceInvocationInstruction(programClass,
+ invocationOffset,
+ invocationInstruction);
+ }
+
+ // Remove unused parameters.
+ for (int index = 0; index < parameterSize; index++)
{
- replaceSwitchInstruction(offset, switchInstruction);
+ if (!ParameterUsageMarker.isParameterUsed(programMethod, index))
+ {
+ TracedStack stack =
+ partialEvaluator.getStackBefore(invocationOffset);
+
+ int stackIndex = stack.size() - parameterSize + index;
+
+ if (DEBUG)
+ {
+ System.out.println(" ["+invocationOffset+"] Ignoring parameter #"+index+" of "+programClass.getName()+"."+programMethod.getDescriptor(programClass)+"] (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])");
+ System.out.println(" Full stack: "+stack);
+ }
+
+ markStackSimplificationBefore(invocationOffset, stackIndex);
+ }
}
}
}
@@ -1693,7 +1612,9 @@ implements AttributeVisitor,
* Replaces the push instruction at the given offset by a simpler push
* instruction, if possible.
*/
- private void replaceAnyPushInstruction(int offset)
+ private void replaceAnyPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
@@ -1701,19 +1622,19 @@ implements AttributeVisitor,
switch (pushedValue.computationalType())
{
case Value.TYPE_INTEGER:
- replaceIntegerPushInstruction(offset);
+ replaceIntegerPushInstruction(clazz, offset, instruction);
break;
case Value.TYPE_LONG:
- replaceLongPushInstruction(offset);
+ replaceLongPushInstruction(clazz, offset, instruction);
break;
case Value.TYPE_FLOAT:
- replaceFloatPushInstruction(offset);
+ replaceFloatPushInstruction(clazz, offset, instruction);
break;
case Value.TYPE_DOUBLE:
- replaceDoublePushInstruction(offset);
+ replaceDoublePushInstruction(clazz, offset, instruction);
break;
case Value.TYPE_REFERENCE:
- replaceReferencePushInstruction(offset);
+ replaceReferencePushInstruction(clazz, offset, instruction);
break;
}
}
@@ -1724,7 +1645,9 @@ implements AttributeVisitor,
* Replaces the integer pushing instruction at the given offset by a simpler
* push instruction, if possible.
*/
- private void replaceIntegerPushInstruction(int offset)
+ private void replaceIntegerPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
@@ -1732,7 +1655,9 @@ implements AttributeVisitor,
int value = pushedValue.integerValue().value();
if (value << 16 >> 16 == value)
{
- replacePushInstruction(offset,
+ replacePushInstruction(clazz,
+ offset,
+ instruction,
InstructionConstants.OP_SIPUSH,
value);
}
@@ -1744,7 +1669,9 @@ implements AttributeVisitor,
* Replaces the long pushing instruction at the given offset by a simpler
* push instruction, if possible.
*/
- private void replaceLongPushInstruction(int offset)
+ private void replaceLongPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
@@ -1753,7 +1680,9 @@ implements AttributeVisitor,
if (value == 0L ||
value == 1L)
{
- replacePushInstruction(offset,
+ replacePushInstruction(clazz,
+ offset,
+ instruction,
InstructionConstants.OP_LCONST_0,
(int)value);
}
@@ -1765,7 +1694,9 @@ implements AttributeVisitor,
* Replaces the float pushing instruction at the given offset by a simpler
* push instruction, if possible.
*/
- private void replaceFloatPushInstruction(int offset)
+ private void replaceFloatPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
@@ -1775,7 +1706,9 @@ implements AttributeVisitor,
value == 1f ||
value == 2f)
{
- replacePushInstruction(offset,
+ replacePushInstruction(clazz,
+ offset,
+ instruction,
InstructionConstants.OP_FCONST_0,
(int)value);
}
@@ -1787,7 +1720,9 @@ implements AttributeVisitor,
* Replaces the double pushing instruction at the given offset by a simpler
* push instruction, if possible.
*/
- private void replaceDoublePushInstruction(int offset)
+ private void replaceDoublePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
@@ -1796,7 +1731,9 @@ implements AttributeVisitor,
if (value == 0.0 ||
value == 1.0)
{
- replacePushInstruction(offset,
+ replacePushInstruction(clazz,
+ offset,
+ instruction,
InstructionConstants.OP_DCONST_0,
(int)value);
}
@@ -1808,13 +1745,17 @@ implements AttributeVisitor,
* Replaces the reference pushing instruction at the given offset by a
* simpler push instruction, if possible.
*/
- private void replaceReferencePushInstruction(int offset)
+ private void replaceReferencePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isSpecific())
{
// A reference value can only be specific if it is null.
- replacePushInstruction(offset,
+ replacePushInstruction(clazz,
+ offset,
+ instruction,
InstructionConstants.OP_ACONST_NULL,
0);
}
@@ -1824,18 +1765,22 @@ implements AttributeVisitor,
/**
* Replaces the instruction at a given offset by a given push instruction.
*/
- private void replacePushInstruction(int offset, byte opcode, int value)
+ private void replacePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ byte replacementOpcode,
+ int value)
{
// Remember the replacement instruction.
Instruction replacementInstruction =
- new SimpleInstruction(opcode, value).shrink();
+ new SimpleInstruction(replacementOpcode, value).shrink();
if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
// Mark that the instruction has been simplified.
- isSimplified[offset] = true;
+ markSimplification(clazz, offset, instruction);
// Visit the instruction, if required.
if (extraPushInstructionVisitor != null)
@@ -1848,23 +1793,54 @@ implements AttributeVisitor,
/**
+ * Replaces the instruction at a given offset by a static invocation.
+ */
+ private void replaceInvocationInstruction(Clazz clazz,
+ int offset,
+ ConstantInstruction constantInstruction)
+ {
+ // Remember the replacement instruction.
+ Instruction replacementInstruction =
+ new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC,
+ constantInstruction.constantIndex).shrink();
+
+ if (DEBUG_ANALYSIS) System.out.println(" Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
+
+ codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Mark that the instruction has been simplified.
+ //markSimplification(clazz, offset, constantInstruction);
+
+ // 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);
+ //}
+ }
+
+
+ /**
* Replaces the given 'jsr' instruction by a simpler branch instruction,
* if possible.
*/
- private void replaceJsrInstruction(int offset, BranchInstruction branchInstruction)
+ private void replaceJsrInstruction(Clazz clazz,
+ int offset,
+ BranchInstruction branchInstruction)
{
// Is the subroutine ever returning?
if (!partialEvaluator.isSubroutineReturning(offset + branchInstruction.branchOffset))
{
// All 'jsr' instructions to this subroutine can be replaced
// by unconditional branch instructions.
- replaceBranchInstruction(offset, branchInstruction);
+ replaceBranchInstruction(clazz, offset, branchInstruction);
}
else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
{
// We have to make sure the instruction after this 'jsr'
// instruction is valid, even if it is never reached.
- insertInfiniteLoop(offset + branchInstruction.length(offset));
+ replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction);
}
}
@@ -1873,7 +1849,9 @@ implements AttributeVisitor,
* Deletes the given branch instruction, or replaces it by a simpler branch
* instruction, if possible.
*/
- private void replaceBranchInstruction(int offset, Instruction instruction)
+ private void replaceBranchInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
@@ -1885,10 +1863,7 @@ implements AttributeVisitor,
int branchOffset = branchTargets.instructionOffset(0) - offset;
if (branchOffset == instruction.length(offset))
{
- if (DEBUG_ANALYSIS) System.out.println(" Deleting zero branch instruction at ["+offset+"]");
-
- // Delete the branch instruction.
- codeAttributeEditor.deleteInstruction(offset);
+ if (DEBUG_ANALYSIS) System.out.println(" Ignoring zero branch instruction at ["+offset+"]");
}
else
{
@@ -1903,13 +1878,11 @@ implements AttributeVisitor,
replacementInstruction);
// Mark that the instruction has been simplified.
- isSimplified[offset] = true;
+ markSimplification(clazz, offset, instruction);
// 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);
}
}
@@ -1920,10 +1893,14 @@ implements AttributeVisitor,
/**
* Makes sure all branch targets of the given switch instruction are valid.
*/
- private void replaceSwitchInstruction(int offset, SwitchInstruction switchInstruction)
+ private void replaceSwitchInstruction(Clazz clazz,
+ int offset,
+ SwitchInstruction switchInstruction)
{
// Get the actual branch targets.
InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+
+ // Get an offset that can serve as a valid default offset.
int defaultOffset =
branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) -
offset;
@@ -1973,22 +1950,24 @@ implements AttributeVisitor,
/**
- * Puts an infinite loop at the given offset.
+ * Replaces the given instruction by an infinite loop.
*/
- private void insertInfiniteLoop(int offset)
+ private void replaceByInfiniteLoop(Clazz clazz,
+ int offset,
+ Instruction instruction)
{
- // Replace the branch instruction by a simple branch instruction.
+ if (DEBUG_ANALYSIS) System.out.println(" Inserting infinite loop at ["+offset+"]");
+
+ // Replace the instruction by an infinite loop.
Instruction replacementInstruction =
new BranchInstruction(InstructionConstants.OP_GOTO, 0);
- if (DEBUG_ANALYSIS) System.out.println(" Inserting infinite loop at unreachable instruction at ["+offset+"]");
-
codeAttributeEditor.replaceInstruction(offset,
replacementInstruction);
- // Mark that the instruction has been simplified.
- isNecessary[offset] = true;
- isSimplified[offset] = true;
+ // Mark the instruction.
+ markInstruction(offset);
+ markSimplification(clazz, offset, instruction);
}
@@ -1996,7 +1975,7 @@ implements AttributeVisitor,
/**
* Returns whether the given instruction is a dup or swap instruction
- * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x1, swap).
+ * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap).
*/
private boolean isDupOrSwap(Instruction instruction)
{
@@ -2017,52 +1996,157 @@ implements AttributeVisitor,
/**
- * Initializes the necessary data structure.
+ * Returns whether any traced but unnecessary instruction between the two
+ * given offsets is branching over the second given offset.
*/
- private void initializeNecessary(CodeAttribute codeAttribute)
+ private boolean isAnyUnnecessaryInstructionBranchingOver(int instructionOffset1,
+ int instructionOffset2)
{
- int codeLength = codeAttribute.u4codeLength;
+ for (int offset = instructionOffset1; offset < instructionOffset2; offset++)
+ {
+ // Is it a traced but unmarked straddling branch?
+ if (partialEvaluator.isTraced(offset) &&
+ !isInstructionNecessary(offset) &&
+ isAnyLargerThan(partialEvaluator.branchTargets(offset),
+ instructionOffset2))
+ {
+ return true;
+ }
+ }
- // Create new arrays for storing information at each instruction offset.
- if (isNecessary.length < codeLength)
+ return false;
+ }
+
+
+ /**
+ * Returns whether all of the given instruction offsets (at least one)
+ * are smaller than or equal to the given offset.
+ */
+ private boolean isAllSmallerThanOrEqual(InstructionOffsetValue instructionOffsets,
+ int instructionOffset)
+ {
+ if (instructionOffsets != null)
{
- isNecessary = new boolean[codeLength];
- isSimplified = new boolean[codeLength];
+ // Loop over all instruction offsets.
+ int branchCount = instructionOffsets.instructionOffsetCount();
+ if (branchCount > 0)
+ {
+ for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+ {
+ // Is the offset larger than the reference offset?
+ if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
- else
+
+ return false;
+ }
+
+
+ /**
+ * Returns whether any of the given instruction offsets (at least one)
+ * is larger than the given offset.
+ */
+ private boolean isAnyLargerThan(InstructionOffsetValue instructionOffsets,
+ int instructionOffset)
+ {
+ if (instructionOffsets != null)
{
- for (int index = 0; index < codeLength; index++)
+ // Loop over all instruction offsets.
+ int branchCount = instructionOffsets.instructionOffsetCount();
+ if (branchCount > 0)
{
- isNecessary[index] = false;
- isSimplified[index] = false;
+ for (int branchIndex = 0; branchIndex < branchCount; branchIndex++)
+ {
+ // Is the offset larger than the reference offset?
+ if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset)
+ {
+ return true;
+ }
+ }
}
}
+
+ return false;
}
/**
- * Returns whether the given variable is ever referenced (stored) by an
- * instruction that is marked as necessary.
+ * Initializes the necessary data structure.
*/
- private boolean isVariableReferenced(CodeAttribute codeAttribute,
- int startOffset,
- int variableIndex)
+ private void initializeNecessary(CodeAttribute codeAttribute)
{
int codeLength = codeAttribute.u4codeLength;
+ int maxLocals = codeAttribute.u2maxLocals;
+ int maxStack = codeAttribute.u2maxStack;
+
+ // Create new arrays for storing information at each instruction offset.
+ if (variablesNecessaryAfter.length < codeLength ||
+ variablesNecessaryAfter[0].length < maxLocals)
+ {
+ variablesNecessaryAfter = new boolean[codeLength][maxLocals];
+ }
+ else
+ {
+ for (int offset = 0; offset < codeLength; offset++)
+ {
+ for (int index = 0; index < maxLocals; index++)
+ {
+ variablesNecessaryAfter[offset][index] = false;
+ }
+ }
+ }
- for (int offset = startOffset; offset < codeLength; offset++)
+ if (stacksNecessaryAfter.length < codeLength ||
+ stacksNecessaryAfter[0].length < maxStack)
{
- if (isNecessary[offset] &&
- !isSimplified[offset] &&
- isNecessary(partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex),
- true,
- false))
+ stacksNecessaryAfter = new boolean[codeLength][maxStack];
+ }
+ else
+ {
+ for (int offset = 0; offset < codeLength; offset++)
{
- return true;
+ for (int index = 0; index < maxStack; index++)
+ {
+ stacksNecessaryAfter[offset][index] = false;
+ }
}
}
- return false;
+ if (stacksSimplifiedBefore.length < codeLength ||
+ stacksSimplifiedBefore[0].length < maxStack)
+ {
+ stacksSimplifiedBefore = new boolean[codeLength][maxStack];
+ }
+ else
+ {
+ for (int offset = 0; offset < codeLength; offset++)
+ {
+ for (int index = 0; index < maxStack; index++)
+ {
+ stacksSimplifiedBefore[offset][index] = false;
+ }
+ }
+ }
+
+ if (instructionsNecessary.length < codeLength)
+ {
+ instructionsNecessary = new boolean[codeLength];
+ instructionsSimplified = new boolean[codeLength];
+ }
+ else
+ {
+ for (int index = 0; index < codeLength; index++)
+ {
+ instructionsNecessary[index] = false;
+ instructionsSimplified[index] = false;
+ }
+ }
}
@@ -2070,83 +2154,204 @@ implements AttributeVisitor,
* Returns whether the given stack entry is present after execution of the
* instruction at the given offset.
*/
- private boolean isStackEntriesNecessaryAfter(int instructionOffset, int stackIndex1, int stackIndex2, boolean all)
+ private boolean isStackEntriesNecessaryAfter(int instructionOffset,
+ int stackIndex1,
+ int stackIndex2)
{
- boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1, all);
- boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2, all);
+ boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1);
+ boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2);
// if (present1 ^ present2)
// {
-// throw new IllegalArgumentException("Can't handle partial use of dup2 instructions");
+// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions");
// }
return present1 || present2;
}
- /**
- * Returns whether the given stack entry is present before execution of the
- * instruction at the given offset.
- */
- private boolean isStackEntryNecessaryBefore(int instructionOffset,
- int stackIndex,
- boolean all)
+ private void markVariableAfter(int instructionOffset,
+ int variableIndex)
{
- return isNecessary(partialEvaluator.getStackBefore(instructionOffset).getTopConsumerValue(stackIndex),
- false,
- all);
+ if (!isVariableNecessaryAfter(instructionOffset, variableIndex))
+ {
+ if (DEBUG_ANALYSIS) System.out.print("["+instructionOffset+".v"+variableIndex+"],");
+
+ variablesNecessaryAfter[instructionOffset][variableIndex] = true;
+
+ if (maxMarkedOffset < instructionOffset)
+ {
+ maxMarkedOffset = instructionOffset;
+ }
+ }
}
/**
- * Returns whether the given stack entry is present after execution of the
- * instruction at the given offset.
+ * Returns whether the specified variable is ever necessary after an
+ * instruction in the specified block.
*/
- private boolean isStackEntryNecessaryAfter(int instructionOffset,
- int stackIndex,
- boolean all)
+ private boolean isVariableNecessaryAfter(int startOffset,
+ int endOffset,
+ int variableIndex)
{
- return isNecessary(partialEvaluator.getStackAfter(instructionOffset).getTopConsumerValue(stackIndex),
- false,
- all) ||
- partialEvaluator.getStackAfter(instructionOffset).getTopProducerValue(stackIndex).instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY);
+ for (int offset = startOffset; offset < endOffset; offset++)
+ {
+ if (isVariableNecessaryAfter(offset, variableIndex))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
- /**
- * Returns whether any or all of the instructions at the given offsets are
- * marked as necessary.
- */
- private boolean isNecessary(Value offsets,
- boolean producer,
- boolean all)
+ private boolean isVariableNecessaryAfter(int instructionOffset,
+ int variableIndex)
{
- return offsets != null &&
- isNecessary(offsets.instructionOffsetValue(), producer, all);
+ return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY ||
+ variablesNecessaryAfter[instructionOffset][variableIndex];
}
- /**
- * Returns whether any or all of the instructions at the given offsets are
- * marked as necessary.
- */
- private boolean isNecessary(InstructionOffsetValue offsets,
- boolean producer,
- boolean all)
+ private void markStackEntryAfter(int instructionOffset,
+ int stackIndex)
+ {
+ if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex))
+ {
+ if (DEBUG_ANALYSIS) System.out.print("["+instructionOffset+".s"+stackIndex+"],");
+
+ stacksNecessaryAfter[instructionOffset][stackIndex] = true;
+
+ if (maxMarkedOffset < instructionOffset)
+ {
+ maxMarkedOffset = instructionOffset;
+ }
+ }
+ }
+
+
+ private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets,
+ int stackIndex)
{
- int offsetCount = offsets.instructionOffsetCount();
+ int offsetCount = instructionOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
- int offset = offsets.instructionOffset(offsetIndex);
+ if (isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ private boolean isStackEntryNecessaryAfter(int instructionOffset,
+ int stackIndex)
+ {
+ return instructionOffset == PartialEvaluator.AT_CATCH_ENTRY ||
+ stacksNecessaryAfter[instructionOffset][stackIndex];
+ }
+
+
+ private void markStackSimplificationBefore(int instructionOffset,
+ int stackIndex)
+ {
+ stacksSimplifiedBefore[instructionOffset][stackIndex] = true;
+ }
+
+
+ private boolean isStackSimplifiedBefore(int instructionOffset,
+ int stackIndex)
+ {
+ return stacksSimplifiedBefore[instructionOffset][stackIndex];
+ }
+
+
+ private void markSimplification(Clazz clazz,
+ int instructionOffset,
+ Instruction instruction)
+ {
+ // Mark the instruction itself.
+ instructionsSimplified[instructionOffset] = true;
+
+ // Also mark all stack entries that would be popped.
+ TracedStack tracedStack =
+ partialEvaluator.getStackBefore(instructionOffset);
+
+ int top = tracedStack.size() - 1;
+ int popCount = instruction.stackPopCount(clazz);
+
+ for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
+ {
+ markStackSimplificationBefore(instructionOffset, top - stackIndex);
+ }
+ }
+
+
+ private boolean isInstructionSimplified(int instructionOffset)
+ {
+ return instructionsSimplified[instructionOffset];
+ }
+
+
+ private void markInstruction(int instructionOffset)
+ {
+ if (!isInstructionNecessary(instructionOffset))
+ {
+ if (DEBUG_ANALYSIS) System.out.print(instructionOffset+",");
+
+ instructionsNecessary[instructionOffset] = true;
+
+ if (maxMarkedOffset < instructionOffset)
+ {
+ maxMarkedOffset = instructionOffset;
+ }
+ }
+ }
+
+
+ private boolean isAnyInstructionNecessary(int instructionOffset1,
+ int instructionOffset2)
+ {
+ for (int instructionOffset = instructionOffset1;
+ instructionOffset < instructionOffset2;
+ instructionOffset++)
+ {
+ if (isInstructionNecessary(instructionOffset))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
- if (offset != PartialEvaluator.AT_METHOD_ENTRY &&
- (all ^ (isNecessary[offset] && (producer || !isSimplified[offset]))))
+ /**
+ * Returns the highest offset of an instruction that has been marked as
+ * necessary, before the given offset.
+ */
+ private int lastNecessaryInstructionOffset(int instructionOffset)
+ {
+ for (int offset = instructionOffset-1; offset >= 0; offset--)
+ {
+ if (isInstructionNecessary(instructionOffset))
{
- return !all;
+ return offset;
}
}
- return all;
+ return 0;
+ }
+
+
+ private boolean isInstructionNecessary(int instructionOffset)
+ {
+ return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY ||
+ instructionsNecessary[instructionOffset];
}
}
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
index 6cd00c1..d6fe397 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -60,17 +60,15 @@ implements AttributeVisitor,
private final InvocationUnit invocationUnit;
private final boolean evaluateAllCode;
- private InstructionOffsetValue[] varProducerValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
- private InstructionOffsetValue[] stackProducerValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
- private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
- private TracedVariables[] variablesBefore = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
- private TracedStack[] stacksBefore = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
- private TracedVariables[] variablesAfter = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
- private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
- private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
- private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH];
- private int[] initializedVariables = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+ private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+ private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+ private TracedVariables[] variablesBefore = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
+ private TracedStack[] stacksBefore = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
+ private TracedVariables[] variablesAfter = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
+ private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
+ private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+ private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+ private int[] initializedVariables = new int[ClassConstants.TYPICAL_CODE_LENGTH];
private boolean evaluateExceptions;
private final BasicBranchUnit branchUnit;
@@ -210,18 +208,6 @@ implements AttributeVisitor,
if (isTraced(offset))
{
- InstructionOffsetValue varProducerOffsets = varProducerOffsets(offset);
- if (varProducerOffsets.instructionOffsetCount() > 0)
- {
- System.out.println(" has overall been using information from instructions setting vars: "+varProducerOffsets);
- }
-
- InstructionOffsetValue stackProducerOffsets = stackProducerOffsets(offset);
- if (stackProducerOffsets.instructionOffsetCount() > 0)
- {
- System.out.println(" has overall been using information from instructions setting stack: "+stackProducerOffsets);
- }
-
int initializationOffset = branchTargetFinder.initializationOffset(offset);
if (initializationOffset != NONE)
{
@@ -284,6 +270,16 @@ implements AttributeVisitor,
/**
+ * Returns whether the instruction at the given offset is the start of a
+ * subroutine.
+ */
+ public boolean isSubroutineStart(int instructionOffset)
+ {
+ return branchTargetFinder.isSubroutineStart(instructionOffset);
+ }
+
+
+ /**
* Returns whether the instruction at the given offset is part of a
* subroutine.
*/
@@ -378,16 +374,6 @@ implements AttributeVisitor,
/**
- * Returns the instruction offsets that set the variable that is being
- * used at the given instruction offset.
- */
- public InstructionOffsetValue varProducerOffsets(int instructionOffset)
- {
- return varProducerValues[instructionOffset];
- }
-
-
- /**
* Returns the stack before execution of the instruction at the given
* offset.
*/
@@ -408,16 +394,6 @@ implements AttributeVisitor,
/**
- * Returns the instruction offsets that set the stack entries that are being
- * used at the given instruction offset.
- */
- public InstructionOffsetValue stackProducerOffsets(int instructionOffset)
- {
- return stackProducerValues[instructionOffset];
- }
-
-
- /**
* Returns the instruction offsets that branch to the given instruction
* offset.
*/
@@ -534,9 +510,9 @@ implements AttributeVisitor,
private void pushInstructionBlock(TracedVariables variables,
TracedStack stack,
int startOffset,
- java.util.Stack instructionBlockStack)
+ java.util.Stack instructionBlocKStack)
{
- instructionBlockStack.push(new MyInstructionBlock(variables,
+ instructionBlocKStack.push(new MyInstructionBlock(variables,
stack,
startOffset));
}
@@ -696,8 +672,6 @@ implements AttributeVisitor,
// Reset the trace value.
InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
- variables.setCollectedProducerValue(traceValue);
- stack.setCollectedProducerValue(traceValue);
// Reset the initialization flag.
variables.resetInitialization();
@@ -741,12 +715,6 @@ implements AttributeVisitor,
}
// Collect the offsets of the instructions whose results were used.
- InstructionOffsetValue variablesTraceValue = variables.getCollectedProducerValue().instructionOffsetValue();
- InstructionOffsetValue stackTraceValue = stack.getCollectedProducerValue().instructionOffsetValue();
- varProducerValues[instructionOffset] =
- varProducerValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
- stackProducerValues[instructionOffset] =
- stackProducerValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
initializedVariables[instructionOffset] = variables.getInitializationIndex();
// Collect the branch targets from the branch unit.
@@ -754,33 +722,14 @@ implements AttributeVisitor,
int branchTargetCount = branchTargets.instructionOffsetCount();
// Stop tracing.
- variables.setCollectedProducerValue(traceValue);
- stack.setCollectedProducerValue(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 (varProducerValues[instructionOffset].instructionOffsetCount() > 0)
- {
- System.out.println(" has up till now been using information from instructions setting vars: "+varProducerValues[instructionOffset]);
- }
- if (stackProducerValues[instructionOffset].instructionOffsetCount() > 0)
- {
- System.out.println(" has up till now been using information from instructions setting stack: "+stackProducerValues[instructionOffset]);
- }
if (branchTargetValues[instructionOffset] != null)
{
System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]);
@@ -925,8 +874,6 @@ implements AttributeVisitor,
if (variablesAfter.length < codeLength)
{
// Create new arrays.
- varProducerValues = new InstructionOffsetValue[codeLength];
- stackProducerValues = new InstructionOffsetValue[codeLength];
branchOriginValues = new InstructionOffsetValue[codeLength];
branchTargetValues = new InstructionOffsetValue[codeLength];
variablesBefore = new TracedVariables[codeLength];
@@ -940,8 +887,6 @@ implements AttributeVisitor,
// Reset the arrays.
for (int index = 0; index < codeLength; index++)
{
- varProducerValues[index] = InstructionOffsetValue.EMPTY_VALUE;
- stackProducerValues[index] = InstructionOffsetValue.EMPTY_VALUE;
initializedVariables[index] = NONE;
}
}
@@ -950,8 +895,6 @@ implements AttributeVisitor,
// Reset the arrays.
for (int index = 0; index < codeLength; index++)
{
- varProducerValues[index] = InstructionOffsetValue.EMPTY_VALUE;
- stackProducerValues[index] = InstructionOffsetValue.EMPTY_VALUE;
branchOriginValues[index] = null;
branchTargetValues[index] = null;
generalizedContexts[index] = false;
@@ -987,10 +930,6 @@ implements AttributeVisitor,
Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY);
parameters.setProducerValue(storeValue);
- // Reset the trace value.
- InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
- parameters.setCollectedProducerValue(traceValue);
-
// Initialize the method parameters.
invocationUnit.enterMethod(clazz, method, parameters);
diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java
index f42e695..893e9aa 100644
--- a/src/proguard/optimize/evaluation/VariableOptimizer.java
+++ b/src/proguard/optimize/evaluation/VariableOptimizer.java
@@ -80,18 +80,13 @@ implements AttributeVisitor
// Analyze the liveness of the variables in the code.
livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute);
- int startIndex = 0;
+ int startIndex =
+ (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ||
+ reuseThis ? 0 : 1;
- int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
- if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
- {
- parameterSize++;
-
- if (!reuseThis)
- {
- startIndex = 1;
- }
- }
+ int parameterSize =
+ ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
int variableSize = codeAttribute.u2maxLocals;
int codeLength = codeAttribute.u4codeLength;
diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java
index 736846d..5a795aa 100644
--- a/src/proguard/optimize/info/ParameterUsageMarker.java
+++ b/src/proguard/optimize/info/ParameterUsageMarker.java
@@ -45,7 +45,10 @@ implements MemberVisitor
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
{
- int parameterSize = parameterSize(programClass, programMethod);
+ int parameterSize =
+ ClassUtil.internalMethodParameterSize(programMethod.getDescriptor(programClass),
+ programMethod.getAccessFlags());
+
if (parameterSize > 0)
{
// Is it a native method?
@@ -209,12 +212,8 @@ implements MemberVisitor
*/
private int parameterSize(Clazz clazz, Method method)
{
- int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
- if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
- {
- parameterSize++;
- }
- return parameterSize;
+ return ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
}
}
diff --git a/src/proguard/optimize/info/VariableUsageMarker.java b/src/proguard/optimize/info/VariableUsageMarker.java
index 298908e..8c9b214 100644
--- a/src/proguard/optimize/info/VariableUsageMarker.java
+++ b/src/proguard/optimize/info/VariableUsageMarker.java
@@ -38,21 +38,10 @@ extends SimplifiedVisitor
implements AttributeVisitor,
InstructionVisitor
{
- private int variablesSize;
private boolean[] variableUsed = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE];
/**
- * Returns the size of the variables frame of the most recently visited
- * code attribute.
- */
- public int getVariablesSize()
- {
- return variablesSize;
- }
-
-
- /**
* Returns whether the given variable has been marked as being used.
*/
public boolean isVariableUsed(int variableIndex)
@@ -83,8 +72,6 @@ implements AttributeVisitor,
}
}
- variablesSize = maxLocals;
-
codeAttribute.instructionsAccept(clazz, method, this);
}
diff --git a/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
index e340e02..b8705fa 100644
--- a/src/proguard/optimize/peephole/InstructionSequenceConstants.java
+++ b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
@@ -1594,24 +1594,24 @@ public class InstructionSequenceConstants
new SimpleInstruction(InstructionConstants.OP_LCONST_0),
},
},
- { // ... % 1f = 0f
- {
- new SimpleInstruction(InstructionConstants.OP_FCONST_1),
- new SimpleInstruction(InstructionConstants.OP_FREM),
- },{
- new SimpleInstruction(InstructionConstants.OP_POP),
- new SimpleInstruction(InstructionConstants.OP_FCONST_0),
- },
- },
- { // ... % 1d = 0d
- {
- new SimpleInstruction(InstructionConstants.OP_DCONST_1),
- new SimpleInstruction(InstructionConstants.OP_DREM),
- },{
- new SimpleInstruction(InstructionConstants.OP_POP2),
- new SimpleInstruction(InstructionConstants.OP_DCONST_0),
- },
- },
+// { // ... % 1f = 0f
+// {
+// new SimpleInstruction(InstructionConstants.OP_FCONST_1),
+// new SimpleInstruction(InstructionConstants.OP_FREM),
+// },{
+// new SimpleInstruction(InstructionConstants.OP_POP),
+// new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+// },
+// },
+// { // ... % 1d = 0d
+// {
+// new SimpleInstruction(InstructionConstants.OP_DCONST_1),
+// new SimpleInstruction(InstructionConstants.OP_DREM),
+// },{
+// new SimpleInstruction(InstructionConstants.OP_POP2),
+// new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+// },
+// },
{ // -(-...) = ...
{
new SimpleInstruction(InstructionConstants.OP_INEG),
diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java
index 0194da7..f116f40 100644
--- a/src/proguard/optimize/peephole/MethodInliner.java
+++ b/src/proguard/optimize/peephole/MethodInliner.java
@@ -35,8 +35,8 @@ import proguard.optimize.info.*;
import java.util.Stack;
/**
- * This AttributeVisitor inlines short methods in the code attributes that it
- * visits.
+ * This AttributeVisitor inlines short methods or methods that are only invoked
+ * once, in the code attributes that it visits.
*
* @author Eric Lafortune
*/
@@ -48,9 +48,11 @@ implements AttributeVisitor,
ConstantVisitor,
MemberVisitor
{
- private static final int MAXIMUM_INLINING_CODE_LENGTH = 8;
- private static final int MAXIMUM_CODE_EXPANSION = 2;
- private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128;
+ private static final int MAXIMUM_INLINED_CODE_LENGTH = Integer.parseInt(System.getProperty("maximum.inlined.code.length", "8"));
+ private static final int MAXIMUM_RESULTING_CODE_LENGTH_JSE = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "8000"));
+ private static final int MAXIMUM_RESULTING_CODE_LENGTH_JME = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "2000"));
+ private static final int MAXIMUM_CODE_EXPANSION = 2;
+ private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128;
//*
private static final boolean DEBUG = false;
@@ -59,6 +61,7 @@ implements AttributeVisitor,
//*/
+ private final boolean microEdition;
private final boolean allowAccessModification;
private final boolean inlineSingleInvocations;
private final InstructionVisitor extraInlinedInvocationVisitor;
@@ -69,45 +72,59 @@ implements AttributeVisitor,
private final ConstantAdder constantAdder = new ConstantAdder();
private final StackSizeComputer stackSizeComputer = new StackSizeComputer();
- private Clazz targetClass;
- private Method targetMethod;
- private boolean inlining;
- private Stack inliningMethods = new Stack();
- private boolean emptyInvokingStack;
- private int uninitializedObjectCount;
- private int variableOffset;
- private boolean inlined;
- private boolean inlinedAny;
+ private ProgramClass targetClass;
+ private ProgramMethod targetMethod;
+ private int estimatedResultingCodeLength;
+ private boolean inlining;
+ private Stack inliningMethods = new Stack();
+ private boolean emptyInvokingStack;
+ private int uninitializedObjectCount;
+ private int variableOffset;
+ private boolean inlined;
+ private boolean inlinedAny;
/**
* Creates a new MethodInliner.
+ * @param microEdition indicates whether the resulting code is
+ * targeted at Java Micro Edition.
* @param allowAccessModification indicates whether the access modifiers of
* classes and class members can be changed
* in order to inline methods.
+ * @param inlineSingleInvocations indicates whether the single invocations
+ * should be inlined, or, alternatively,
+ * short methods.
*/
- public MethodInliner(boolean allowAccessModification,
+ public MethodInliner(boolean microEdition,
+ boolean allowAccessModification,
boolean inlineSingleInvocations)
{
- this(allowAccessModification, inlineSingleInvocations, null);
+ this(microEdition,
+ allowAccessModification,
+ inlineSingleInvocations,
+ null);
}
/**
* Creates a new MethodInliner.
+ * @param microEdition indicates whether the resulting code is
+ * targeted at Java Micro Edition.
* @param allowAccessModification indicates whether the access modifiers of
* classes and class members can be changed
* in order to inline methods.
- * @param allowAccessModification indicates whether the single invocations
+ * @param inlineSingleInvocations indicates whether the single invocations
* should be inlined, or, alternatively,
* short methods.
* @param extraInlinedInvocationVisitor an optional extra visitor for all
* inlined invocation instructions.
*/
- public MethodInliner(boolean allowAccessModification,
+ public MethodInliner(boolean microEdition,
+ boolean allowAccessModification,
boolean inlineSingleInvocations,
InstructionVisitor extraInlinedInvocationVisitor)
{
+ this.microEdition = microEdition;
this.allowAccessModification = allowAccessModification;
this.inlineSingleInvocations = inlineSingleInvocations;
this.extraInlinedInvocationVisitor = extraInlinedInvocationVisitor;
@@ -127,13 +144,14 @@ implements AttributeVisitor,
// clazz.getName().equals("abc/Def") &&
// method.getName(clazz).equals("abc");
- targetClass = clazz;
- targetMethod = method;
+ targetClass = (ProgramClass)clazz;
+ targetMethod = (ProgramMethod)method;
+ estimatedResultingCodeLength = codeAttribute.u4codeLength;
inliningMethods.clear();
- uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0;
- inlinedAny = false;
+ uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0;
+ inlinedAny = false;
codeAttributeComposer.reset();
- constantAdder.setTargetClass((ProgramClass)clazz);
+ constantAdder.setTargetClass(targetClass);
stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute);
// Append the body of the code.
@@ -157,10 +175,13 @@ implements AttributeVisitor,
}
// Only inline the method if it is invoked once or if it is short.
- // TODO: Check total code length after inlining.
- else if (inlineSingleInvocations ?
- MethodInvocationMarker.getInvocationCount(method) == 1 :
- codeAttribute.u4codeLength <= MAXIMUM_INLINING_CODE_LENGTH)
+ else if ((inlineSingleInvocations ?
+ MethodInvocationMarker.getInvocationCount(method) == 1 :
+ codeAttribute.u4codeLength <= MAXIMUM_INLINED_CODE_LENGTH) &&
+ estimatedResultingCodeLength + codeAttribute.u4codeLength <
+ (microEdition ?
+ MAXIMUM_RESULTING_CODE_LENGTH_JME :
+ MAXIMUM_RESULTING_CODE_LENGTH_JSE))
{
if (DEBUG)
{
@@ -169,6 +190,11 @@ implements AttributeVisitor,
targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]");
}
+ // Ignore the removal of the original method invocation,
+ // the addition of the parameter setup, and
+ // the modification of a few inlined instructions.
+ estimatedResultingCodeLength += codeAttribute.u4codeLength;
+
// Append instructions to store the parameters.
storeParameters(clazz, method);
@@ -479,6 +505,11 @@ implements AttributeVisitor,
// Only inline the method if it isn't recursing.
!inliningMethods.contains(programMethod) &&
+ // Only inline the method if its target class has at least the
+ // same version number as the source class, in order to avoid
+ // introducing incompatible constructs.
+ targetClass.u4version >= programClass.u4version &&
+
// Only inline the method if it doesn't invoke a super method, or if
// it is in the same class.
(!SuperInvocationMarker.invokesSuperMethods(programMethod) ||
diff --git a/src/proguard/optimize/peephole/VariableShrinker.java b/src/proguard/optimize/peephole/VariableShrinker.java
index 3418452..4440227 100644
--- a/src/proguard/optimize/peephole/VariableShrinker.java
+++ b/src/proguard/optimize/peephole/VariableShrinker.java
@@ -80,30 +80,28 @@ implements AttributeVisitor
{
if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
{
- // Figure out the local variables that are used by the code.
- variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute);
+ // Compute the parameter size.
+ int parameterSize =
+ ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
+ method.getAccessFlags());
// Get the total size of the local variable frame.
- int variablesSize = variableUsageMarker.getVariablesSize();
-
- // The descriptor may have been been shrunk already, so get the
- // original parameter size.
- int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
+ int maxLocals = codeAttribute.u2maxLocals;
if (DEBUG)
{
System.out.println("VariableShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
- System.out.println(" parameter size = " + parameterSize);
- System.out.println(" variables size = " + variablesSize);
+ System.out.println(" Parameter size = " + parameterSize);
+ System.out.println(" Max locals = " + maxLocals);
}
- // Make sure the size of the local variable frame is set correctly.
- codeAttribute.u2maxLocals = variablesSize;
+ // Figure out the local variables that are used by the code.
+ variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute);
// Delete unused local variables from the local variable frame.
- variableEditor.reset(variablesSize);
+ variableEditor.reset(maxLocals);
- for (int variableIndex = parameterSize+1; variableIndex < variablesSize; variableIndex++)
+ for (int variableIndex = parameterSize+1; variableIndex < maxLocals; variableIndex++)
{
// Is the variable not required?
if (!variableUsageMarker.isVariableUsed(variableIndex))
diff --git a/src/proguard/preverify/CodeSubroutineInliner.java b/src/proguard/preverify/CodeSubroutineInliner.java
index 5f448e3..d3d3eb7 100644
--- a/src/proguard/preverify/CodeSubroutineInliner.java
+++ b/src/proguard/preverify/CodeSubroutineInliner.java
@@ -27,7 +27,7 @@ import proguard.classfile.editor.CodeAttributeComposer;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.SimplifiedVisitor;
-import proguard.classfile.visitor.ExceptionExcludedOffsetFilter;
+import proguard.classfile.visitor.*;
import proguard.optimize.peephole.BranchTargetFinder;
/**
@@ -50,10 +50,11 @@ implements AttributeVisitor,
private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
- private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
+ private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(true);
- private boolean inlinedAny;
- private ExceptionInfoVisitor subroutineExceptionInliner;
+ private ExceptionInfoVisitor subroutineExceptionInliner = this;
+ private int clipStart = 0;
+ private int clipEnd = Integer.MAX_VALUE;
// Implementations for AttributeVisitor.
@@ -67,6 +68,11 @@ implements AttributeVisitor,
// clazz.getName().equals("abc/Def") &&
// method.getName(clazz).equals("abc");
+ if (DEBUG)
+ {
+ method.accept(clazz, new ClassPrinter());
+ }
+
branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
// Don't bother if there aren't any subroutines anyway.
@@ -75,50 +81,13 @@ implements AttributeVisitor,
return;
}
- inlinedAny = false;
- subroutineExceptionInliner = this;
- codeAttributeComposer.reset();
-
- // Append the body of the code.
- copyCode(clazz, method, codeAttribute);
-
- // Update the code attribute if any code has been inlined.
- if (inlinedAny)
- {
- codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
- }
- }
-
-
- /**
- * Returns whether the given code attribute contains any subroutines.
- */
- private boolean containsSubroutines(CodeAttribute codeAttribute)
- {
- for (int offset = 0; offset < codeAttribute.u4codeLength; offset++)
- {
- if (branchTargetFinder.isSubroutineInvocation(offset))
- {
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
- * Appends the code of the given code attribute.
- */
- private void copyCode(Clazz clazz,
- Method method,
- CodeAttribute codeAttribute)
- {
if (DEBUG)
{
System.out.println("SubroutineInliner: processing ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
}
+ // Append the body of the code.
+ codeAttributeComposer.reset();
codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
// Copy the non-subroutine instructions.
@@ -135,7 +104,7 @@ implements AttributeVisitor,
// Skip the subroutine.
if (DEBUG)
{
- System.out.println("Skipping subroutine at ["+offset+"]");
+ System.out.println(" Skipping original subroutine instruction "+instruction.toString(offset));
}
// Append a label at this offset instead.
@@ -152,17 +121,43 @@ implements AttributeVisitor,
// Copy the exceptions. Note that exceptions with empty try blocks
// are automatically removed.
- codeAttribute.exceptionsAccept(clazz, method, this);
+ codeAttribute.exceptionsAccept(clazz,
+ method,
+ subroutineExceptionInliner);
if (DEBUG)
{
- System.out.println("Appending label after code at ["+offset+"]");
+ System.out.println(" Appending label after code at ["+offset+"]");
}
// Append a label just after the code.
codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
+ // End and update the code attribute.
codeAttributeComposer.endCodeFragment();
+ codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
+
+ if (DEBUG)
+ {
+ method.accept(clazz, new ClassPrinter());
+ }
+ }
+
+
+ /**
+ * Returns whether the given code attribute contains any subroutines.
+ */
+ private boolean containsSubroutines(CodeAttribute codeAttribute)
+ {
+ for (int offset = 0; offset < codeAttribute.u4codeLength; offset++)
+ {
+ if (branchTargetFinder.isSubroutineInvocation(offset))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
@@ -179,17 +174,20 @@ implements AttributeVisitor,
if (DEBUG)
{
- System.out.println("Inlining subroutine ["+subroutineStart+" -> "+subroutineEnd+"] at ["+subroutineInvocationOffset+"]");
+ System.out.println(" Inlining subroutine ["+subroutineStart+" -> "+subroutineEnd+"] at ["+subroutineInvocationOffset+"]");
}
// Don't go inlining exceptions that are already applicable to this
// subroutine invocation.
- ExceptionInfoVisitor oldSubroutineExceptionInliner =
- subroutineExceptionInliner;
+ ExceptionInfoVisitor oldSubroutineExceptionInliner = subroutineExceptionInliner;
+ int oldClipStart = clipStart;
+ int oldClipEnd = clipEnd;
subroutineExceptionInliner =
new ExceptionExcludedOffsetFilter(subroutineInvocationOffset,
subroutineExceptionInliner);
+ clipStart = subroutineStart;
+ clipEnd = subroutineEnd;
codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
@@ -203,16 +201,12 @@ implements AttributeVisitor,
if (DEBUG)
{
- System.out.println("Appending label after inlined subroutine at ["+subroutineEnd+"]");
+ System.out.println(" Appending label after inlined subroutine at ["+subroutineEnd+"]");
}
// Append a label just after the code.
codeAttributeComposer.appendLabel(subroutineEnd);
- // We can again inline exceptions that are applicable to this
- // subroutine invocation.
- subroutineExceptionInliner = oldSubroutineExceptionInliner;
-
// Inline the subroutine exceptions.
codeAttribute.exceptionsAccept(clazz,
method,
@@ -220,9 +214,13 @@ implements AttributeVisitor,
subroutineEnd,
subroutineExceptionInliner);
- codeAttributeComposer.endCodeFragment();
+ // We can again inline exceptions that are applicable to this
+ // subroutine invocation.
+ subroutineExceptionInliner = oldSubroutineExceptionInliner;
+ clipStart = oldClipStart;
+ clipEnd = oldClipEnd;
- inlinedAny = true;
+ codeAttributeComposer.endCodeFragment();
}
@@ -245,7 +243,7 @@ implements AttributeVisitor,
{
if (DEBUG)
{
- System.out.println("Replacing subroutine return at ["+offset+"] by a label");
+ System.out.println(" Replacing subroutine return at ["+offset+"] by a label");
}
// Append a label at this offset instead of the subroutine return.
@@ -255,7 +253,7 @@ implements AttributeVisitor,
{
if (DEBUG)
{
- System.out.println("Replacing subroutine return at ["+offset+"] by a simple branch");
+ System.out.println(" Replacing subroutine return at ["+offset+"] by a simple branch");
}
// Replace the instruction by a branch.
@@ -270,7 +268,7 @@ implements AttributeVisitor,
{
if (DEBUG)
{
- System.out.println("Replacing first subroutine instruction at ["+offset+"] by a label");
+ System.out.println(" Replacing first subroutine instruction at ["+offset+"] by a label");
}
// Append a label at this offset instead of saving the subroutine
@@ -320,8 +318,6 @@ implements AttributeVisitor,
branchOffset).shrink();
codeAttributeComposer.appendInstruction(offset, replacementInstruction);
-
- inlinedAny = true;
}
}
else
@@ -336,34 +332,43 @@ implements AttributeVisitor,
public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
{
- int startPC = exceptionInfo.u2startPC;
- int endPC = exceptionInfo.u2endPC;
+ int startPC = Math.max(exceptionInfo.u2startPC, clipStart);
+ int endPC = Math.min(exceptionInfo.u2endPC, clipEnd);
int handlerPC = exceptionInfo.u2handlerPC;
int catchType = exceptionInfo.u2catchType;
// Exclude any subroutine invocations that jump out of the try block,
// by adding a try block before (and later on, after) each invocation.
- int offset = startPC;
- while (offset < endPC)
+ for (int offset = startPC; offset < endPC; offset++)
{
- Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
- int instructionLength = instruction.length(offset);
-
- // Is it a subroutine invocation?
- if (branchTargetFinder.isSubroutineInvocation(offset) &&
- !exceptionInfo.isApplicable(offset + ((BranchInstruction)instruction).branchOffset))
+ if (branchTargetFinder.isSubroutineInvocation(offset))
{
- // Append a try block that ends before the subroutine invocation.
- codeAttributeComposer.appendException(new ExceptionInfo(startPC,
- offset,
- handlerPC,
- catchType));
-
- // The next try block will start after the subroutine invocation.
- startPC = offset + instructionLength;
+ Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
+ int instructionLength = instruction.length(offset);
+
+ // Is it a subroutine invocation?
+ if (!exceptionInfo.isApplicable(offset + ((BranchInstruction)instruction).branchOffset))
+ {
+ if (DEBUG)
+ {
+ System.out.println(" Appending extra exception ["+startPC+" -> "+offset+"] -> "+handlerPC);
+ }
+
+ // Append a try block that ends before the subroutine invocation.
+ codeAttributeComposer.appendException(new ExceptionInfo(startPC,
+ offset,
+ handlerPC,
+ catchType));
+
+ // The next try block will start after the subroutine invocation.
+ startPC = offset + instructionLength;
+ }
}
+ }
- offset += instructionLength;
+ if (DEBUG)
+ {
+ System.out.println(" Appending exception ["+startPC+" -> "+endPC+"] -> "+handlerPC);
}
// Append the exception. Note that exceptions with empty try blocks
@@ -372,7 +377,5 @@ implements AttributeVisitor,
endPC,
handlerPC,
catchType));
-
- // TODO: While inlining a subroutine, its exception handler code may have to be inlined too.
}
}
diff --git a/src/proguard/shrink/ClassShrinker.java b/src/proguard/shrink/ClassShrinker.java
index bf49e34..5c50b8b 100644
--- a/src/proguard/shrink/ClassShrinker.java
+++ b/src/proguard/shrink/ClassShrinker.java
@@ -25,9 +25,9 @@ import proguard.classfile.attribute.*;
import proguard.classfile.attribute.annotation.*;
import proguard.classfile.attribute.annotation.visitor.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
-import proguard.classfile.constant.Constant;
-import proguard.classfile.editor.ConstantPoolRemapper;
-import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.editor.*;
+import proguard.classfile.util.*;
import proguard.classfile.visitor.*;
/**
@@ -102,6 +102,9 @@ implements ClassVisitor,
constantPoolRemapper.setConstantIndexMap(constantIndexMap);
constantPoolRemapper.visitProgramClass(programClass);
+ // Remove the unused interfaces from the class signature.
+ programClass.attributesAccept(new SignatureShrinker());
+
// Compact the extra field pointing to the subclasses of this class.
programClass.subClasses =
shrinkToNewArray(programClass.subClasses);
@@ -215,6 +218,73 @@ implements ClassVisitor,
}
+ /**
+ * This AttributeVisitor updates the Utf8 constants of class signatures,
+ * removing any unused interfaces.
+ */
+ private class SignatureShrinker
+ extends SimplifiedVisitor
+ implements AttributeVisitor
+ {
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+ {
+ String signature = clazz.getString(signatureAttribute.u2signatureIndex);
+ Clazz[] referencedClasses = signatureAttribute.referencedClasses;
+
+ // Go over the generic definitions, superclass and implemented interfaces.
+ InternalTypeEnumeration internalTypeEnumeration =
+ new InternalTypeEnumeration(signature);
+
+ StringBuffer newSignatureBuffer = new StringBuffer();
+
+ int referencedClassIndex = 0;
+ int newReferencedClassIndex = 0;
+
+ while (internalTypeEnumeration.hasMoreTypes())
+ {
+ // Consider the classes referenced by this signature.
+ String type = internalTypeEnumeration.nextType();
+ int classCount = new DescriptorClassEnumeration(type).classCount();
+
+ Clazz referencedClass = referencedClasses[referencedClassIndex];
+ if (referencedClass == null ||
+ usageMarker.isUsed(referencedClass))
+ {
+ // Append the superclass or interface.
+ newSignatureBuffer.append(type);
+
+ // Copy the referenced classes.
+ for (int counter = 0; counter < classCount; counter++)
+ {
+ referencedClasses[newReferencedClassIndex++] =
+ referencedClasses[referencedClassIndex++];
+ }
+ }
+ else
+ {
+ // Skip the referenced classes.
+ referencedClassIndex += classCount;
+ }
+ }
+
+ if (newReferencedClassIndex < referencedClassIndex)
+ {
+ // Update the signature.
+ ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString());
+
+ // Clear the unused entries.
+ while (newReferencedClassIndex < referencedClassIndex)
+ {
+ referencedClasses[newReferencedClassIndex++] = null;
+ }
+ }
+ }
+ }
+
+
// Implementations for ElementValueVisitor.
public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {}
--
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