[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