[proguard] 01/02: Imported Upstream version 4.4

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Thu Apr 10 09:00:42 UTC 2014


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to annotated tag debian/4.4-1
in repository proguard.

commit 37e94f0a7dbd79b3e4b7545819ad227053e6b599
Author: Sam Clegg <samo at debian.org>
Date:   Thu Apr 10 10:42:01 2014 +0200

    Imported Upstream version 4.4
---
 README                                             |    2 +-
 build/README                                       |   34 -
 build/build.sh                                     |   18 +-
 build/build.xml                                    |    5 +
 build/makefile                                     |    4 +-
 docs/FAQ.html                                      |    2 +-
 docs/GPL_exception.html                            |    3 +-
 docs/acknowledgements.html                         |    5 +-
 docs/alternatives.html                             |   12 +-
 docs/downloads.html                                |   47 +-
 docs/feedback.html                                 |    2 +-
 docs/index.html                                    |    2 +-
 docs/license.html                                  |   11 +-
 docs/main.html                                     |    2 +-
 docs/manual/ant.html                               |   75 +-
 docs/manual/examples.html                          |  318 ++-
 docs/manual/gui.html                               |    6 +-
 docs/manual/index.html                             |    2 +-
 docs/manual/introduction.html                      |    2 +-
 docs/manual/limitations.html                       |    2 +-
 docs/manual/optimizations.html                     |  158 ++
 docs/manual/refcard.html                           |   42 +-
 docs/manual/retrace/examples.html                  |    2 +-
 docs/manual/retrace/index.html                     |    2 +-
 docs/manual/retrace/introduction.html              |    2 +-
 docs/manual/retrace/usage.html                     |   87 +-
 docs/manual/style.css                              |    6 +
 docs/manual/troubleshooting.html                   |   35 +-
 docs/manual/usage.html                             |  276 ++-
 docs/manual/wtk.html                               |    2 +-
 docs/quality.html                                  |    2 +-
 docs/results.html                                  |    2 +-
 docs/screenshots.html                              |    2 +-
 docs/sections.html                                 |    4 +
 docs/sflogo.png                                    |  Bin 1299 -> 469 bytes
 docs/testimonials.html                             |    2 +-
 docs/title.html                                    |    2 +-
 examples/annotations/examples/Applet.java          |    6 +-
 examples/annotations/examples/Application.java     |    6 +-
 examples/annotations/examples/Bean.java            |    6 +-
 examples/annotations/examples/NativeCallBack.java  |    6 +-
 examples/annotations/lib/annotations.jar           |  Bin 6123 -> 6123 bytes
 examples/ant/proguard.xml                          |    1 +
 lib/proguard.jar                                   |  Bin 0 -> 664512 bytes
 lib/proguardgui.jar                                |  Bin 0 -> 137516 bytes
 lib/retrace.jar                                    |  Bin 0 -> 6888 bytes
 src/proguard/ArgumentWordReader.java               |    2 +-
 src/proguard/ClassPath.java                        |    2 +-
 src/proguard/ClassPathEntry.java                   |   71 +-
 src/proguard/ClassSpecification.java               |   28 +-
 src/proguard/ClassSpecificationVisitorFactory.java |   52 +-
 src/proguard/Configuration.java                    |   50 +-
 src/proguard/ConfigurationConstants.java           |    6 +-
 src/proguard/ConfigurationParser.java              |   92 +-
 src/proguard/ConfigurationWriter.java              |  105 +-
 src/proguard/DataEntryReaderFactory.java           |   16 +-
 src/proguard/DataEntryWriterFactory.java           |   16 +-
 src/proguard/DescriptorKeepChecker.java            |    6 +-
 src/proguard/DuplicateClassPrinter.java            |    8 +-
 src/proguard/FileWordReader.java                   |    2 +-
 src/proguard/FullyQualifiedClassNameChecker.java   |    8 +-
 src/proguard/GPL.java                              |    3 +-
 src/proguard/Initializer.java                      |  282 +--
 src/proguard/InputReader.java                      |   87 +-
 ...cification.java => KeepClassSpecification.java} |   35 +-
 src/proguard/MemberSpecification.java              |    2 +-
 src/proguard/OutputWriter.java                     |   93 +-
 src/proguard/ParseException.java                   |    2 +-
 src/proguard/ProGuard.java                         |    4 +-
 src/proguard/SubclassedClassFilter.java            |    2 +-
 src/proguard/Targeter.java                         |    4 +-
 src/proguard/UpToDateChecker.java                  |    2 +-
 src/proguard/WordReader.java                       |    2 +-
 src/proguard/ant/ClassPathElement.java             |   13 +-
 src/proguard/ant/ClassSpecificationElement.java    |    4 +-
 src/proguard/ant/ConfigurationElement.java         |    2 +-
 src/proguard/ant/ConfigurationTask.java            |  142 +-
 src/proguard/ant/FilterElement.java                |   20 +-
 src/proguard/ant/KeepAttributeElement.java         |   70 -
 src/proguard/ant/KeepSpecificationElement.java     |   10 +-
 src/proguard/ant/MemberSpecificationElement.java   |    2 +-
 src/proguard/ant/ProGuardTask.java                 |    7 +-
 src/proguard/classfile/ClassConstants.java         |    2 +-
 src/proguard/classfile/ClassPool.java              |    2 +-
 src/proguard/classfile/Clazz.java                  |    2 +-
 src/proguard/classfile/Field.java                  |    2 +-
 src/proguard/classfile/LibraryClass.java           |    2 +-
 src/proguard/classfile/LibraryField.java           |    2 +-
 src/proguard/classfile/LibraryMember.java          |    2 +-
 src/proguard/classfile/LibraryMethod.java          |    2 +-
 src/proguard/classfile/Member.java                 |    2 +-
 src/proguard/classfile/Method.java                 |    2 +-
 src/proguard/classfile/ProgramClass.java           |    2 +-
 src/proguard/classfile/ProgramField.java           |    2 +-
 src/proguard/classfile/ProgramMember.java          |    2 +-
 src/proguard/classfile/ProgramMethod.java          |    2 +-
 src/proguard/classfile/VisitorAccepter.java        |    2 +-
 src/proguard/classfile/attribute/Attribute.java    |    2 +-
 .../classfile/attribute/CodeAttribute.java         |    2 +-
 .../attribute/ConstantValueAttribute.java          |    2 +-
 .../classfile/attribute/DeprecatedAttribute.java   |    2 +-
 .../attribute/EnclosingMethodAttribute.java        |    2 +-
 .../classfile/attribute/ExceptionInfo.java         |    2 +-
 .../classfile/attribute/ExceptionsAttribute.java   |    2 +-
 .../classfile/attribute/InnerClassesAttribute.java |    2 +-
 .../classfile/attribute/InnerClassesInfo.java      |    2 +-
 .../classfile/attribute/LineNumberInfo.java        |   12 +-
 .../attribute/LineNumberTableAttribute.java        |    2 +-
 .../classfile/attribute/LocalVariableInfo.java     |   27 +-
 .../attribute/LocalVariableTableAttribute.java     |    2 +-
 .../classfile/attribute/LocalVariableTypeInfo.java |   27 +-
 .../attribute/LocalVariableTypeTableAttribute.java |    2 +-
 .../classfile/attribute/SignatureAttribute.java    |    2 +-
 .../classfile/attribute/SourceDirAttribute.java    |    2 +-
 .../classfile/attribute/SourceFileAttribute.java   |    2 +-
 .../classfile/attribute/SyntheticAttribute.java    |    2 +-
 .../classfile/attribute/UnknownAttribute.java      |    2 +-
 .../classfile/attribute/annotation/Annotation.java |    2 +-
 .../annotation/AnnotationDefaultAttribute.java     |    2 +-
 .../annotation/AnnotationElementValue.java         |    2 +-
 .../attribute/annotation/AnnotationsAttribute.java |    2 +-
 .../attribute/annotation/ArrayElementValue.java    |    2 +-
 .../attribute/annotation/ClassElementValue.java    |    2 +-
 .../attribute/annotation/ConstantElementValue.java |    2 +-
 .../attribute/annotation/ElementValue.java         |    2 +-
 .../annotation/EnumConstantElementValue.java       |    2 +-
 .../annotation/ParameterAnnotationsAttribute.java  |    2 +-
 .../RuntimeInvisibleAnnotationsAttribute.java      |    2 +-
 ...timeInvisibleParameterAnnotationsAttribute.java |    2 +-
 .../RuntimeVisibleAnnotationsAttribute.java        |    2 +-
 ...untimeVisibleParameterAnnotationsAttribute.java |    2 +-
 .../annotation/visitor/AllAnnotationVisitor.java   |    2 +-
 .../annotation/visitor/AnnotatedClassVisitor.java  |    2 +-
 .../visitor/AnnotationToMemberVisitor.java         |    2 +-
 .../annotation/visitor/AnnotationTypeFilter.java   |    2 +-
 .../annotation/visitor/AnnotationVisitor.java      |    2 +-
 .../annotation/visitor/ElementValueVisitor.java    |    2 +-
 .../attribute/preverification/DoubleType.java      |    2 +-
 .../attribute/preverification/FloatType.java       |    2 +-
 .../attribute/preverification/FullFrame.java       |    2 +-
 .../attribute/preverification/IntegerType.java     |    2 +-
 .../attribute/preverification/LessZeroFrame.java   |    2 +-
 .../attribute/preverification/LongType.java        |    2 +-
 .../attribute/preverification/MoreZeroFrame.java   |    2 +-
 .../attribute/preverification/NullType.java        |    2 +-
 .../attribute/preverification/ObjectType.java      |    2 +-
 .../attribute/preverification/SameOneFrame.java    |    2 +-
 .../attribute/preverification/SameZeroFrame.java   |    2 +-
 .../preverification/StackMapAttribute.java         |    2 +-
 .../attribute/preverification/StackMapFrame.java   |    2 +-
 .../preverification/StackMapTableAttribute.java    |    2 +-
 .../attribute/preverification/TopType.java         |    2 +-
 .../preverification/UninitializedThisType.java     |    2 +-
 .../preverification/UninitializedType.java         |    2 +-
 .../preverification/VerificationType.java          |    2 +-
 .../preverification/VerificationTypeFactory.java   |    2 +-
 .../visitor/StackMapFrameVisitor.java              |    2 +-
 .../visitor/VerificationTypeVisitor.java           |    2 +-
 .../attribute/visitor/AllAttributeVisitor.java     |    2 +-
 .../visitor/AllExceptionInfoVisitor.java}          |   17 +-
 .../attribute/visitor/AttributeNameFilter.java     |    2 +-
 .../attribute/visitor/AttributeVisitor.java        |    2 +-
 .../attribute/visitor/ExceptionInfoVisitor.java    |    2 +-
 .../attribute/visitor/InnerClassesInfoVisitor.java |    2 +-
 .../attribute/visitor/LineNumberInfoVisitor.java   |    2 +-
 .../visitor/LocalVariableInfoVisitor.java          |    2 +-
 .../visitor/LocalVariableTypeInfoVisitor.java      |    2 +-
 .../attribute/visitor/MultiAttributeVisitor.java   |    2 +-
 .../attribute/visitor/RequiredAttributeFilter.java |    2 +-
 .../attribute/visitor/StackSizeComputer.java       |    4 +-
 src/proguard/classfile/constant/ClassConstant.java |    2 +-
 src/proguard/classfile/constant/Constant.java      |    2 +-
 .../classfile/constant/DoubleConstant.java         |    2 +-
 .../classfile/constant/FieldrefConstant.java       |    2 +-
 src/proguard/classfile/constant/FloatConstant.java |    2 +-
 .../classfile/constant/IntegerConstant.java        |    2 +-
 .../constant/InterfaceMethodrefConstant.java       |    2 +-
 src/proguard/classfile/constant/LongConstant.java  |    2 +-
 .../classfile/constant/MethodrefConstant.java      |    2 +-
 .../classfile/constant/NameAndTypeConstant.java    |    2 +-
 src/proguard/classfile/constant/RefConstant.java   |    2 +-
 .../classfile/constant/StringConstant.java         |    2 +-
 src/proguard/classfile/constant/Utf8Constant.java  |  134 +-
 .../constant/visitor/AllConstantVisitor.java       |    2 +-
 .../constant/visitor/ConstantVisitor.java          |    2 +-
 .../visitor/ExceptClassConstantFilter.java         |    2 +-
 src/proguard/classfile/editor/AccessFixer.java     |    2 +-
 src/proguard/classfile/editor/AnnotationAdder.java |   69 +-
 .../editor/AnnotationsAttributeEditor.java         |    2 +-
 src/proguard/classfile/editor/AttributeAdder.java  |   85 +-
 src/proguard/classfile/editor/AttributeSorter.java |    2 +-
 .../classfile/editor/AttributesEditor.java         |    2 +-
 src/proguard/classfile/editor/ClassEditor.java     |    2 +-
 .../classfile/editor/ClassElementSorter.java       |    2 +-
 .../classfile/editor/ClassMemberSorter.java        |    2 +-
 .../classfile/editor/ClassReferenceFixer.java      |    2 +-
 .../classfile/editor/CodeAttributeComposer.java    |   69 +-
 .../classfile/editor/CodeAttributeEditor.java      |  200 +-
 .../editor/CodeAttributeEditorResetter.java        |    2 +-
 .../classfile/editor/ComparableConstant.java       |    2 +-
 src/proguard/classfile/editor/ConstantAdder.java   |    2 +-
 .../classfile/editor/ConstantPoolEditor.java       |    2 +-
 .../classfile/editor/ConstantPoolRemapper.java     |    2 +-
 .../classfile/editor/ConstantPoolSorter.java       |    2 +-
 .../classfile/editor/ElementValueAdder.java        |    3 +-
 .../classfile/editor/ElementValuesEditor.java      |    2 +-
 src/proguard/classfile/editor/ExceptionAdder.java  |    2 +-
 .../classfile/editor/ExceptionInfoAdder.java       |    2 +-
 .../editor/ExceptionsAttributeEditor.java          |    2 +-
 .../classfile/editor/InstructionAdder.java         |    2 +-
 .../classfile/editor/InstructionWriter.java        |    2 +-
 src/proguard/classfile/editor/InterfaceAdder.java  |    2 +-
 src/proguard/classfile/editor/InterfaceSorter.java |    2 +-
 .../classfile/editor/InterfacesEditor.java         |    2 +-
 .../classfile/editor/LineNumberInfoAdder.java      |   59 +
 .../editor/LineNumberTableAttributeEditor.java     |   67 +
 .../classfile/editor/LocalVariableInfoAdder.java   |   67 +
 .../editor/LocalVariableTableAttributeEditor.java  |   67 +
 .../editor/LocalVariableTypeInfoAdder.java         |   68 +
 .../LocalVariableTypeTableAttributeEditor.java     |   68 +
 src/proguard/classfile/editor/MemberAdder.java     |    4 +-
 .../classfile/editor/MemberReferenceFixer.java     |    6 +-
 .../classfile/editor/MethodInvocationFixer.java    |    2 +-
 .../classfile/editor/NamedAttributeDeleter.java    |    2 +-
 .../ParameterAnnotationsAttributeEditor.java       |   71 +
 .../classfile/editor/StackSizeUpdater.java         |    2 +-
 src/proguard/classfile/editor/SubclassAdder.java   |    2 +-
 src/proguard/classfile/editor/SubclassToAdder.java |    2 +-
 src/proguard/classfile/editor/VariableCleaner.java |  135 ++
 src/proguard/classfile/editor/VariableEditor.java  |    2 +-
 .../classfile/editor/VariableRemapper.java         |    2 +-
 .../classfile/editor/VariableSizeUpdater.java      |    2 +-
 .../classfile/instruction/BranchInstruction.java   |    2 +-
 .../classfile/instruction/ConstantInstruction.java |    2 +-
 .../classfile/instruction/Instruction.java         |    4 +-
 .../instruction/InstructionConstants.java          |    2 +-
 .../classfile/instruction/InstructionFactory.java  |    2 +-
 .../classfile/instruction/InstructionUtil.java     |    2 +-
 .../instruction/LookUpSwitchInstruction.java       |    2 +-
 .../classfile/instruction/SimpleInstruction.java   |    2 +-
 .../classfile/instruction/SwitchInstruction.java   |    2 +-
 .../instruction/TableSwitchInstruction.java        |    2 +-
 .../classfile/instruction/VariableInstruction.java |   21 +-
 .../instruction/visitor/AllInstructionVisitor.java |    2 +-
 .../instruction/visitor/InstructionCounter.java    |    2 +-
 .../instruction/visitor/InstructionVisitor.java    |    2 +-
 .../visitor/MultiInstructionVisitor.java           |    2 +-
 src/proguard/classfile/io/LibraryClassReader.java  |    2 +-
 src/proguard/classfile/io/ProgramClassReader.java  |    2 +-
 src/proguard/classfile/io/ProgramClassWriter.java  |    6 +-
 src/proguard/classfile/io/RuntimeDataInput.java    |    2 +-
 src/proguard/classfile/io/RuntimeDataOutput.java   |    2 +-
 src/proguard/classfile/util/AccessUtil.java        |    2 +-
 .../classfile/util/ClassReferenceInitializer.java  |   88 +-
 .../util/ClassSubHierarchyInitializer.java         |    2 +-
 .../util/ClassSuperHierarchyInitializer.java       |   10 +-
 src/proguard/classfile/util/ClassUtil.java         |    2 +-
 .../classfile/util/DescriptorClassEnumeration.java |   20 +-
 .../util/DynamicClassReferenceInitializer.java     |   19 +-
 .../util/DynamicMemberReferenceInitializer.java    |   62 +-
 .../classfile/util/ExternalTypeEnumeration.java    |    2 +-
 .../classfile/util/InstructionSequenceMatcher.java |   26 +-
 .../classfile/util/InternalTypeEnumeration.java    |    2 +-
 src/proguard/classfile/util/MemberFinder.java      |    2 +-
 src/proguard/classfile/util/MethodLinker.java      |    2 +-
 src/proguard/classfile/util/SimplifiedVisitor.java |    2 +-
 .../classfile/util/StringReferenceInitializer.java |   89 +
 src/proguard/classfile/util/StringSharer.java      |    2 +-
 src/proguard/classfile/util/WarningPrinter.java    |   72 +-
 .../classfile/visitor/AllClassVisitor.java         |    2 +-
 .../classfile/visitor/AllFieldVisitor.java         |    2 +-
 .../classfile/visitor/AllMemberVisitor.java        |    2 +-
 .../classfile/visitor/AllMethodVisitor.java        |    2 +-
 .../classfile/visitor/BottomClassFilter.java       |    2 +-
 .../classfile/visitor/ClassAccessFilter.java       |    2 +-
 src/proguard/classfile/visitor/ClassCleaner.java   |    2 +-
 src/proguard/classfile/visitor/ClassCollector.java |    2 +-
 src/proguard/classfile/visitor/ClassCounter.java   |    2 +-
 .../visitor/ClassForNameClassVisitor.java          |    2 +-
 .../classfile/visitor/ClassHierarchyTraveler.java  |    2 +-
 .../classfile/visitor/ClassNameFilter.java         |   19 +-
 .../classfile/visitor/ClassPoolFiller.java         |    2 +-
 .../classfile/visitor/ClassPoolVisitor.java        |    2 +-
 .../classfile/visitor/ClassPresenceFilter.java     |    2 +-
 src/proguard/classfile/visitor/ClassPrinter.java   |   17 +-
 .../classfile/visitor/ClassVersionFilter.java      |    2 +-
 .../classfile/visitor/ClassVersionSetter.java      |    2 +-
 src/proguard/classfile/visitor/ClassVisitor.java   |    2 +-
 .../visitor/ConcreteClassDownTraveler.java         |    2 +-
 .../classfile/visitor/DotClassClassVisitor.java    |    2 +-
 .../classfile/visitor/ExceptClassFilter.java       |    2 +-
 .../classfile/visitor/ExceptClassesFilter.java     |    2 +-
 .../classfile/visitor/ExceptionCounter.java        |    2 +-
 .../visitor/ExceptionExcludedOffsetFilter.java     |    4 +-
 ...r.java => ExceptionHandlerConstantVisitor.java} |   28 +-
 ...angeFilter.java => ExceptionHandlerFilter.java} |   24 +-
 .../classfile/visitor/ExceptionOffsetFilter.java   |    4 +-
 .../classfile/visitor/ExceptionRangeFilter.java    |    2 +-
 .../visitor/ImplementedClassConstantFilter.java    |    2 +-
 .../classfile/visitor/ImplementedClassFilter.java  |    2 +-
 .../visitor/ImplementingClassConstantFilter.java   |    2 +-
 .../classfile/visitor/LibraryClassFilter.java      |    2 +-
 .../classfile/visitor/LibraryMemberFilter.java     |    2 +-
 .../classfile/visitor/MemberAccessFilter.java      |    2 +-
 .../classfile/visitor/MemberClassAccessFilter.java |    2 +-
 .../classfile/visitor/MemberCollector.java         |    2 +-
 src/proguard/classfile/visitor/MemberCounter.java  |    2 +-
 .../classfile/visitor/MemberDescriptorFilter.java  |    2 +-
 .../classfile/visitor/MemberNameFilter.java        |    2 +-
 .../classfile/visitor/MemberToClassVisitor.java    |    2 +-
 src/proguard/classfile/visitor/MemberVisitor.java  |    2 +-
 .../visitor/MethodImplementationFilter.java        |    2 +-
 .../visitor/MethodImplementationTraveler.java      |    2 +-
 .../classfile/visitor/MultiClassPoolVisitor.java   |    2 +-
 .../classfile/visitor/MultiClassVisitor.java       |    2 +-
 .../classfile/visitor/MultiMemberVisitor.java      |    2 +-
 .../classfile/visitor/NamedClassVisitor.java       |    2 +-
 .../classfile/visitor/NamedFieldVisitor.java       |    2 +-
 .../classfile/visitor/NamedMethodVisitor.java      |    2 +-
 .../classfile/visitor/ProgramClassFilter.java      |    2 +-
 .../classfile/visitor/ProgramMemberFilter.java     |    2 +-
 .../classfile/visitor/ReferencedClassVisitor.java  |    5 +-
 .../classfile/visitor/ReferencedMemberVisitor.java |    2 +-
 .../classfile/visitor/SimilarMemberVisitor.java    |    2 +-
 .../classfile/visitor/SimpleClassPrinter.java      |    2 +-
 src/proguard/classfile/visitor/SubclassFilter.java |    2 +-
 .../classfile/visitor/SubclassTraveler.java        |    2 +-
 .../classfile/visitor/VariableClassVisitor.java    |    2 +-
 .../classfile/visitor/VariableMemberVisitor.java   |    2 +-
 src/proguard/evaluation/BasicBranchUnit.java       |    2 +-
 src/proguard/evaluation/BasicInvocationUnit.java   |    6 +-
 src/proguard/evaluation/BranchUnit.java            |    2 +-
 src/proguard/evaluation/InvocationUnit.java        |    2 +-
 src/proguard/evaluation/Processor.java             |    2 +-
 src/proguard/evaluation/Stack.java                 |    2 +-
 src/proguard/evaluation/TracedStack.java           |    2 +-
 src/proguard/evaluation/TracedVariables.java       |    2 +-
 src/proguard/evaluation/Variables.java             |    2 +-
 src/proguard/evaluation/value/Category1Value.java  |    2 +-
 src/proguard/evaluation/value/Category2Value.java  |    2 +-
 src/proguard/evaluation/value/ComparisonValue.java |    4 +-
 .../evaluation/value/CompositeDoubleValue.java     |    2 +-
 .../evaluation/value/CompositeFloatValue.java      |    2 +-
 .../evaluation/value/CompositeIntegerValue.java    |    2 +-
 .../evaluation/value/CompositeLongValue.java       |    2 +-
 .../evaluation/value/ConvertedByteValue.java       |    2 +-
 .../evaluation/value/ConvertedCharacterValue.java  |    2 +-
 .../evaluation/value/ConvertedDoubleValue.java     |    2 +-
 .../evaluation/value/ConvertedFloatValue.java      |    2 +-
 .../evaluation/value/ConvertedIntegerValue.java    |    2 +-
 .../evaluation/value/ConvertedLongValue.java       |    2 +-
 .../evaluation/value/ConvertedShortValue.java      |    2 +-
 src/proguard/evaluation/value/DoubleValue.java     |    2 +-
 src/proguard/evaluation/value/FloatValue.java      |    2 +-
 .../evaluation/value/IdentifiedDoubleValue.java    |    2 +-
 .../evaluation/value/IdentifiedFloatValue.java     |    2 +-
 .../evaluation/value/IdentifiedIntegerValue.java   |    2 +-
 .../evaluation/value/IdentifiedLongValue.java      |    2 +-
 .../evaluation/value/IdentifiedReferenceValue.java |    2 +-
 .../evaluation/value/IdentifiedValueFactory.java   |    2 +-
 .../evaluation/value/InstructionOffsetValue.java   |    2 +-
 src/proguard/evaluation/value/IntegerValue.java    |    2 +-
 src/proguard/evaluation/value/LongValue.java       |    2 +-
 .../evaluation/value/NegatedDoubleValue.java       |    2 +-
 .../evaluation/value/NegatedFloatValue.java        |    2 +-
 .../evaluation/value/NegatedIntegerValue.java      |    2 +-
 .../evaluation/value/NegatedLongValue.java         |    2 +-
 .../evaluation/value/ParticularDoubleValue.java    |    2 +-
 .../evaluation/value/ParticularFloatValue.java     |    2 +-
 .../evaluation/value/ParticularIntegerValue.java   |    2 +-
 .../evaluation/value/ParticularLongValue.java      |    2 +-
 src/proguard/evaluation/value/ReferenceValue.java  |    4 +-
 .../evaluation/value/SpecificDoubleValue.java      |    2 +-
 .../evaluation/value/SpecificFloatValue.java       |    2 +-
 .../evaluation/value/SpecificIntegerValue.java     |    2 +-
 .../evaluation/value/SpecificLongValue.java        |    2 +-
 .../evaluation/value/SpecificValueFactory.java     |    2 +-
 src/proguard/evaluation/value/TopValue.java        |    2 +-
 .../evaluation/value/UnknownDoubleValue.java       |    2 +-
 .../evaluation/value/UnknownFloatValue.java        |    2 +-
 .../evaluation/value/UnknownIntegerValue.java      |    2 +-
 .../evaluation/value/UnknownLongValue.java         |    2 +-
 src/proguard/evaluation/value/Value.java           |    2 +-
 src/proguard/evaluation/value/ValueFactory.java    |    2 +-
 src/proguard/gui/ClassPathPanel.java               |   15 +-
 src/proguard/gui/ClassSpecificationDialog.java     |   34 +-
 src/proguard/gui/ClassSpecificationsPanel.java     |    2 +-
 src/proguard/gui/ExtensionFileFilter.java          |    2 +-
 src/proguard/gui/FilterBuilder.java                |  208 ++
 src/proguard/gui/FilterDialog.java                 |   45 +-
 src/proguard/gui/GUIResources.java                 |    2 +-
 src/proguard/gui/GUIResources.properties           |  210 +-
 src/proguard/gui/KeepSpecificationsPanel.java      |    8 +-
 src/proguard/gui/ListPanel.java                    |    2 +-
 src/proguard/gui/MemberSpecificationDialog.java    |    8 +-
 src/proguard/gui/MemberSpecificationsPanel.java    |    2 +-
 src/proguard/gui/MessageDialogRunnable.java        |   18 +-
 src/proguard/gui/OptimizationsDialog.java          |  251 ++
 src/proguard/gui/ProGuardGUI.java                  |  270 ++-
 src/proguard/gui/ProGuardRunnable.java             |    2 +-
 src/proguard/gui/ReTraceRunnable.java              |    5 +-
 src/proguard/gui/SwingUtil.java                    |    6 +-
 src/proguard/gui/TabbedPane.java                   |   19 +-
 src/proguard/gui/TextAreaOutputStream.java         |   14 +-
 src/proguard/gui/splash/BufferedSprite.java        |    2 +-
 src/proguard/gui/splash/CircleSprite.java          |    2 +-
 src/proguard/gui/splash/ClipSprite.java            |    2 +-
 src/proguard/gui/splash/ColorSprite.java           |    2 +-
 src/proguard/gui/splash/CompositeSprite.java       |    2 +-
 src/proguard/gui/splash/ConstantColor.java         |    2 +-
 src/proguard/gui/splash/ConstantDouble.java        |    2 +-
 src/proguard/gui/splash/ConstantFont.java          |    2 +-
 src/proguard/gui/splash/ConstantInt.java           |    2 +-
 src/proguard/gui/splash/ConstantString.java        |    2 +-
 src/proguard/gui/splash/ConstantTiming.java        |    2 +-
 src/proguard/gui/splash/FontSprite.java            |    2 +-
 src/proguard/gui/splash/ImageSprite.java           |    2 +-
 src/proguard/gui/splash/LinearColor.java           |    2 +-
 src/proguard/gui/splash/LinearDouble.java          |    2 +-
 src/proguard/gui/splash/LinearInt.java             |    2 +-
 src/proguard/gui/splash/LinearTiming.java          |    2 +-
 src/proguard/gui/splash/OverrideGraphics2D.java    |    2 +-
 src/proguard/gui/splash/RectangleSprite.java       |    2 +-
 src/proguard/gui/splash/SawToothTiming.java        |    2 +-
 src/proguard/gui/splash/ShadowedSprite.java        |    2 +-
 src/proguard/gui/splash/SineTiming.java            |    2 +-
 src/proguard/gui/splash/SmoothTiming.java          |    2 +-
 src/proguard/gui/splash/SplashPanel.java           |    8 +-
 src/proguard/gui/splash/Sprite.java                |    2 +-
 src/proguard/gui/splash/TextSprite.java            |    2 +-
 src/proguard/gui/splash/TimeSwitchSprite.java      |    2 +-
 src/proguard/gui/splash/Timing.java                |    2 +-
 src/proguard/gui/splash/TypeWriterString.java      |    2 +-
 src/proguard/gui/splash/VariableColor.java         |    2 +-
 src/proguard/gui/splash/VariableDouble.java        |    2 +-
 src/proguard/gui/splash/VariableFont.java          |    2 +-
 src/proguard/gui/splash/VariableInt.java           |    2 +-
 src/proguard/gui/splash/VariableSizeFont.java      |    2 +-
 src/proguard/gui/splash/VariableString.java        |    2 +-
 src/proguard/gui/vtitle.gif                        |  Bin 26541 -> 0 bytes
 src/proguard/gui/vtitle.png                        |  Bin 0 -> 23313 bytes
 src/proguard/io/CascadingDataEntryWriter.java      |   12 +-
 src/proguard/io/ClassFilter.java                   |   25 +-
 src/proguard/io/ClassReader.java                   |    5 +-
 src/proguard/io/ClassRewriter.java                 |    2 +-
 src/proguard/io/DataEntry.java                     |   11 +-
 src/proguard/io/DataEntryCopier.java               |   31 +-
 ...ryReader.java => DataEntryDirectoryFilter.java} |   24 +-
 src/proguard/io/DataEntryFilter.java               |    2 +-
 src/proguard/io/DataEntryNameFilter.java           |    2 +-
 ...aEntryRenamer.java => DataEntryObfuscator.java} |   52 +-
 src/proguard/io/DataEntryParentFilter.java         |    2 +-
 src/proguard/io/DataEntryPump.java                 |    2 +-
 src/proguard/io/DataEntryReader.java               |    2 +-
 src/proguard/io/DataEntryRenamer.java              |  113 +-
 src/proguard/io/DataEntryRewriter.java             |   58 +-
 src/proguard/io/DataEntryWriter.java               |   10 +-
 .../io/{ClassFilter.java => DirectoryFilter.java}  |   38 +-
 src/proguard/io/DirectoryPump.java                 |    9 +-
 src/proguard/io/DirectoryWriter.java               |   28 +-
 src/proguard/io/FileDataEntry.java                 |    8 +-
 src/proguard/io/FilteredDataEntryReader.java       |    2 +-
 src/proguard/io/FilteredDataEntryWriter.java       |   15 +-
 src/proguard/io/Finisher.java                      |    2 +-
 src/proguard/io/JarReader.java                     |   13 +-
 src/proguard/io/JarWriter.java                     |  108 +-
 src/proguard/io/ManifestRewriter.java              |  216 ++
 src/proguard/io/NameFilter.java                    |   83 +
 src/proguard/io/ParentDataEntryWriter.java         |    9 +-
 src/proguard/io/RenamedDataEntry.java              |    8 +-
 src/proguard/io/ZipDataEntry.java                  |   21 +-
 src/proguard/obfuscate/AttributeShrinker.java      |    2 +-
 src/proguard/obfuscate/AttributeUsageMarker.java   |    2 +-
 src/proguard/obfuscate/ClassObfuscator.java        |  145 +-
 src/proguard/obfuscate/ClassRenamer.java           |    2 +-
 src/proguard/obfuscate/DictionaryNameFactory.java  |    2 +-
 src/proguard/obfuscate/MapCleaner.java             |    2 +-
 src/proguard/obfuscate/MappingKeeper.java          |   23 +-
 src/proguard/obfuscate/MappingPrinter.java         |    2 +-
 src/proguard/obfuscate/MappingProcessor.java       |   23 +-
 src/proguard/obfuscate/MappingReader.java          |   67 +-
 src/proguard/obfuscate/MemberNameCleaner.java      |    2 +-
 src/proguard/obfuscate/MemberNameCollector.java    |    2 +-
 .../obfuscate/MemberNameConflictFixer.java         |    5 +-
 src/proguard/obfuscate/MemberObfuscator.java       |    2 +-
 .../obfuscate/MemberSpecialNameFilter.java         |    2 +-
 src/proguard/obfuscate/MultiMappingProcessor.java  |    8 +-
 src/proguard/obfuscate/NameAndTypeShrinker.java    |    2 +-
 src/proguard/obfuscate/NameAndTypeUsageMarker.java |    2 +-
 src/proguard/obfuscate/NameFactory.java            |    2 +-
 src/proguard/obfuscate/NameFactoryResetter.java    |    2 +-
 src/proguard/obfuscate/NameMarker.java             |    2 +-
 .../{NameFactory.java => NumericNameFactory.java}  |   29 +-
 src/proguard/obfuscate/Obfuscator.java             |   97 +-
 src/proguard/obfuscate/SimpleNameFactory.java      |    2 +-
 src/proguard/obfuscate/SourceFileRenamer.java      |    8 +-
 src/proguard/obfuscate/SpecialNameFactory.java     |    2 +-
 src/proguard/obfuscate/Utf8Shrinker.java           |    2 +-
 src/proguard/obfuscate/Utf8UsageMarker.java        |    2 +-
 src/proguard/optimize/ChangedCodePrinter.java      |    2 +-
 src/proguard/optimize/ConstantMemberFilter.java    |    4 +-
 src/proguard/optimize/ConstantParameterFilter.java |   79 +
 .../optimize/DuplicateInitializerFixer.java        |    2 +-
 .../DuplicateInitializerInvocationFixer.java       |    2 +-
 src/proguard/optimize/KeepMarker.java              |    2 +-
 .../optimize/MemberDescriptorSpecializer.java      |    2 +-
 .../optimize/MethodDescriptorShrinker.java         |   17 +-
 src/proguard/optimize/MethodStaticizer.java        |    2 +-
 .../optimize/OptimizationInfoMemberFilter.java     |    2 +-
 src/proguard/optimize/Optimizer.java               |  978 +++++---
 src/proguard/optimize/ParameterShrinker.java       |   40 +-
 src/proguard/optimize/TailRecursionSimplifier.java |   10 +-
 src/proguard/optimize/WriteOnlyFieldFilter.java    |    2 +-
 ...tionSimplifier.java => EvaluationShrinker.java} | 1128 ++-------
 .../optimize/evaluation/EvaluationSimplifier.java  | 2564 ++++----------------
 .../optimize/evaluation/LivenessAnalyzer.java      |    8 +-
 .../optimize/evaluation/LoadingInvocationUnit.java |   88 +-
 .../optimize/evaluation/PartialEvaluator.java      |  555 +++--
 .../optimize/evaluation/StoringInvocationUnit.java |   62 +-
 .../optimize/evaluation/TracedBranchUnit.java      |    2 +-
 .../optimize/evaluation/VariableOptimizer.java     |   33 +-
 src/proguard/optimize/info/AccessMethodMarker.java |    2 +-
 .../optimize/info/BackwardBranchMarker.java        |    2 +-
 .../optimize/info/CatchExceptionMarker.java        |    2 +-
 ...{DotClassFilter.java => CaughtClassFilter.java} |   14 +-
 ...{DotClassFilter.java => CaughtClassMarker.java} |   34 +-
 .../optimize/info/ClassOptimizationInfo.java       |   26 +-
 .../optimize/info/ClassOptimizationInfoSetter.java |    2 +-
 src/proguard/optimize/info/DotClassFilter.java     |    2 +-
 src/proguard/optimize/info/DotClassMarker.java     |    4 +-
 .../optimize/info/ExceptionInstructionChecker.java |    2 +-
 .../optimize/info/FieldOptimizationInfo.java       |    2 +-
 .../optimize/info/InstanceofClassFilter.java       |    2 +-
 .../optimize/info/InstanceofClassMarker.java       |    4 +-
 .../optimize/info/InstantiationClassFilter.java    |    2 +-
 .../optimize/info/InstantiationClassMarker.java    |    4 +-
 .../info/MemberOptimizationInfoSetter.java         |    2 +-
 .../optimize/info/MethodInvocationMarker.java      |    2 +-
 .../optimize/info/MethodOptimizationInfo.java      |   31 +-
 .../optimize/info/NoSideEffectMethodMarker.java    |    2 +-
 .../optimize/info/NonPrivateMemberMarker.java      |    2 +-
 .../PackageVisibleMemberContainingClassMarker.java |    4 +-
 .../PackageVisibleMemberInvokingClassMarker.java   |    4 +-
 .../optimize/info/ParameterUsageMarker.java        |  170 +-
 .../optimize/info/ReadWriteFieldMarker.java        |    6 +-
 .../info/SideEffectInstructionChecker.java         |    2 +-
 .../optimize/info/SideEffectMethodMarker.java      |    2 +-
 .../optimize/info/SuperInvocationMarker.java       |    2 +-
 .../optimize/info/VariableUsageMarker.java         |    2 +-
 .../optimize/peephole/BranchTargetFinder.java      |    2 +-
 src/proguard/optimize/peephole/ClassFinalizer.java |   63 +-
 src/proguard/optimize/peephole/ClassMerger.java    |   58 +-
 .../optimize/peephole/GotoCommonCodeReplacer.java  |    8 +-
 .../optimize/peephole/GotoGotoReplacer.java        |    2 +-
 .../optimize/peephole/GotoReturnReplacer.java      |    2 +-
 .../optimize/peephole/HorizontalClassMerger.java   |    2 +-
 .../peephole/InstructionSequenceConstants.java     |  255 +-
 .../peephole/InstructionSequenceReplacer.java      |    2 +-
 .../peephole/InstructionSequencesReplacer.java     |    2 +-
 .../optimize/peephole/LoadStoreRemover.java        |  123 -
 .../optimize/peephole/MemberPrivatizer.java        |   33 +-
 .../{ClassFinalizer.java => MethodFinalizer.java}  |   54 +-
 src/proguard/optimize/peephole/MethodInliner.java  |   15 +-
 src/proguard/optimize/peephole/NopRemover.java     |    2 +-
 .../optimize/peephole/PeepholeOptimizer.java       |    2 +-
 src/proguard/optimize/peephole/PushPopRemover.java |  168 --
 .../optimize/peephole/ReachableCodeMarker.java     |    2 +-
 .../RetargetedInnerClassAttributeRemover.java      |    2 +-
 .../optimize/peephole/StoreLoadReplacer.java       |  141 --
 .../optimize/peephole/TargetClassChanger.java      |    2 +-
 .../optimize/peephole/UnreachableCodeRemover.java  |    2 +-
 .../peephole/UnreachableExceptionRemover.java      |    2 +-
 .../optimize/peephole/VariableShrinker.java        |    4 +-
 .../optimize/peephole/VerticalClassMerger.java     |    2 +-
 src/proguard/preverify/CodePreverifier.java        |   38 +-
 src/proguard/preverify/CodeSubroutineInliner.java  |   42 +-
 src/proguard/preverify/Preverifier.java            |    2 +-
 src/proguard/preverify/SubroutineInliner.java      |    2 +-
 src/proguard/retrace/ReTrace.java                  |  664 ++++-
 src/proguard/retrace/StackTrace.java               |  178 --
 src/proguard/retrace/StackTraceItem.java           |  291 ---
 src/proguard/shrink/AnnotationUsageMarker.java     |    2 +-
 src/proguard/shrink/ClassShrinker.java             |    2 +-
 src/proguard/shrink/InnerUsageMarker.java          |    2 +-
 src/proguard/shrink/InterfaceUsageMarker.java      |    2 +-
 src/proguard/shrink/ShortestUsageMark.java         |    2 +-
 src/proguard/shrink/ShortestUsageMarker.java       |    2 +-
 src/proguard/shrink/ShortestUsagePrinter.java      |    2 +-
 src/proguard/shrink/Shrinker.java                  |    2 +-
 src/proguard/shrink/UsageMarker.java               |    2 +-
 src/proguard/shrink/UsagePrinter.java              |    2 +-
 src/proguard/shrink/UsedClassFilter.java           |    2 +-
 src/proguard/shrink/UsedMemberFilter.java          |    2 +-
 src/proguard/util/AndMatcher.java                  |    2 +-
 src/proguard/util/ClassNameParser.java             |    2 +-
 .../util/{NotMatcher.java => ConstantMatcher.java} |   20 +-
 src/proguard/util/EmptyStringMatcher.java          |    2 +-
 src/proguard/util/ExtensionMatcher.java            |    2 +-
 src/proguard/util/FileNameParser.java              |    2 +-
 src/proguard/util/FixedStringMatcher.java          |    2 +-
 src/proguard/util/ListMatcher.java                 |    2 +-
 src/proguard/util/ListParser.java                  |   14 +-
 src/proguard/util/ListUtil.java                    |  100 +-
 src/proguard/util/NameParser.java                  |    2 +-
 src/proguard/util/NotMatcher.java                  |    2 +-
 src/proguard/util/OrMatcher.java                   |    2 +-
 src/proguard/util/SettableMatcher.java             |    2 +-
 src/proguard/util/StringMatcher.java               |    2 +-
 src/proguard/util/StringParser.java                |    2 +-
 src/proguard/util/VariableStringMatcher.java       |    2 +-
 src/proguard/wtk/ProGuardObfuscator.java           |    2 +-
 611 files changed, 8643 insertions(+), 6954 deletions(-)

diff --git a/README b/README
index 6946bd0..8766da5 100644
--- a/README
+++ b/README
@@ -30,4 +30,4 @@ Enjoy!
 
 http://proguard.sourceforge.net/
 
-Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
diff --git a/build/README b/build/README
deleted file mode 100644
index fa5f5a4..0000000
--- a/build/README
+++ /dev/null
@@ -1,34 +0,0 @@
-ProGuard, Java class file shrinker, optimizer, obfuscator, and preverifier
-==========================================================================
-
-This directory contains a number of alternative ways to build ProGuard:
-
-- build.sh  : a shell script for GNU/Linux
-- makefile  : a makefile for GNU/Linux
-- build.xml : an Ant build file for all platforms
-
-- As a final alternative, you can also easily compile the code from the
-  command line:
-
-    mkdir classes
-    javac -sourcepath src -d classes src/proguard/ProGuard.java
-    javac -sourcepath src -d classes src/proguard/gui/ProGuardGUI.java
-    javac -sourcepath src -d classes src/proguard/retrace/ReTrace.java
-
-  For the ProGuard Ant task:
-
-    javac -sourcepath src -d classes -classpath lib/ant.jar \
-        src/proguard/ant/ProGuardTask.java
-
-  For the Java Micro Edition Wireless Tool Kit (JME WTK) obfuscator plug-in:
-
-    javac -sourcepath src -d classes -classpath wtklib/kenv.zip \
-        src/proguard/wtk/ProGuardObfuscator.java
-
-Note that you'll have to install Ant and the JME WTK yourself.
-
-Enjoy!
-
-http://proguard.sourceforge.net/
-
-Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
diff --git a/build/build.sh b/build/build.sh
index b829aa1..eb5fcf7 100755
--- a/build/build.sh
+++ b/build/build.sh
@@ -2,6 +2,10 @@
 #
 # GNU/Linux build script for ProGuard.
 
+#
+# Configuration.
+#
+
 ANT_HOME=${ANT_HOME:-/usr/local/java/ant}
 WTK_HOME=${WTK_HOME:-/usr/local/java/wtk}
 
@@ -23,12 +27,16 @@ ANT_TASK=proguard/ant/ProGuardTask
 WTK_PLUGIN=proguard/wtk/ProGuardObfuscator
 
 ANT_JAR=$ANT_HOME/lib/ant.jar
-WTK_JAR=$WTK_HOME/wtklib/kenv.jar
+WTK_JAR=$WTK_HOME/wtklib/kenv.zip
 
 PROGUARD_JAR=$LIB/proguard.jar
 PROGUARD_GUI_JAR=$LIB/proguardgui.jar
 RETRACE_JAR=$LIB/retrace.jar
 
+#
+# Function definitions.
+#
+
 function compile {
   # Compile java source files.
   echo "Compiling ${1//\//.} ..."
@@ -38,7 +46,7 @@ function compile {
 
   # Copy resource files.
   (cd "$SRC"; find $(dirname $1) -maxdepth 1 \
-     \( -name \*.properties -o -name \*.gif -o -name \*.pro \) \
+     \( -name \*.properties -o -name \*.png -o -name \*.gif -o -name \*.pro \) \
      -exec cp --parents {} "../$CLASSES" \; )
 }
 
@@ -52,6 +60,12 @@ function updatejar {
   jar -uf "$PROGUARD_JAR" -C "$CLASSES" $(dirname $1)
 }
 
+#
+# Main script body.
+#
+
+mkdir -p "$CLASSES"
+
 compile   $PROGUARD
 createjar $PROGUARD "$PROGUARD_JAR"
 
diff --git a/build/build.xml b/build/build.xml
index 3b5dd71..a592945 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -28,6 +28,7 @@
     <copy todir = "${classes}">
       <fileset dir = "${src}">
         <include name = "proguard/*.properties"/>
+        <include name = "proguard/*.png"/>
         <include name = "proguard/*.gif"/>
         <include name = "proguard/*.pro"/>
       </fileset>
@@ -51,6 +52,7 @@
     <copy todir = "${classes}">
       <fileset dir = "${src}">
         <include name = "proguard/gui/*.properties"/>
+        <include name = "proguard/gui/*.png"/>
         <include name = "proguard/gui/*.gif"/>
         <include name = "proguard/gui/*.pro"/>
       </fileset>
@@ -74,6 +76,7 @@
     <copy todir = "${classes}">
       <fileset dir = "${src}">
         <include name = "proguard/retrace/*.properties"/>
+        <include name = "proguard/retrace/*.png"/>
         <include name = "proguard/retrace/*.gif"/>
         <include name = "proguard/retrace/*.pro"/>
       </fileset>
@@ -106,6 +109,7 @@
     <copy todir = "${classes}">
       <fileset dir = "${src}">
         <include name = "proguard/ant/*.properties"/>
+        <include name = "proguard/ant/*.png"/>
         <include name = "proguard/ant/*.gif"/>
         <include name = "proguard/ant/*.pro"/>
       </fileset>
@@ -138,6 +142,7 @@
     <copy todir = "${classes}">
       <fileset dir = "${src}">
         <include name = "proguard/wtk/*.properties"/>
+        <include name = "proguard/wtk/*.png"/>
         <include name = "proguard/wtk/*.gif"/>
         <include name = "proguard/wtk/*.pro"/>
       </fileset>
diff --git a/build/makefile b/build/makefile
index bb4af4f..0508eb4 100644
--- a/build/makefile
+++ b/build/makefile
@@ -66,7 +66,7 @@ clean:
 
 
 define RESOURCES
-  $(shell find $(SRC)/$(dir $(1)) -maxdepth 1 \( -name \*.properties -o -name \*.gif -o -name \*.pro \) -printf $(CLASSES)/$(dir $(1))%P\\n)
+  $(shell find $(SRC)/$(dir $(1)) -maxdepth 1 \( -name \*.properties -o -name \*.png -o -name \*.gif -o -name \*.pro \) -printf $(CLASSES)/$(dir $(1))%P\\n)
 endef
 
 define TARGETRULE
@@ -81,7 +81,7 @@ $(CLASSES) $(LIB):
 $(CLASSES)/%.class: $(SRC)/%.java
 	javac $(JAVAC_OPTIONS) $^
 
-$(CLASSES)/%.properties $(CLASSES)/%.gif $(CLASSES)/%.pro:
+$(CLASSES)/%.properties $(CLASSES)/%.png $(CLASSES)/%.gif $(CLASSES)/%.pro:
 	cp $(subst $(CLASSES),$(SRC),$@) $@
 
 %.jar %.zip:
diff --git a/docs/FAQ.html b/docs/FAQ.html
index 04897dc..8f19b15 100644
--- a/docs/FAQ.html
+++ b/docs/FAQ.html
@@ -247,7 +247,7 @@ Manual</a> for more details.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html
index 0955cf8..0a44d66 100644
--- a/docs/GPL_exception.html
+++ b/docs/GPL_exception.html
@@ -7,7 +7,7 @@
 <H1>Special Exception to the GNU General Public License</H1>
 
 <P>
-Copyright © 2002-2008 Eric Lafortune
+Copyright © 2002-2009 Eric Lafortune
 </P>
 
 <P>
@@ -35,6 +35,7 @@ the code of this program with the following stand-alone applications:
 <ul>
 <li>Apache Ant,
 <li>Apache Maven,
+<li>the Eclipse ProGuardDT GUI,
 <li>the EclipseME JME IDE,
 <li>the Sun NetBeans Java IDE,
 <li>the Sun JME Wireless Toolkit, and
diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html
index 5713ae1..af19461 100644
--- a/docs/acknowledgements.html
+++ b/docs/acknowledgements.html
@@ -32,7 +32,8 @@ 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, Andrew Wilson, Sean Owen, Niels Gron,
+Eduard Welch, Dawid Weiss, Andrew Wilson, Sean Owen, Niels Gron, Ishan Mehta,
+Steven Adams, Xavier Kral,
 and many others. Thanks! Your feedback has been invaluable.
 <p>
 
@@ -59,7 +60,7 @@ href="http://www.javadocking.com/" target="other">Java Docking Library</a>.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/alternatives.html b/docs/alternatives.html
index 7a09b34..fa5db0c 100644
--- a/docs/alternatives.html
+++ b/docs/alternatives.html
@@ -214,6 +214,16 @@ below is incorrect.
 </tr>
 
 <tr>
+<td><a target="other" rel="nofollow" href="http://www.sable.mcgill.ca/">Sable</a></td>
+<td><a target="other" href="http://www.sable.mcgill.ca/JBCO/">JBCO</a></td>
+<td align="center"><br></td>
+<td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
+<td>Free (LGPL)</td>
+</tr>
+
+<tr>
 <td><a target="other" rel="nofollow" href="http://sourceforge.net/users/glurk/">Thorsten Heit</a></td>
 <td><a target="other" href="http://sourceforge.net/projects/javaguard/">JavaGuard</a></td>
 <td align="center"><br></td>
@@ -639,7 +649,7 @@ All trademarks are property of their respective holders.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/downloads.html b/docs/downloads.html
index dfaee8d..ebd1b28 100644
--- a/docs/downloads.html
+++ b/docs/downloads.html
@@ -15,7 +15,8 @@
 License. Please consult the <a href="license.html">license page</a> for more
 details.
 <p>
-<b>ProGuard</b> is written in Java. It requires a Java 2 runtime environment.
+<b>ProGuard</b> is written in Java, so it requires a Java Runtime Environment
+   (JRE 1.4 or higher).
 <p>
 You can download the latest release (containing the program jar, the
 documentation you're reading now, examples, and the source code) from this
@@ -43,12 +44,52 @@ interest too, because they typically contain any less urgent bug fixes
 collected since the previous release.
 <p>
 
+<h3><div>Jul 2009</div> Version 4.4</h3>
+<ul>
+<li>Added new peephole optimizations.
+<li>Added option <code>-optimizations</code> for fine-grained configuration of
+    optimizations.
+<li>Added option <code>-adaptclassstrings</code> for adapting string constants
+    that correspond to obfuscated classes.
+<li>Added option <code>-keeppackagenames</code> for keeping specified package
+    names from being obfuscated.
+<li>Added option <code>-keepdirectories</code> for keeping specified directory
+    entries in output jars.
+<li>Extended options <code>-dontnote</code> and <code>-dontwarn</code> for
+    fine-grained configuration of notes and warnings. 
+<li>Added option <code>-regex</code> in ReTrace, for specifying alternative
+    regular expressions to parse stack traces.
+<li>Extended renaming of resource files based on obfuscation.
+<li>Improved inlining of constant parameters and removal of unused parameters.
+<li>Avoiding bug in IBM's JVM for JSE, in optimization step.
+<li>Avoiding ArrayIndexOutOfBoundsException in optimization step.
+<li>Fixed configuration with annotations that are not preserved themselves.
+<li>Fixed preverification of invocations of super constructors with arguments
+    containing ternary operators.
+<li>Fixed processing of unreachable exception handlers.
+<li>Fixed merging of exception classes.
+<li>Fixed repeated method inlining.
+<li>Fixed inlining of finally blocks surrounded by large try blocks, compiled
+    with JDK 1.4 or earlier.
+<li>Fixed optimization of complex finally blocks, compiled with JDK 1.4 or
+    earlier.
+<li>Fixed obfuscation of anonymous class names, if <code>EnclosingMethod</code>
+    attributes are being kept.
+<li>Fixed obfuscation of inner class names in generic types.
+<li>Fixed decoding of UTF-8 strings containing special characters.
+<li>Fixed copying of debug information and annotations when merging classes.
+<li>Fixed writing out of unknown attributes.
+<li>Fixed updating manifest files with split lines.
+<li>Updated documentation and examples.
+</ul>
+
 <h3><div>Dec 2008</div> Version 4.3</h3>
 <ul>
 <li>Added class merging.
 <li>Added static single assignment analysis.
 <li>Added support for annotation and enumeration class types in configuration.
-<li>Refined shrinking of fields in case of unusual -keepclassmembers options.
+<li>Refined shrinking of fields in case of unusual
+    <code>-keepclassmembers</code> options.
 <li>Added simplification of tail recursion calls.
 <li>Added new peephole optimizations.
 <li>Fixed optimization of unused variable initializations causing negative
@@ -472,7 +513,7 @@ Upgrade considerations:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/feedback.html b/docs/feedback.html
index 60bc4a9..a7e770b 100644
--- a/docs/feedback.html
+++ b/docs/feedback.html
@@ -98,7 +98,7 @@ projects.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/index.html b/docs/index.html
index 8787906..771bb6a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -64,7 +64,7 @@ You can go straight to the <a href="main.html">main page</a>.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/license.html b/docs/license.html
index b393294..b4654fd 100644
--- a/docs/license.html
+++ b/docs/license.html
@@ -26,11 +26,12 @@ the complete source code available as well. If you develop a program that is
 linked with
 <b>ProGuard</b>, the program as a whole has to be distributed at no charge
 under the GPL. I am granting a <a href="GPL_exception.html">special
-exception</a> to the latter clause (in wording suggested by the <a
-href="http://www.gnu.org/copyleft/gpl-faq.html#GPLIncompatibleLibs"
+exception</a> to the latter clause (in wording suggested by
+the <a href="http://www.gnu.org/copyleft/gpl-faq.html#GPLIncompatibleLibs"
 target="other">FSF</a>), for combinations with the following stand-alone
-applications: Apache Ant, Apache Maven, the EclipseME JME IDE, the Sun NetBeans
-Java IDE, the Sun JME Wireless Toolkit, and the Javaground Tools.
+applications: Apache Ant, Apache Maven, the Eclipse ProGuardDT GUI, the
+EclipseME JME IDE, the Sun NetBeans Java IDE, the Sun JME Wireless Toolkit,
+and the Javaground Tools.
 
 <p>
 The <b>ProGuard user documentation</b> represents an important part of this
@@ -39,7 +40,7 @@ version of the code.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/main.html b/docs/main.html
index 2e4e117..e988c79 100644
--- a/docs/main.html
+++ b/docs/main.html
@@ -85,7 +85,7 @@ The following sections provide more detailed information:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/ant.html b/docs/manual/ant.html
index 6088991..bc3a3b4 100644
--- a/docs/manual/ant.html
+++ b/docs/manual/ant.html
@@ -102,7 +102,7 @@ If you really prefer a full-blown XML configuration, you can replace the
 ProGuard configuration options by XML configuration tags. The resulting
 configuration will be equivalent, but much more verbose and difficult to read,
 as XML goes. The remainder of this page presents the supported tags. For a
-more extensive discussion of their meaning, please refer to the traditional <a
+more extensive discussion of their meaning, please consult the traditional <a
 href="usage.html">Usage</a> section. You can find some sample configuration
 files in the <code>examples/ant</code> directory of the ProGuard distribution.
 <p>
@@ -169,7 +169,7 @@ elements:
 <dt><a href="usage.html#optimizationpasses"><code><b>optimizationpasses</b></code></a>
     = "<i>n</i>"
     (default = 1)</dt>
-<dd>The number of optimization passes to be performed.
+<dd>The number of optimization passes to be performed.</dd>
 
 <dt><a href="usage.html#allowaccessmodification"><code><b>allowaccessmodification</b></code></a>
     = "<i>boolean</i>"
@@ -267,13 +267,16 @@ elements:
 <dt><a href="usage.html#dontnote"><code><b>note</b></code></a>
     = "<i>boolean</i>"
    (default = true)</dt>
-<dd>Print notes about potential mistakes or omissions in the
-    configuration.</dd>
+<dd>Print notes about potential mistakes or omissions in the configuration.
+    Use the nested element <a href="#dontnote">dontnote</a> for more
+    fine-grained control.</dd>
 
 <dt><a href="usage.html#dontwarn"><code><b>warn</b></code></a>
     = "<i>boolean</i>"
     (default = true)</dt>
-<dd>Warn about unresolved references at all.</dd>
+<dd>Print warnings about unresolved references. Use the nested
+    element <a href="#dontwarn">dontwarn</a> for more fine-grained
+    control. <i>Only use this option if you know what you're doing!</i></dd>
 
 <dt><a href="usage.html#ignorewarnings"><code><b>ignorewarnings</b></code></a>
     = "<i>boolean</i>"
@@ -310,6 +313,13 @@ elements:
     <code><b>/></b></code></dt>
 <dd>Specifies the library jars (or wars, ears, zips, or directories).</dd>
 
+<dt><a href="usage.html#keepdirectories"><code><b><keepdirectory name = </b></code></a>"<i>directory_name</i>"
+    <code><b>/></b></code><br/>
+    <a href="usage.html#keepdirectories"><code><b><keepdirectories filter = </b></code></a>"<a href="usage.html#filefilters"><i>directory_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Keep the specified directories in the output jars (or wars, ears, zips, or
+    directories).</dd>
+
 <dt><a href="usage.html#keep"><code><b><keep</b></code></a>
     <a href="#keepmodifier"><i>modifiers</i></a>
     <a href="#classspecification"><i>class_specification</i></a>
@@ -375,22 +385,55 @@ elements:
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
     <code><b></assumenosideeffects></b></code></dt>
 <dd>Assume that the specified methods don't have any side effects, while
-    optimizing.</dd>
+    optimizing. <i>Only use this option if you know what you're
+    doing!</i></dd>
+
+<dt><a href="usage.html#optimizations"><code><b><optimization name = </b></code></a>"<a href="optimizations.html"><i>optimization_name</i></a>"
+    <code><b>/></b></code><br/>
+    <a href="usage.html#optimizations"><code><b><optimizations filter = </b></code></a>""<a href="optimizations.html"><i>optimization_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Perform only the specified optimizations.</dd>
+
+<dt><a href="usage.html#keeppackagenames"><code><b><keeppackagename name = </b></code></a>"<i>package_name</i>"
+    <code><b>/></b></code><br/>
+    <a href="usage.html#keeppackagenames"><code><b><keeppackagenames filter = </b></code></a>"<a href="usage.html#filters"><i>package_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Keep the specified package names from being obfuscated. If no name is
+    given, all package names are preserved.</dd>
 
 <dt><a href="usage.html#keepattributes"><code><b><keepattribute name = </b></code></a>"<i>attribute_name</i>"
+    <code><b>/></b></code><br/>
+    <a href="usage.html#keepattributes"><code><b><keepattributes filter = </b></code></a>"<a href="usage.html#filters"><i>attribute_filter</i></a>"
     <code><b>/></b></code></dt>
-<dd>Preserve the given optional Java bytecode attribute, with optional
+<dd>Preserve the specified optional Java bytecode attributes, with optional
     wildcards. If no name is given, all attributes are preserved.</dd>
 
-<dt><a href="usage.html#adaptresourcefilenames"><code><b><adaptresourcefilenames filter = </b></code></a>"<a href="usage.html#filters"><i>filter</i></a>"
+<dt><a href="usage.html#adaptclassstrings"><code><b><adaptclassstrings filter = </b></code></a>"<a href="usage.html#filters"><i>class_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Adapt string constants in the specified classes, based on the obfuscated
+    names of any corresponding classes.</dd>
+
+<dt><a href="usage.html#adaptresourcefilenames"><code><b><adaptresourcefilenames filter = </b></code></a>"<a href="usage.html#filefilters"><i>file_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 filter = </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#filefilters"><i>file_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>
+    obfuscated names of the processed classes.</dd>
+
+<dt><a name="dontnote" />
+    <a href="usage.html#dontnote"><code><b><dontnote filter = </b></code></a>"<a href="usage.html#filters"><i>class_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Don't print notes about classes matching the specified class name
+    filter.</dd>
+
+<dt><a name="dontwarn" />
+    <a href="usage.html#dontwarn"><code><b><dontwarn filter = </b></code></a>"<a href="usage.html#filters"><i>class_filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Don't print warnings about classes matching the specified class name
+    filter. <i>Only use this option if you know what you're doing!</i></dd>
 
 <dt><a name="configuration_element"><code><b><configuration refid = </b></code></a>"<i>ref_id</i>"
     <code><b>/></b></code></dt>
@@ -434,24 +477,24 @@ In addition, the jar tags can have ProGuard-style filter attributes:
 <dl>
 
 <dt><code><b>filter</b></code> =
-    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
+    "<a href="usage.html#filefilters"><i>file_filter</i></a>"</dt>
 <dd>An optional filter for all class file names and resource file names that
     are encountered.</dd>
 
 <dt><code><b>jarfilter</b></code> =
-    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
+    "<a href="usage.html#filefilters"><i>file_filter</i></a>"</dt>
 <dd>An optional filter for all jar names that are encountered.</dd>
 
 <dt><code><b>warfilter</b></code> =
-    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
+    "<a href="usage.html#filefilters"><i>file_filter</i></a>"</dt>
 <dd>An optional filter for all war names that are encountered.</dd>
 
 <dt><code><b>earfilter</b></code> =
-    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
+    "<a href="usage.html#filefilters"><i>file_filter</i></a>"</dt>
 <dd>An optional filter for all ear names that are encountered.</dd>
 
 <dt><code><b>zipfilter</b></code> =
-    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
+    "<a href="usage.html#filefilters"><i>file_filter</i></a>"</dt>
 <dd>An optional filter for all zip names that are encountered.</dd>
 
 </dl>
@@ -560,7 +603,7 @@ attributes:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/examples.html b/docs/manual/examples.html
index 33a8ecc..3f47fca 100644
--- a/docs/manual/examples.html
+++ b/docs/manual/examples.html
@@ -15,11 +15,17 @@ Some typical useful configurations:
 <li><a href="#application">A typical application</a>
 <li><a href="#applet">A typical applet</a>
 <li><a href="#midlet">A typical midlet</a>
+<li><a href="#jcapplet">A typical Java Card applet</a>
+<li><a href="#xlet">A typical xlet</a>
+<li><a href="#androidapplication">A typical Android application</a>
+<li><a href="#library">A typical library</a>
 <li><a href="#applications">All possible applications in the input jars</a>
 <li><a href="#applets">All possible applets in the input jars</a>
 <li><a href="#midlets">All possible midlets in the input jars</a>
+<li><a href="#jcapplets">All possible Java Card applets in the input jars</a>
+<li><a href="#xlets">All possible xlets in the input jars</a>
+<li><a href="#androidapplications">All possible Android applications in the input jars</a>
 <li><a href="#servlets">All possible servlets in the input jars</a>
-<li><a href="#library">A typical library</a>
 <li><a href="#native">Processing native methods</a>
 <li><a href="#callback">Processing callback methods</a>
 <li><a href="#enumerations">Processing enumeration classes</a>
@@ -162,6 +168,153 @@ option.
 <p>
 If applicable, you should add options for processing <a href="#native">native
 methods</a> and <a href="#resourcefiles">resource files</a>.
+<p>
+Note that you will still have to adapt the midlet jar size in the
+corresponding jad file; ProGuard doesn't do that for you.
+
+<a name="jcapplet"> </a>
+<h3>A typical Java Card applet</h3>
+These options shrink, optimize, and obfuscate the Java Card applet
+<code>mypackage.MyApplet</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar
+-dontwarn    java.lang.Class
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+
+-keep public class mypackage.MyApplet
+</pre>
+<p>
+The configuration is very similar to the configuration for midlets, except that
+it now targets the Java Card run-time environment. This environment doesn't
+have java.lang.Class, so we're telling ProGuard not to worry about it.
+
+<a name="xlet"> </a>
+<h3>A typical xlet</h3>
+These options shrink, optimize, and obfuscate the xlet
+<code>mypackage.MyXlet</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/jtv1.1/javatv.jar
+-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar
+-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+
+-keep public class mypackage.MyXlet
+</pre>
+<p>
+The configuration is very similar to the configuration for midlets, except that
+it now targets the CDC run-time environment with the Java TV API.
+
+<a name="androidapplication"> </a>
+<h3>A typical Android application</h3>
+These options shrink, optimize, and obfuscate the simple Android application
+based on a single activity <code>mypackage.MyActivity</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/android-1.5_r1/platforms/android-1.5/android.jar
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-optimizations !code/simplification/arithmetic
+
+-keep public class mypackage.MyActivity
+</pre>
+<p>
+The configuration is very similar to the configuration for midlets, except that
+it now targets the Android run-time environment.
+<p>
+The <a href="usage.html#optimizations"><code>-optimizations</code></a> option
+disables some arithmetic simplifications that Dalvik 1.0 and 1.5 can't handle.
+<p>
+If applicable, you should add options for processing <a href="#native">native
+methods</a>, <a href="#callback">callback methods</a>, and <a
+href="#resourcefiles">resource files</a>.
+
+<a name="library"> </a>
+<h3>A typical library</h3>
+These options shrink, optimize, and obfuscate an entire library, keeping all
+public and protected classes and class members, native method names, and
+serialization code:
+<pre>
+-injars       in.jar
+-outjars      out.jar
+-libraryjars  <java.home>/lib/rt.jar
+-printmapping out.map
+
+-renamesourcefileattribute SourceFile
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
+                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+
+-keep public class * {
+    public protected *;
+}
+
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+</pre>
+<p>
+This configuration should preserve everything we'll ever want to access in the
+library. Only if there are any other non-public classes or methods that are
+invoked dynamically, they should be specified using additional <a
+href="usage.html#keep"><code>-keep</code></a> options.
+<p>
+The <a
+href="usage.html#keepclassmembernames"><code>-keepclassmembernames</code></a>
+option for the <code>class$</code> methods is not strictly necessary. These
+methods are inserted by the <code>javac</code> compiler and the
+<code>jikes</code> compiler respectively, to implement the <code>.class</code>
+construct. ProGuard will automatically detect them and deal with them, even
+when their names have been obfuscated. However, older versions of ProGuard and
+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 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
+be preserved too, for any inner classes that can be referenced from outside the
+library. The <code>javac</code> compiler would be unable to find the inner
+classes otherwise.
+<p>
+The "Signature" attribute is required to be able to access generic types when
+compiling in JDK 5.0 and higher.
+<p>
+Finally, we're keeping the "Deprecated" attribute and the attributes for
+producing <a href="#stacktrace">useful stack traces</a>.
+<p>
+We've also added some options for for processing <a href="#native">native
+methods</a>, <a href="#enumerations">enumerations</a>, <a
+href="#serializable">serializable classes</a>, and <a
+href="#annotations">annotations</a>, which are all discussed in their
+respective examples.
 
 <a name="applications"> </a>
 <h3>All possible applications in the input jars</h3>
@@ -255,6 +408,85 @@ out which midlets exactly will be preserved.
 <p>
 If applicable, you should add options for processing <a href="#native">native
 methods</a> and <a href="#resourcefiles">resource files</a>.
+<p>
+Note that you will still have to adapt the midlet jar size in the
+corresponding jad file; ProGuard doesn't do that for you.
+
+<a name="jcapplets"> </a>
+<h3>All possible Java Card applets in the input jars</h3>
+These options shrink, optimize, and obfuscate all public Java Card applets in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar
+-dontwarn    java.lang.Class
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-printseeds
+
+-keep public class * implements javacard.framework.Applet
+</pre>
+<p>
+We're simply keeping all classes that implement the <code>Applet</code>
+interface.
+<p>
+The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
+out which applets exactly will be preserved.
+
+<a name="xlets"> </a>
+<h3>All possible xlets in the input jars</h3>
+These options shrink, optimize, and obfuscate all public xlets in
+<code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/jtv1.1/javatv.jar
+-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar
+-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-printseeds
+
+-keep public class * implements javax.tv.xlet.Xlet
+</pre>
+<p>
+We're simply keeping all classes that implement the <code>Xlet</code> interface.
+<p>
+The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
+out which xlets exactly will be preserved.
+
+<a name="androidapplications"> </a>
+<h3>All possible Android applications in the input jars</h3>
+These options shrink, optimize, and obfuscate all public activities, services,
+broadcast receivers, and content providers in <code>in.jar</code>:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/android-1.5_r1/platforms/android-1.5/android.jar
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+-optimizations !code/simplification/arithmetic
+-printseeds
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+</pre>
+<p>
+We're keeping all classes that extend the base classes that may be referenced
+by the <code>AndroidManifest.xml</code> file of the application.
+<p>
+The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
+out which implementations exactly will be preserved.
+<p>
+If applicable, you should add options for processing <a href="#native">native
+methods</a>, <a href="#callback">callback methods</a>, and <a
+href="#resourcefiles">resource files</a>.
 
 <a name="servlets"> </a>
 <h3>All possible servlets in the input jars</h3>
@@ -289,84 +521,6 @@ classes</a>, <a href="#beans">bean classes</a>, <a
 href="#annotations">annotations</a>, and <a href="#resourcefiles">resource
 files</a>.
 
-<a name="library"> </a>
-<h3>A typical library</h3>
-These options shrink, optimize, and obfuscate an entire library, keeping all
-public and protected classes and class members, native method names, and
-serialization code:
-<pre>
--injars       in.jar
--outjars      out.jar
--libraryjars  <java.home>/lib/rt.jar
--printmapping out.map
-
--renamesourcefileattribute SourceFile
--keepattributes Exceptions,InnerClasses,Signature,Deprecated,
-                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-
--keep public class * {
-    public protected *;
-}
-
--keepclassmembernames class * {
-    java.lang.Class class$(java.lang.String);
-    java.lang.Class class$(java.lang.String, boolean);
-}
-
--keepclasseswithmembernames class * {
-    native <methods>;
-}
-
--keepclassmembers enum * {
-    public static **[] values();
-    public static ** valueOf(java.lang.String);
-}
-
--keepclassmembers class * implements java.io.Serializable {
-    static final long serialVersionUID;
-    private void writeObject(java.io.ObjectOutputStream);
-    private void readObject(java.io.ObjectInputStream);
-    java.lang.Object writeReplace();
-    java.lang.Object readResolve();
-}
-</pre>
-<p>
-This configuration should preserve everything we'll ever want to access in the
-library. Only if there are any other non-public classes or methods that are
-invoked dynamically, they should be specified using additional <a
-href="usage.html#keep"><code>-keep</code></a> options.
-<p>
-The <a
-href="usage.html#keepclassmembernames"><code>-keepclassmembernames</code></a>
-option for the <code>class$</code> methods is not strictly necessary. These
-methods are inserted by the <code>javac</code> compiler and the
-<code>jikes</code> compiler respectively, to implement the <code>.class</code>
-construct. ProGuard will automatically detect them and deal with them, even
-when their names have been obfuscated. However, older versions of ProGuard and
-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 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
-be preserved too, for any inner classes that can be referenced from outside the
-library. The <code>javac</code> compiler would be unable to find the inner
-classes otherwise.
-<p>
-The "Signature" attribute is required to be able to access generic types when
-compiling in JDK 5.0 and higher.
-<p>
-Finally, we're keeping the "Deprecated" attribute and the attributes for
-producing <a href="#stacktrace">useful stack traces</a>.
-<p>
-We've also added some options for for processing <a href="#native">native
-methods</a>, <a href="#enumerations">enumerations</a>, <a
-href="#serializable">serializable classes</a>, and <a
-href="#annotations">annotations</a>, which are all discussed in their
-respective examples.
-
 <a name="native"> </a>
 <h3>Processing native methods</h3>
 If your application, applet, servlet, library, etc., contains native methods,
@@ -662,9 +816,7 @@ any). The <a
 href="usage.html#adaptresourcefilecontents">-adaptresourcefilecontents</a>
 option looks for class names in properties files and in the manifest file, and
 replaces these names by the obfuscated names (if any). You'll probably want to
-adapt the filters to suit your application. Note that package names of
-resource files that don't have corresponding class files are not updated in
-the current implementation.
+adapt the filters to suit your application.
 
 <a name="stacktrace"> </a>
 <h3>Producing useful obfuscated stack traces</h3>
@@ -1143,7 +1295,7 @@ illustrate some of the possibilities.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/gui.html b/docs/manual/gui.html
index 033fcd8..37684a6 100644
--- a/docs/manual/gui.html
+++ b/docs/manual/gui.html
@@ -345,10 +345,12 @@ Corresponding configuration options:
 <li>-<a href="usage.html#overloadaggressively">overloadaggressively</a>
 <li>-<a href="usage.html#useuniqueclassmembernames">useuniqueclassmembernames</a>
 <li>-<a href="usage.html#dontusemixedcaseclassnames">dontusemixedcaseclassnames</a>
+<li>-<a href="usage.html#keeppackagenames">keeppackagenames</a>
 <li>-<a href="usage.html#flattenpackagehierarchy">flattenpackagehierarchy</a>
 <li>-<a href="usage.html#repackageclasses">repackageclasses</a>
 <li>-<a href="usage.html#keepattributes">keepattributes</a>
 <li>-<a href="usage.html#renamesourcefileattribute">renamesourcefileattribute</a>
+<li>-<a href="usage.html#adaptclassstrings">adaptclassstrings</a>
 <li>-<a href="usage.html#adaptresourcefilenames">adaptresourcefilenames</a>
 <li>-<a href="usage.html#adaptresourcefilecontents">adaptresourcefilecontents</a>
 <li>-<a href="usage.html#keepnames">keepnames</a>
@@ -374,6 +376,7 @@ href="#shrinking">Shrinking Tab</a>.
 Corresponding configuration options:
 <ul type="none">
 <li>-<a href="usage.html#dontoptimize">dontoptimize</a>
+<li>-<a href="usage.html#optimizations">optimizations</a>
 <li>-<a href="usage.html#optimizationpasses">optimizationpasses</a>
 <li>-<a href="usage.html#allowaccessmodification">allowaccessmodification</a>
 <li>-<a href="usage.html#mergeinterfacesaggressively">mergeinterfacesaggressively</a>
@@ -402,6 +405,7 @@ Corresponding configuration options:
 <li>-<a href="usage.html#ignorewarnings">ignorewarnings</a>
 <li>-<a href="usage.html#dontskipnonpubliclibraryclasses">dontskipnonpubliclibraryclasses</a>
 <li>-<a href="usage.html#dontskipnonpubliclibraryclassmembers">dontskipnonpubliclibraryclassmembers</a>
+<li>-<a href="usage.html#keepdirectories">keepdirectories</a>
 <li>-<a href="usage.html#forceprocessing">forceprocessing</a>
 <li>-<a href="usage.html#printseeds">printseeds</a>
 <li>-<a href="usage.html#printconfiguration">printconfiguration</a>
@@ -463,7 +467,7 @@ There are two buttons at the bottom:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/index.html b/docs/manual/index.html
index 65db3c8..397b910 100644
--- a/docs/manual/index.html
+++ b/docs/manual/index.html
@@ -32,7 +32,7 @@
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/introduction.html b/docs/manual/introduction.html
index 5035057..cdab330 100644
--- a/docs/manual/introduction.html
+++ b/docs/manual/introduction.html
@@ -149,7 +149,7 @@ about the internals of the code.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html
index 68c27da..cfe0ff5 100644
--- a/docs/manual/limitations.html
+++ b/docs/manual/limitations.html
@@ -57,7 +57,7 @@ which are easily avoided or resolved:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/optimizations.html b/docs/manual/optimizations.html
new file mode 100644
index 0000000..9c20571
--- /dev/null
+++ b/docs/manual/optimizations.html
@@ -0,0 +1,158 @@
+<!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<meta http-equiv="content-style-type" content="text/css">
+<link rel="stylesheet" type="text/css" href="style.css">
+<title>Optimizations</title>
+</head>
+<body>
+
+<h2>Optimizations</h2>
+
+The optimization step of ProGuard can be switched off with the
+<a href="usage.html#dontoptimize"><code>-dontoptimize</code></a> option. For
+more fine-grained control over individual optimizations, experts can use the
+<a href="usage.html#optimizations"><code>-optimizations</code></a> option,
+with a filter based on the optimization names listed below. The filter works
+like any <a href="usage.html#filters">filter</a> in ProGuard.
+<p>
+
+The following wildcards are supported:
+
+<table cellspacing="10">
+<tr><td valign="top"><code><b>?</b></code></td>
+    <td>matches any single character in an optimization name.</td></tr>
+<tr><td valign="top"><code><b>*</b></code></td>
+    <td>matches any part of an optimization name.</td></tr>
+</table>
+
+An optimization that is preceded by an exclamation mark '<b>!</b>' is
+<i>excluded</i> from further attempts to match with <i>subsequent</i>
+optimization names in the filter. Make sure to specify filters correctly,
+since they are not checked for potential typos.
+<p>
+
+For example,
+"<code>code/simplification/variable,code/simplification/arithmetic</code>"
+only performs the two specified peephole optimizations.
+<p>
+
+For example, "<code>!method/propagation/*</code>" performs all optimizations,
+except the ones that propagate values between methods.
+<p>
+
+For example,
+"<code>!code/simplification/advanced,code/simplification/*</code>" only
+performs all peephole optimizations.
+<p>
+Some optimizations necessarily imply other optimizations. These are then
+indicated. Note that the list is likely to change over time, as optimizations
+are added and reorganized.
+<p>
+
+<dl>
+<dt><code><b>class/marking/final</b></code></dt>
+<dd>Marks classes as final, whenever possible.</dd>
+
+<dt><code><b>class/merging/vertical</b></code></dt>
+<dd>Merges classes vertically in the class hierarchy, whenever possible.</dd>
+
+<dt><code><b>class/merging/horizontal</b></code></dt>
+<dd>Merges classes horizontally in the class hierarchy, whenever possible.</dd>
+
+<dt><div>(⇒ <code>code/removal/advanced</code>)</div>
+    <code><b>field/removal/writeonly</b></code></dt>
+<dd>Removes write-only fields.</dd>
+
+<dt><code><b>field/marking/private</b></code></dt>
+<dd>Marks fields as private, whenever possible.</dd>
+
+<dt><div>(⇒ <code>code/simplification/advanced</code>)</div>
+    <code><b>field/propagation/value</b></code></dt>
+<dd>Propagates the values of fields across methods.</dd>
+
+<dt><code><b>method/marking/private</b></code></dt>
+<dd>Marks methods as private, whenever possible (<i>devirtualization</i>).</dd>
+
+<dt><div>(⇒ <code>code/removal/advanced</code>)</div>
+    <code><b>method/marking/static</b></code></dt>
+<dd>Marks methods as static, whenever possible (<i>devirtualization</i>).</dd>
+
+<dt><code><b>method/marking/final</b></code></dt>
+<dd>Marks methods as final, whenever possible.</dd>
+
+<dt><div>(⇒ <code>code/removal/advanced</code>)</div>
+    <code><b>method/removal/parameter</b></code></dt>
+<dd>Removes unused method parameters.</dd>
+
+<dt><div>(⇒ <code>code/simplification/advanced</code>)</div>
+    <code><b>method/propagation/parameter</b></code></dt>
+<dd>Propagates the values of method parameters from method invocations to
+    the invoked methods.</dd>
+
+<dt><div>(⇒ <code>code/simplification/advanced</code>)</div>
+    <code><b>method/propagation/returnvalue</b></code></dt>
+<dd>Propagates the values of method return values from methods to their
+    invocations.</dd>
+
+<dt><code><b>method/inlining/short</b></code></dt>
+<dd>Inlines short methods.</dd>
+
+<dt><code><b>method/inlining/unique</b></code></dt>
+<dd>Inlines methods that are only called once.</dd>
+
+<dt><code><b>method/inlining/tailrecursion</b></code></dt>
+<dd>Simplifies tail recursion calls, whenever possible.</dd>
+
+<dt><code><b>code/merging</b></code></dt>
+<dd>Merges identical blocks of code by modifying branch targets.</dd>
+
+<dt><code><b>code/simplification/variable</b></code></dt>
+<dd>Performs peephole optimizations for variable loading and storing.</dd>
+
+<dt><code><b>code/simplification/arithmetic</b></code></dt>
+<dd>Performs peephole optimizations for arithmetic instructions.</dd>
+
+<dt><code><b>code/simplification/cast</b></code></dt>
+<dd>Performs peephole optimizations for casting operations.</dd>
+
+<dt><code><b>code/simplification/field</b></code></dt>
+<dd>Performs peephole optimizations for field loading and storing.</dd>
+
+<dt><div>(⇒ <code>code/removal/simple</code>)</div>
+    <code><b>code/simplification/branch</b></code></dt>
+<dd>Performs peephole optimizations for branch instructions.</dd>
+
+<dt><div>(<i>best used with</i> <code>code/removal/advanced</code>)</div>
+    <code><b>code/simplification/advanced</b></code></dt>
+<dd>Simplifies code based on control flow analysis and data flow
+    analysis.</dd>
+
+<dt><div>(⇒ <code>code/removal/exception</code>)</div>
+    <code><b>code/removal/advanced</b></code></dt>
+<dd>Removes dead code based on control flow analysis and data flow
+    analysis.</dd>
+
+<dt><div>(⇒ <code>code/removal/exception</code>)</div>
+    <code><b>code/removal/simple</b></code></dt>
+<dd>Removes dead code based on a simple control flow analysis.</dd>
+
+<dt><code><b>code/removal/variable</b></code></dt>
+<dd>Removes unused variables from the local variable frame.</dd>
+
+<dt><code><b>code/removal/exception</b></code></dt>
+<dd>Removes exceptions with empty catch blocks.</dd>
+
+<dt><code><b>code/allocation/variable</b></code></dt>
+<dd>Optimizes variable allocation on the local variable frame.</dd>
+</dl>
+<p>
+
+<hr>
+<address>
+Copyright © 2002-2009
+<a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
+</address>
+</body>
+</html>
diff --git a/docs/manual/refcard.html b/docs/manual/refcard.html
index 9ef9d78..236f049 100644
--- a/docs/manual/refcard.html
+++ b/docs/manual/refcard.html
@@ -73,6 +73,13 @@
 </tr>
 
 <tr>
+<td valign="top"><a href="usage.html#keepdirectories"><code><b>-keepdirectories</b></code></a>
+                 [<a href="usage.html#filters"><i>directory_filter</i></a>]</td>
+<td>Keep the specified directories in the output jars (or wars, ears, zips, or
+    directories).</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#target"><code><b>-target</b></code></a>
                  <i>version</i></td>
 <td>Set the given version number in the processed classes.</td>
@@ -160,6 +167,12 @@
 </tr>
 
 <tr>
+<td valign="top"><a href="usage.html#optimizations"><code><b>-optimizations</b></code></a>
+                 <a href="optimizations.html"><i>optimization_filter</i></a></td>
+<td>The optimizations to be enabled and disabled.</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#optimizationpasses"><code><b>-optimizationpasses</b></code></a>
                  <i>n</i></td>
 <td>The number of optimization passes to be performed.</td>
@@ -235,6 +248,12 @@
 </tr>
 
 <tr>
+<td valign="top"><a href="usage.html#keeppackagenames"><code><b>-keeppackagenames</b></code></a>
+                 [<i><a href="usage.html#filters">package_filter</a></i>]</td>
+<td>Keep the specified package names from being obfuscated.</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#flattenpackagehierarchy"><code><b>-flattenpackagehierarchy</b></code></a>
                  [<i>package_name</i>]</td>
 <td>Repackage all packages that are renamed into the single given parent
@@ -250,7 +269,7 @@
 
 <tr>
 <td valign="top"><a href="usage.html#keepattributes"><code><b>-keepattributes</b></code></a>
-                 [<i>attribute_name<b>,</b>...</i>]</td>
+                 [<i><a href="usage.html#filters">attribute_filter</a></i>]</td>
 <td>Preserve the given optional attributes; typically
     <code>Exceptions</code>, <code>InnerClasses</code>,
     <code>Signature</code>, <code>Deprecated</code>,
@@ -269,17 +288,24 @@
 </tr>
 
 <tr>
+<td valign="top"><a href="usage.html#adaptclassstrings"><code><b>-adaptclassstrings</b></code></a>
+                 [<a href="usage.html#filters"><i>class_filter</i></a>]</td>
+<td>Adapt string constants in the specified classes, based on the obfuscated
+    names of any corresponding classes.</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#adaptresourcefilenames"><code><b>-adaptresourcefilenames</b></code></a>
-                 [<a href="#filters"><i>filter</i></a>]</td>
+                 [<a href="usage.html#filefilters"><i>file_filter</i></a>]</td>
 <td>Rename the specified resource files, based on the obfuscated names of the
     corresponding class files.</td>
 </tr>
 
 <tr>
 <td valign="top"><a href="usage.html#adaptresourcefilecontents"><code><b>-adaptresourcefilecontents</b></code></a>
-                 [<a href="#filters"><i>filter</i></a>]</td>
+                 [<a href="usage.html#filefilters"><i>file_filter</i></a>]</td>
 <td>Update the contents of the specified resource files, based on the
-    obfuscated names of the class files.</td>
+    obfuscated names of the processed classes.</td>
 </tr>
 
 <tr>
@@ -298,13 +324,15 @@
 </tr>
 
 <tr>
-<td valign="top"><a href="usage.html#dontnote"><code><b>-dontnote</b></code></a></td>
+<td valign="top"><a href="usage.html#dontnote"><code><b>-dontnote</b></code></a>
+                 [<a href="usage.html#filters"><i>class_filter</i></a>]</td>
 <td>Don't print notes about potential mistakes or omissions in the
     configuration.</td>
 </tr>
 
 <tr>
-<td valign="top"><a href="usage.html#dontwarn"><code><b>-dontwarn</b></code></a></td>
+<td valign="top"><a href="usage.html#dontwarn"><code><b>-dontwarn</b></code></a>
+                 [<a href="usage.html#filters"><i>class_filter</i></a>]</td>
 <td>Don't warn about unresolved references at all.</td>
 </tr>
 
@@ -430,7 +458,7 @@ Notes:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/retrace/examples.html b/docs/manual/retrace/examples.html
index 7829efb..211017b 100644
--- a/docs/manual/retrace/examples.html
+++ b/docs/manual/retrace/examples.html
@@ -328,7 +328,7 @@ Exception in thread "main" java.lang.Error: Random exception
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/retrace/index.html b/docs/manual/retrace/index.html
index d87163c..ebb23ac 100644
--- a/docs/manual/retrace/index.html
+++ b/docs/manual/retrace/index.html
@@ -18,7 +18,7 @@
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/retrace/introduction.html b/docs/manual/retrace/introduction.html
index 8be230f..19f9471 100644
--- a/docs/manual/retrace/introduction.html
+++ b/docs/manual/retrace/introduction.html
@@ -60,7 +60,7 @@ original class names and class member names to their obfuscated names.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/retrace/usage.html b/docs/manual/retrace/usage.html
index 5355fff..88587ff 100644
--- a/docs/manual/retrace/usage.html
+++ b/docs/manual/retrace/usage.html
@@ -14,37 +14,72 @@ You can find the ReTrace jar in the <code>lib</code> directory of the
 ProGuard distribution. To run ReTrace, just type:
 <p>
 <p class="code">
-<code><b>java -jar retrace.jar [-verbose] </b></code><i>mapping_file</i>
-[<i>stacktrace_file</i>]
+<code><b>java -jar retrace.jar </b></code>[<i>options...</i>]
+         <i>mapping_file</i> [<i>stacktrace_file</i>]
 </p>
-The arguments have the following meaning:
-<p>
-<table cellspacing="10">
-<tr>
-<td valign="top"><code><b>-verbose</b></code></td>
-
-<td>Optionally specifies to print out more informative stack traces that
-    include not only method names, but also method return types and
-    arguments.</td>
+These are the arguments:
 
-</tr>
-<tr>
-<td valign="top"><i>mapping_file</i></td>
+<dl>
+<dt><i>mapping_file</i></dt>
 
-<td>Specifies the name of the mapping file, produced by ProGuard with the
-    option "<code>-printmapping</code> <i>mapping_file</i>", while obfuscating
-    the application that produced the stack trace.</td>
+<dd>Specifies the name of the mapping file, produced by ProGuard with the
+    option
+    "<a href="../usage.html#printmapping"><code>-printmapping</code></a> <i>mapping_file</i>",
+    while obfuscating the application that produced the stack trace.</dd>
 
-</tr>
-<tr>
-<td valign="top"><i>stacktrace_file</i></td>
+<dt><i>stacktrace_file</i></dt>
 
-<td>Optionally specifies the name of the file containing the stack trace. If
+<dd>Optionally specifies the name of the file containing the stack trace. If
     no file is specified, a stack trace is read from the standard input. Blank
-    lines and unrecognized lines are ignored, as far as possible.</td>
-
-</tr>
-</table>
+    lines and unrecognized lines are ignored, as far as possible.</dd>
+</dl>
+
+The following options are supported:
+<dl>
+<dt><code><b>-verbose</b></code></dt>
+
+<dd>Specifies to print out more informative stack traces that include not only
+    method names, but also method return types and arguments.</dd>
+
+<dt><code><b>-regex</b></code> <i>regular_expression</i></dt>
+
+<dd>Specifies the regular expression that is used to parse the lines in the
+    stack trace. Specifying a different regular expression allows to
+    de-obfuscate more general types of input than just stack traces. The
+    default is suitable for stack traces produced by most JVMs:
+    <pre>
+    (?:\s*%c:.*)|(?:\s*at\s+%c.%m\s*\(.*?(?::%l)?\)\s*)
+    </pre>
+    The regular expression is a Java regular expression (cfr. the documentation
+    of <code>java.util.regex.Pattern</code>), with a few additional wildcards:
+    <table cellspacing="10">
+    <tr><td valign="top"><code><b>%c</b></code></td>
+        <td>matches a class name (e.g.
+            "<code>myapplication.MyClass</code>").</td></tr>
+    <tr><td valign="top"><code><b>%C</b></code></td>
+        <td>matches a class name with slashes (e.g.
+            "<code>myapplication/MyClass</code>").</td></tr>
+    <tr><td valign="top"><code><b>%t</b></code></td>
+        <td>matches a field type or method return type (e.g.
+            "<code>myapplication.MyClass[]</code>").</td></tr>
+    <tr><td valign="top"><code><b>%f</b></code></td>
+        <td>matches a field name (e.g.
+            "<code>myField</code>").</td></tr>
+    <tr><td valign="top"><code><b>%m</b></code></td>
+        <td>matches a method name (e.g.
+            "<code>myMethod</code>").</td></tr>
+    <tr><td valign="top"><code><b>%a</b></code></td>
+        <td>matches a list of method arguments (e.g.
+            "<code>boolean,int</code>").</td></tr>
+    <tr><td valign="top"><code><b>%l</b></code></td>
+        <td>matches a line number inside a method (e.g.
+            "<code>123</code>").</td></tr>
+    </table>
+    Elements that match these wildcards are de-obfuscated, when possible. Note
+    that regular expressions must not contain any capturing groups. Use
+    non-capturing groups instead: <code>(?:</code>...<code>)</code>
+    </dd>
+</dl>
 
 The restored stack trace is printed to the standard output. The completeness
 of the restored stack trace depends on the presence of line number tables in
@@ -74,7 +109,7 @@ will be left unchanged.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/style.css b/docs/manual/style.css
index 778e10f..28fc112 100644
--- a/docs/manual/style.css
+++ b/docs/manual/style.css
@@ -19,6 +19,12 @@ dt {
   padding: 6px;
 }
 
+dt div 
+{
+  color: grey;
+  float: right;
+}
+
 dd {
   padding: 6px;
 }
diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html
index 68ce0f5..d3ed14e 100644
--- a/docs/manual/troubleshooting.html
+++ b/docs/manual/troubleshooting.html
@@ -22,6 +22,7 @@ few problems. The following sections discuss some common issues and solutions:
 <li><a href="#duplicateclass">Note: duplicate definition of program/library class</a></li>
 <li><a href="#duplicatezipentry">Warning: can't write resource ... Duplicate zip entry</a></li>
 <li><a href="#unresolvedclass">Warning: can't find superclass or interface</a></li>
+<li><a href="#unresolvedclass">Warning: can't find referenced class</a></li>
 <li><a href="#unresolvedclassmember">Warning: can't find referenced field/method</a></li>
 <li><a href="#unresolvedenclosingmethod">Warning: can't find enclosing class/method</a></li>
 <li><a href="#dependency">Warning: library class ... depends on program class ...</a></li>
@@ -143,28 +144,30 @@ ProGuard may terminate when it encounters parsing errors or I/O errors, or
 some more serious warnings:
 
 <dl>
-<dt><a name="unresolvedclass"><b>Warning: can't find superclass or interface</b></a></dt>
+<dt><a name="unresolvedclass"><b>Warning: can't find superclass or interface</b><br/><b>Warning: can't find referenced class</b></a></dt>
 
-<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 <a
-    href="usage.html#libraryjars"><code>-libraryjars</code></a> option.
+<dd>If there are unresolved references to classes or interfaces, you most
+    likely forgot to specify an essential library. For proper processing, all
+    libraries that are referenced by your code must be specified, including
+    the Java run-time 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 <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
-    this case, the warning could also be ignored, because the class is not a
-    fundamental part of the class hierarchy).
+    option. Common examples are the classes
+    <code>javax.swing.TransferHandler$HasGetTransferHandler</code> and
+    <code>java.util.zip.ZipConstants</code>, which are used as interfaces in
+    some public classes, even though they are only package visible. This
+    option is not set by default for reasons of efficiency. Setting it increases
+    the processing time a bit, but it won't hurt the output in any way.
     <p>
     If you're missing a library and you're absolutely sure it isn't used
     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>
+    option. Only use these options if you really know what you're doing
+    though.</dd>
 
 <dt><a name="unresolvedclassmember"><b>Warning: can't find referenced field/method</b></a></dt>
 
@@ -455,9 +458,9 @@ might be several reasons:
     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
-    manually.</dd>
+    Furthermore, directory entries in jar files aren't copied, unless you
+    specify the option <a
+    href="usage.html#keepdirectories"><code>-keepdirectories</code></a>.</dd>
 
 <dt><a name="invalidjarfile"><b>Invalid or corrupt jarfile</b></a></dt>
 
@@ -582,7 +585,7 @@ might be several reasons:
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/usage.html b/docs/manual/usage.html
index 7a0a694..8be8d60 100644
--- a/docs/manual/usage.html
+++ b/docs/manual/usage.html
@@ -52,8 +52,10 @@ The sections below provide more details:
 <li><a href="#optimizationoptions">Optimization Options</a>
 <li><a href="#obfuscationoptions">Obfuscation Options</a>
 <li><a href="#preverificationoptions">Preverification Options</a>
+<li><a href="#generaloptions">General Options</a>
 <li><a href="#classpath">Class Paths</a>
 <li><a href="#filename">File Names</a>
+<li><a href="#filefilters">File Filters</a>
 <li><a href="#filters">Filters</a>
 <li><a href="#keepoverview">Overview of <code>Keep</code> Options</a>
 <li><a href="#keepoptionmodifiers">Keep Option Modifiers</a>
@@ -90,7 +92,7 @@ The sections below provide more details:
     will be copied without changes. Please be aware of any temporary files
     (e.g. created by IDEs), especially if you are reading your input files
     straight from directories. The entries in the class path can be filtered,
-    as explained in the <a href="#filters">filters</a> section. For better
+    as explained in the <a href="#filefilters">filters</a> section. For better
     readability, class path entries can be specified using multiple
     <code>-injars</code> options.</dd>
 
@@ -101,10 +103,10 @@ The sections below provide more details:
     directories). The processed input of the preceding <code>-injars</code>
     options will be written to the named jars. This allows you to collect the
     contents of groups of input jars into corresponding groups of output jars.
-    In addition, the output entries can be filtered, as explained in the <a
-    href="#filters">filters</a> section. Each processed class file or resource
-    file is then written to the first output entry with a matching filter,
-    within the group of output jars.
+    In addition, the output entries can be filtered, as explained in
+    the <a href="#filefilters">filters</a> section. Each processed class file
+    or resource file is then written to the first output entry with a matching
+    filter, within the group of output jars.
     <p>
     You must avoid letting the output files overwrite any input files. For
     better readability, class path entries can be specified using multiple
@@ -121,7 +123,7 @@ The sections below provide more details:
     class files that are only <i>called</i> needn't be present, although their
     presence can improve the results of the optimization step. The entries in
     the class path can be filtered, as explained in the <a
-    href="#filters">filters</a> section. For better readability, class path
+    href="#filefilters">filters</a> section. For better readability, class path
     entries can be specified using multiple <code>-libraryjars</code> options.
     <p>
     Please note that the boot path and the class path set for running ProGuard
@@ -157,6 +159,16 @@ The sections below provide more details:
     those cases, it can be useful to actually read the class members, in order
     to make sure the processed code remains consistent.</dd>
 
+<dt><a name="keepdirectories"><code><b>-keepdirectories</b></code></a>
+    [<i><a href="#filefilters">directory_filter</a></i>]</dt>
+
+<dd>Specifies the directories to be kept in the output jars (or wars, ears, or
+    directories). By default, directory entries are removed. This reduces the
+    jar size, but it may be undesirable if the program code tries to find them
+    with constructs like "<code>MyClass.class.getResource("")</code>". If the
+    option is specified without a filter, all directories are kept. With a
+    filter, only matching directories are kept.</dd>
+
 <dt><a name="target"><code><b>-target</b></code></a> <i>version</i></dt>
 
 <dd>Specifies the version number to be set in the processed class files. The
@@ -316,6 +328,13 @@ The sections below provide more details:
 <dd>Specifies not to optimize the input class files. By default, optimization
     is enabled; all methods are optimized at a bytecode level.</dd>
 
+<dt><a name="optimizations"><code><b>-optimizations</b></code></a>
+    <a href="optimizations.html"><i>optimization_filter</i></a></dt>
+
+<dd>Specifies the optimizations to be enabled and disabled, at a more
+    fine-grained level. Only applicable when optimizing. <i>This is an expert
+    option.</i></dd>
+
 <dt><a name="optimizationpasses"><code><b>-optimizationpasses</b></code></a> <i>n</i></dt>
 
 <dd>Specifies the number of optimization passes to be performed. By default, a
@@ -353,9 +372,10 @@ The sections below provide more details:
     obfuscating with the <a
     href="#repackageclasses"><code>-repackageclasses</code></a> option).
     <p>
-    Counter-indication: you probably shouldn't use this option when processing
-    code that is to be used as a library, since classes and class members that
-    weren't designed to be public in the API may become public.</dd>
+    <i>Counter-indication:</i> you probably shouldn't use this option when
+    processing code that is to be used as a library, since classes and class
+    members that weren't designed to be public in the API may become
+    public.</dd>
 
 <dt><a name="mergeinterfacesaggressively"><code><b>-mergeinterfacesaggressively</b></code></a></dt>
 
@@ -373,9 +393,16 @@ The sections below provide more details:
     "http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#34031"
     >Section 8.1.4</a>). Only applicable when optimizing.
     <p>
-    Counter-indication: setting this option can reduce the performance of the
-    processed code on some JVMs, since advanced just-in-time compilation tend
-    to favor more interfaces with fewer implementing classes.</dd>
+    <i>Counter-indication:</i> setting this option can reduce the performance
+    of the processed code on some JVMs, since advanced just-in-time
+    compilation tends to favor more interfaces with fewer implementing
+    classes. Worse, some JVMs may not be able to handle the resulting code.
+    Notably:
+    <ul>
+    <li>Sun's JRE 1.3 may throw an <code>InternalError</code> when
+        encountering more than 256 <i>Miranda</i> methods (interface methods
+        without implementations) in a class.
+    </ul></dd>
 
 </dl>
 <p>
@@ -460,7 +487,7 @@ The sections below provide more details:
     make the processed code even smaller (and less comprehensible). Only
     applicable when obfuscating.
     <p>
-    Counter-indication: the resulting class files fall within the Java
+    <i>Counter-indication:</i> the resulting class files fall within the Java
     bytecode specification (cfr. <a href=
     "http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html"
     >The Java Virtual Machine Specification, Second Edition</a>, first
@@ -524,6 +551,14 @@ The sections below provide more details:
     use this option to switch off this behavior. Note that the obfuscated jars
     will become larger as a result. Only applicable when obfuscating.</dd>
 
+<dt><a name="keeppackagenames"><code><b>-keeppackagenames</b></code></a>
+    [<i><a href="#filters">package_filter</a></i>]</dt>
+
+<dd>Specifies not obfuscate the given package names. The optional filter is a
+    comma-separated list of package names. Package names can contain <b>?</b>,
+    <b>*</b>, and <b>**</b> wildcards, and they can be preceded by the
+    <b>!</b> negator. Only applicable when obfuscating.</dd>
+
 <dt><a name="flattenpackagehierarchy"><code><b>-flattenpackagehierarchy</b></code></a>
     [<i>package_name</i>]</dt>
 
@@ -548,22 +583,21 @@ The sections below provide more details:
     deprecated name is <code>-defaultpackage</code>. Only applicable when
     obfuscating.
     <p>
-    Counter-indication: classes that look for resource files in their package
-    directories will no longer work properly if they are moved elsewhere. When
-    in doubt, just leave the packaging untouched by not using this
-    option.</dd>
+    <i>Counter-indication:</i> classes that look for resource files in their
+    package directories will no longer work properly if they are moved
+    elsewhere. When in doubt, just leave the packaging untouched by not using
+    this option.</dd>
 
 <dt><a name="keepattributes"><code><b>-keepattributes</b></code></a>
-    [<i>attribute_name<b>,</b>...</i>]</dt>
+    [<i><a href="#filters">attribute_filter</a></i>]</dt>
 
 <dd>Specifies any optional attributes to be preserved. The attributes can be
-    specified with one or more <code>-keepattributes</code> directives.
-    Multiple attributes should be separated by commas. An empty list preserves
-    all attributes. Attribute names can contain <b>?</b> and
-    <b>*</b> wildcards, and they can be preceded by the <b>!</b> negator (much
-    like file name <a href="#filters">filters</a>). Typical optional
-    attributes are <code>Exceptions</code>, <code>Signature</code>,
-    <code>Deprecated</code>, <code>SourceFile</code>, <code>SourceDir</code>,
+    specified with one or more <code>-keepattributes</code> directives. The
+    optional filter is a comma-separated list of attribute names. Attribute
+    names can contain <b>?</b>, <b>*</b>, and <b>**</b> wildcards, and they
+    can be preceded by the <b>!</b> negator. Typical optional attributes are
+    <code>Exceptions</code>, <code>Signature</code>, <code>Deprecated</code>,
+    <code>SourceFile</code>, <code>SourceDir</code>,
     <code>LineNumberTable</code>, <code>LocalVariableTable</code>,
     <code>LocalVariableTypeTable</code>, <code>Synthetic</code>,
     <code>EnclosingMethod</code>, <code>RuntimeVisibleAnnotations</code>,
@@ -592,8 +626,20 @@ The sections below provide more details:
     applications produce <a href="examples.html#stacktrace">useful obfuscated
     stack traces</a>. Only applicable when obfuscating.</dd>
 
+<dt><a name="adaptclassstrings"><code><b>-adaptclassstrings</b></code></a>
+    [<i><a href="#filters">class_filter</a></i>]</dt>
+
+<dd>Specifies that string constants that correspond to class names should be
+    obfuscated as well. Without a filter, all string constants that correspond
+    to class names are adapted. With a filter, only string constants in
+    classes that match the filter are adapted. For example, if your code
+    contains a large number of hard-coded strings that refer to classes, and
+    you prefer not to keep their names, you may want to use this option.
+    Primarily applicable when obfuscating, although corresponding classes are
+    automatically kept in the shrinking step too.</dd>
+
 <dt><a name="adaptresourcefilenames"><code><b>-adaptresourcefilenames</b></code></a>
-    [<i><a href="#filters">filter</a></i>]</dt>
+    [<i><a href="#filefilters">file_filter</a></i>]</dt>
 
 <dd>Specifies the resource files to be renamed, based on the obfuscated names
     of the corresponding class files (if any). Without a filter, all resource
@@ -603,15 +649,18 @@ The sections below provide more details:
     applicable when obfuscating.</dd>
 
 <dt><a name="adaptresourcefilecontents"><code><b>-adaptresourcefilecontents</b></code></a>
-    [<i><a href="#filters">filter</a></i>]</dt>
+    [<i><a href="#filefilters">file_filter</a></i>]</dt>
 
 <dd>Specifies the resource files whose contents are to be updated. Any class
     names mentioned in the resource files are renamed, based on the obfuscated
-    names of the corresponding class files (if any). Without a filter, the
+    names of the corresponding classes (if any). Without a filter, the
     contents of all resource files updated. With a filter, only matching files
-    are updated. For example, see <a
-    href="examples.html#resourcefiles">processing resource files</a>. Only
-    applicable when obfuscating.</dd>
+    are updated. The resource files are parsed and written using the
+    platform's default character set. You can change this default character set
+    by setting the environment variable <code>LANG</code> or the Java system
+    property <code>file.encoding</code>. For an example,
+    see <a href="examples.html#resourcefiles">processing resource files</a>.
+    Only applicable when obfuscating.</dd>
 
 </dl>
 <p>
@@ -651,19 +700,24 @@ The sections below provide more details:
     program terminates with an exception, this option will print out the entire
     stack trace, instead of just the exception message.</dd>
 
-<dt><a name="dontnote"><code><b>-dontnote</b></code></a></dt>
+<dt><a name="dontnote"><code><b>-dontnote</b></code></a>
+    [<i><a href="#filters">class_filter</a></i>]</dt>
 
 <dd>Specifies not to print notes about potential mistakes or omissions in the
-    configuration, like typos in class names, or missing options that might be
-    useful.</dd>
+    configuration, like typos in class names, or like missing options that
+    might be useful. The optional filter is a regular expression; ProGuard
+    doesn't print notes about classes with matching names.</dd>
 
-<dt><a name="dontwarn"><code><b>-dontwarn</b></code></a></dt>
+<dt><a name="dontwarn"><code><b>-dontwarn</b></code></a>
+    [<i><a href="#filters">class_filter</a></i>]</dt>
 
 <dd>Specifies not to warn about unresolved references and other important
-    problems at all. Ignoring warnings can be dangerous. For instance, if the
-    unresolved classes or class members are indeed required for processing,
-    the processed code will not function properly. <i>Only use this option if
-    you know what you're doing!</i></dd>
+    problems at all. The optional filter is a regular expression; ProGuard
+    doesn't print warnings about classes with matching names. Ignoring
+    warnings can be dangerous. For instance, if the unresolved classes or
+    class members are indeed required for processing, the processed code will
+    not function properly. <i>Only use this option if you know what you're
+    doing!</i></dd>
 
 <dt><a name="ignorewarnings"><code><b>-ignorewarnings</b></code></a></dt>
 
@@ -741,10 +795,49 @@ to <a href="examples.html#restructuring">restructure output archives</a>.
 Files and directories can be specified as discussed in the section on <a
 href="#filename">file names</a> below.
 <p>
-In addition, the individual class patch entries and their contents can be
-filtered, as explained in the <a href="#filters">filters</a> section below.
-This allows for an almost infinite number of packaging and repackaging
-possibilities.
+In addition, ProGuard provides the possibility to filter the class path
+entries and their contents, based on their full relative file names. Each
+class path entry can be followed by up to 5 types of <a
+href="#filefilters">file filters</a> between parentheses, separated by
+semi-colons:
+<ul>
+<li>A filter for all zip names that are encountered,
+<li>A filter for all ear names that are encountered,
+<li>A filter for all war names that are encountered,
+<li>A filter for all jar names that are encountered,
+<li>A filter for all class file names and resource file names that are
+    encountered.
+</ul>
+<p>
+If fewer than 5 filters are specified, they are assumed to be the latter
+filters. Any empty filters are ignored. More formally, a filtered class path
+entry looks like this:
+<pre>
+<i>classpathentry</i><b>(</b>[[[[<i>zipfilter</i><b>;</b>]<i>earfilter</i><b>;</b>]<i>warfilter</i><b>;</b>]<i>jarfilter</i><b>;</b>]<i>filefilter</i><b>)</b>
+</pre>
+<p>
+Square brackets "[]" mean that their contents are optional.
+<p>
+For example, "<code>rt.jar(java/**.class,javax/**.class)</code>" matches all
+class files in the <code>java</code> and <code>javax</code> directories inside
+the <code>rt</code> jar.
+<p>
+For example, "<code>input.jar(!**.gif,images/**)</code>" matches all files in
+the <code>images</code> directory inside the <code>input</code> jar, except
+gif files.
+<p>
+Note that the different filters are applied to all corresponding file types,
+irrespective of their nesting levels in the input; they are orthogonal.
+<p>
+For example,
+"<code>input.war(lib/**.jar,support/**.jar;**.class,**.gif)</code>" only
+considers jar files in the <code>lib</code> and <code>support</code>
+directories in the <code>input</code> war, not any other jar files. It then
+matches all class files and gif files that are encountered.
+<p>
+The filters allow for an almost infinite number of packaging and repackaging
+possibilities. The Examples section provides a few more examples
+for <a href="examples.html#filtering">filtering input and output</a>.
 <p>
 
 <a name="filename"> </a>
@@ -779,36 +872,13 @@ For example, on the command line, you could use an option like <code>'-injars
 "my program.jar":"/your directory/your program.jar"'</code>.
 <p>
 
-<a name="filters"> </a>
-<h2>Filters</h2>
+<a name="filefilters"> </a>
+<h2>File Filters</h2>
 
-ProGuard provides the possibility to filter the class path entries and their
-contents, based on their full relative file names. Each class path entry can
-be followed by up to 5 types of filters between parentheses, separated by
-semi-colons:
-<ul>
-<li>A filter for all zip names that are encountered,
-<li>A filter for all ear names that are encountered,
-<li>A filter for all war names that are encountered,
-<li>A filter for all jar names that are encountered,
-<li>A filter for all class file names and resource file names that are
-    encountered.
-</ul>
-<p>
-If fewer than 5 filters are specified, they are assumed to be the latter
-filters. Any empty filters are ignored. More formally, a filtered class path
-entry looks like this:
-<pre>
-<i>classpathentry</i><b>(</b>[[[[<i>zipfilter</i><b>;</b>]<i>earfilter</i><b>;</b>]<i>warfilter</i><b>;</b>]<i>jarfilter</i><b>;</b>]<i>filefilter</i><b>)</b>
-</pre>
-<p>
-Square brackets "[]" mean that their contents are optional.
-
-<p>
-A filter consists of one or more comma-separated file names that can contain
-wildcards. Only files with matching file names are read (in the case of input
-jars), or written (in the case of output jars). The following wildcards are
-supported:
+Like general <a href="#filters">filters</a>, a file filter is a
+comma-separated list of file names that can contain wildcards. Only files with
+matching file names are read (in the case of input jars), or written (in the
+case of output jars). The following wildcards are supported:
 
 <table cellspacing="10">
 <tr><td valign="top"><code><b>?</b></code></td>
@@ -821,32 +891,58 @@ supported:
         directory separators.</td></tr>
 </table>
 
-For example, "<code>rt.jar(java/**.class,javax/**.class)</code>" matches all
-class files in the <code>java</code> and <code>javax</code> directories inside
-the <code>rt</code> jar.
+For example, "<code>java/**.class,javax/**.class</code>" matches all
+class files in the <code>java</code> and <code>javax</code>.
 <p>
 
 Furthermore, a file name can be preceded by an exclamation mark '<b>!</b>' to
 <i>exclude</i> the file name from further attempts to match with
-<i>subsequent</i> file names. You can think of the filtering mechanism as a
-conveyor belt, with pushers that accept or reject files as they pass by.
+<i>subsequent</i> file names.
 <p>
-For example, "<code>input.jar(!**.gif,images/**)</code>" matches all files in
-the <code>images</code> directory inside the <code>input</code> jar, except
-gif files.
+For example, "<code>!**.gif,images/**</code>" matches all files in the
+<code>images</code> directory, except gif files.
 <p>
+The Examples section provides a few more examples for <a
+href="examples.html#filtering">filtering input and output</a>.
 
-Note that the different filters are applied to all corresponding file types,
-irrespective of their nesting levels in the input; they are orthogonal.
+<a name="filters"> </a>
+<h2>Filters</h2>
+
+ProGuard offers options with filters for many different aspects of the
+configuration: names of files, directories, classes, packages, attributes,
+optimizations, etc. 
 <p>
-For example,
-"<code>input.war(lib/**.jar,support/**.jar;**.class,**.gif)</code>" only
-considers jar files in the <code>lib</code> and <code>support</code>
-directories in the <code>input</code> war, not any other jar files. It then
-matches all class files and gif files that are encountered.
+A filter is a list of comma-separated names that can contain wildcards. Only
+names that match an item on the list pass the filter. The supported wildcards
+depend on the type of names for which the filter is being used, but the
+following wildcards are typical:
+
+<table cellspacing="10">
+<tr><td valign="top"><code><b>?</b></code></td>
+    <td>matches any single character in a name.</td></tr>
+<tr><td valign="top"><code><b>*</b></code></td>
+    <td>matches any part of a name not containing the package separator or
+        directory separator.</td></tr>
+<tr><td valign="top"><code><b>**</b></code></td>
+    <td>matches any part of a name, possibly containing any number of
+        package separators or directory separators.</td></tr>
+</table>
+
+For example, "<code>foo,*bar</code>" matches the name <code>foo</code> and
+all names ending with <code>bar</code>.
+<p>
+
+Furthermore, a name can be preceded by a negating exclamation mark '<b>!</b>'
+to <i>exclude</i> the name from further attempts to match
+with <i>subsequent</i> names. So, if a name matches an item in the filter, it
+is accepted or rejected right away, depending on whether the item has a
+negator. If the name doesn't match the item, it is tested against the next
+item, and so on. It if doesn't match any items, it is accepted or rejected,
+depending on the whether the last item has a negator or not.
+<p>
+For example, "<code>!foobar,*bar</code>" matches all names ending with
+<code>bar</code>, except <code>foobar</code>.
 <p>
- The Examples section provides a few more examples for <a
- href="examples.html#filtering">filtering input and output</a>.
 
 <a name="keepoverview"> </a>
 <h2>Overview of <code>Keep</code> Options</h2>
@@ -1128,7 +1224,7 @@ files.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/manual/wtk.html b/docs/manual/wtk.html
index 30fde13..b8c70b5 100644
--- a/docs/manual/wtk.html
+++ b/docs/manual/wtk.html
@@ -51,7 +51,7 @@ that's inside the <code>proguard.jar</code>.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/quality.html b/docs/quality.html
index 4a375f1..29889e3 100644
--- a/docs/quality.html
+++ b/docs/quality.html
@@ -37,7 +37,7 @@ problems as soon as possible.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/results.html b/docs/results.html
index 3ef8470..480cd4f 100644
--- a/docs/results.html
+++ b/docs/results.html
@@ -147,7 +147,7 @@ is governed by the basic java virtual machine and by the total size of the
 library jars and program jars.
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/screenshots.html b/docs/screenshots.html
index 954fea0..8718839 100644
--- a/docs/screenshots.html
+++ b/docs/screenshots.html
@@ -49,7 +49,7 @@ You can click on the image to see the full-size version.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 </body>
diff --git a/docs/sections.html b/docs/sections.html
index efa7012..d955d33 100644
--- a/docs/sections.html
+++ b/docs/sections.html
@@ -38,6 +38,10 @@ document.write(document.location.hostname == "proguard.sourceforge.net" ?
   "http://sourceforge.net/sflogo.php?group_id=54750&type=1" :
   "sflogo.png");
 document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
+if (document.location.hostname == "proguard.sourceforge.net") {
+  document.write("<script type=\"text/javascript\" src=\"http://sourceforge.net/apps/piwik/proguard/piwik.js\"></scri"+"pt>");
+  document.write("<script type=\"text/javascript\">piwik_log(\"\", 1, \"http://sourceforge.net/apps/piwik/proguard/piwik.php\");</scri"+"pt>");
+}
 //-->
 </script>
 <noscript>
diff --git a/docs/sflogo.png b/docs/sflogo.png
index 1105bc7..142a6f9 100644
Binary files a/docs/sflogo.png and b/docs/sflogo.png differ
diff --git a/docs/testimonials.html b/docs/testimonials.html
index 79becde..6971617 100644
--- a/docs/testimonials.html
+++ b/docs/testimonials.html
@@ -114,7 +114,7 @@ You could've been rich.
 
 <hr>
 <address>
-Copyright © 2002-2008
+Copyright © 2002-2009
 <a href="http://www.graphics.cornell.edu/~eric/">Eric Lafortune</a>.
 </address>
 
diff --git a/docs/title.html b/docs/title.html
index 841d5be..33171b5 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.3</div>
+<div>Version 4.4</div>
 </div>
 
 </body>
diff --git a/examples/annotations/examples/Applet.java b/examples/annotations/examples/Applet.java
index 5a04887..8a5874b 100644
--- a/examples/annotations/examples/Applet.java
+++ b/examples/annotations/examples/Applet.java
@@ -3,8 +3,10 @@ import proguard.annotation.*;
 /**
  * This applet illustrates the use of annotations for configuring ProGuard.
  *
- * After having been compiled, it can be processed using:
- *     java -jar proguard.jar @examples.pro
+ * You can compile it with:
+ *     javac -classpath ../lib/annotations.jar Applet.java
+ * You can then process it with:
+ *     java -jar ../../../lib/proguard.jar @ ../examples.pro
  *
  * The annotation will preserve the class and its essential methods.
  */
diff --git a/examples/annotations/examples/Application.java b/examples/annotations/examples/Application.java
index 96ac208..f8d5060 100644
--- a/examples/annotations/examples/Application.java
+++ b/examples/annotations/examples/Application.java
@@ -3,8 +3,10 @@ import proguard.annotation.KeepApplication;
 /**
  * This application illustrates the use of annotations for configuring ProGuard.
  *
- * After having been compiled, it can be processed using:
- *     java -jar proguard.jar @examples.pro
+ * You can compile it with:
+ *     javac -classpath ../lib/annotations.jar Application.java
+ * You can then process it with:
+ *     java -jar ../../../lib/proguard.jar @ ../examples.pro
  *
  * The annotation will preserve the class and its main method.
  */
diff --git a/examples/annotations/examples/Bean.java b/examples/annotations/examples/Bean.java
index e49a88e..0544bf3 100644
--- a/examples/annotations/examples/Bean.java
+++ b/examples/annotations/examples/Bean.java
@@ -3,8 +3,10 @@ import proguard.annotation.*;
 /**
  * This bean illustrates the use of annotations for configuring ProGuard.
  *
- * After having been compiled, it can be processed using:
- *     java -jar proguard.jar @examples.pro
+ * You can compile it with:
+ *     javac -classpath ../lib/annotations.jar Bean.java
+ * You can then process it with:
+ *     java -jar ../../../lib/proguard.jar @ ../examples.pro
  *
  * The annotations will preserve the class and its public getters and setters.
  */
diff --git a/examples/annotations/examples/NativeCallBack.java b/examples/annotations/examples/NativeCallBack.java
index 9c34884..2c72f7b 100644
--- a/examples/annotations/examples/NativeCallBack.java
+++ b/examples/annotations/examples/NativeCallBack.java
@@ -3,8 +3,10 @@ import proguard.annotation.*;
 /**
  * This application illustrates the use of annotations for configuring ProGuard.
  *
- * After having been compiled, it can be processed using:
- *     java -jar proguard.jar @examples.pro
+ * You can compile it with:
+ *     javac -classpath ../lib/annotations.jar NativeCallBack.java
+ * You can then process it with:
+ *     java -jar ../../../lib/proguard.jar @ ../examples.pro
  *
  * The annotation will preserve the class and its main method.
  */
diff --git a/examples/annotations/lib/annotations.jar b/examples/annotations/lib/annotations.jar
index 8dd8fa7..967d604 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 13ca2ae..315e628 100644
--- a/examples/ant/proguard.xml
+++ b/examples/ant/proguard.xml
@@ -55,6 +55,7 @@
 
     <!-- If we have ant.jar, we can properly process the Ant task. -->
 
+    <keeppackagename name="proguard.ant" />
     <keep name="proguard.ant.*" allowobfuscation="true" />
     <keepclassmembers access="public" name="proguard.ant.*">
       <constructor parameters="org.apache.tools.ant.Project" />
diff --git a/lib/proguard.jar b/lib/proguard.jar
new file mode 100644
index 0000000..3a9aebc
Binary files /dev/null and b/lib/proguard.jar differ
diff --git a/lib/proguardgui.jar b/lib/proguardgui.jar
new file mode 100644
index 0000000..5866856
Binary files /dev/null and b/lib/proguardgui.jar differ
diff --git a/lib/retrace.jar b/lib/retrace.jar
new file mode 100644
index 0000000..8e42e5a
Binary files /dev/null and b/lib/retrace.jar differ
diff --git a/src/proguard/ArgumentWordReader.java b/src/proguard/ArgumentWordReader.java
index 4d0bc2b..89f3824 100644
--- a/src/proguard/ArgumentWordReader.java
+++ b/src/proguard/ArgumentWordReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ClassPath.java b/src/proguard/ClassPath.java
index 80fb1ca..f4eeaad 100644
--- a/src/proguard/ClassPath.java
+++ b/src/proguard/ClassPath.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ClassPathEntry.java b/src/proguard/ClassPathEntry.java
index e7b4240..28483be 100644
--- a/src/proguard/ClassPathEntry.java
+++ b/src/proguard/ClassPathEntry.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,6 +21,7 @@
 package proguard;
 
 import java.io.*;
+import java.util.List;
 
 
 /**
@@ -35,11 +36,11 @@ public class ClassPathEntry
 {
     private File    file;
     private boolean output;
-    private String  filter;
-    private String  jarFilter;
-    private String  warFilter;
-    private String  earFilter;
-    private String  zipFilter;
+    private List    filter;
+    private List    jarFilter;
+    private List    warFilter;
+    private List    earFilter;
+    private List    zipFilter;
 
 
     /**
@@ -92,85 +93,57 @@ public class ClassPathEntry
     }
 
 
-    public String getFilter()
+    public List getFilter()
     {
         return filter;
     }
 
-    public void setFilter(String filter)
+    public void setFilter(List filter)
     {
-        this.filter = filter == null || filter.length() == 0 ? null : filter;
+        this.filter = filter == null || filter.size() == 0 ? null : filter;
     }
 
 
-    public String getJarFilter()
+    public List getJarFilter()
     {
         return jarFilter;
     }
 
-    public void setJarFilter(String filter)
+    public void setJarFilter(List filter)
     {
-        this.jarFilter = filter == null || filter.length() == 0 ? null : filter;
+        this.jarFilter = filter == null || filter.size() == 0 ? null : filter;
     }
 
 
-    public String getWarFilter()
+    public List getWarFilter()
     {
         return warFilter;
     }
 
-    public void setWarFilter(String filter)
+    public void setWarFilter(List filter)
     {
-        this.warFilter = filter == null || filter.length() == 0 ? null : filter;
+        this.warFilter = filter == null || filter.size() == 0 ? null : filter;
     }
 
 
-    public String getEarFilter()
+    public List getEarFilter()
     {
         return earFilter;
     }
 
-    public void setEarFilter(String filter)
+    public void setEarFilter(List filter)
     {
-        this.earFilter = filter == null || filter.length() == 0 ? null : filter;
+        this.earFilter = filter == null || filter.size() == 0 ? null : filter;
     }
 
 
-    public String getZipFilter()
+    public List getZipFilter()
     {
         return zipFilter;
     }
 
-    public void setZipFilter(String filter)
+    public void setZipFilter(List filter)
     {
-        this.zipFilter = filter == null || filter.length() == 0 ? null : filter;
-    }
-
-
-    public String toString()
-    {
-        String string = getName();
-
-        if (filter    != null ||
-            jarFilter != null ||
-            warFilter != null ||
-            earFilter != null ||
-            zipFilter != null)
-        {
-            string +=
-                ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD +
-                (zipFilter != null ? zipFilter : "")  +
-                ConfigurationConstants.SEPARATOR_KEYWORD +
-                (earFilter != null ? earFilter : "")  +
-                ConfigurationConstants.SEPARATOR_KEYWORD +
-                (warFilter != null ? warFilter : "")  +
-                ConfigurationConstants.SEPARATOR_KEYWORD +
-                (jarFilter != null ? jarFilter : "")  +
-                ConfigurationConstants.SEPARATOR_KEYWORD +
-                (filter    != null ? filter    : "")  +
-                ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD;
-        }
-
-        return string;
+        this.zipFilter = filter == null || filter.size() == 0 ? null : filter;
     }
 }
diff --git a/src/proguard/ClassSpecification.java b/src/proguard/ClassSpecification.java
index cfc318b..a84e0c8 100644
--- a/src/proguard/ClassSpecification.java
+++ b/src/proguard/ClassSpecification.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -33,15 +33,15 @@ import java.util.*;
 public class ClassSpecification implements Cloneable
 {
     public final String comments;
-    public int    requiredSetAccessFlags;
-    public int    requiredUnsetAccessFlags;
+    public       int    requiredSetAccessFlags;
+    public       int    requiredUnsetAccessFlags;
     public final String annotationType;
-    public String className;
+    public       String className;
     public final String extendsAnnotationType;
     public final String extendsClassName;
 
-    public List   fieldSpecifications;
-    public List   methodSpecifications;
+    public       List   fieldSpecifications;
+    public       List   methodSpecifications;
 
 
     /**
@@ -220,15 +220,15 @@ public class ClassSpecification implements Cloneable
 
         ClassSpecification other = (ClassSpecification)object;
         return
-//          (this.comments                 == null ? other.comments                    == null : this.comments.equals(other.comments)                                    ) &&
-            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags                                                                                                ) &&
-            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags                                                                                              ) &&
-            (this.annotationType           == null ? other.annotationType         == null : this.annotationType.equals(other.annotationType)              ) &&
-            (this.className                == null ? other.className                  == null : this.className.equals(other.className)                                  ) &&
+//          (this.comments                 == null ? other.comments              == null : this.comments.equals(other.comments)                          ) &&
+            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags                                                                               ) &&
+            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags                                                                             ) &&
+            (this.annotationType           == null ? other.annotationType        == null : this.annotationType.equals(other.annotationType)              ) &&
+            (this.className                == null ? other.className             == null : this.className.equals(other.className)                        ) &&
             (this.extendsAnnotationType    == null ? other.extendsAnnotationType == null : this.extendsAnnotationType.equals(other.extendsAnnotationType)) &&
-            (this.extendsClassName         == null ? other.extendsClassName           == null : this.extendsClassName.equals(other.extendsClassName)                    ) &&
-            (this.fieldSpecifications      == null ? other.fieldSpecifications        == null : this.fieldSpecifications.equals(other.fieldSpecifications)              ) &&
-            (this.methodSpecifications     == null ? other.methodSpecifications       == null : this.methodSpecifications.equals(other.methodSpecifications)            );
+            (this.extendsClassName         == null ? other.extendsClassName      == null : this.extendsClassName.equals(other.extendsClassName)          ) &&
+            (this.fieldSpecifications      == null ? other.fieldSpecifications   == null : this.fieldSpecifications.equals(other.fieldSpecifications)    ) &&
+            (this.methodSpecifications     == null ? other.methodSpecifications  == null : this.methodSpecifications.equals(other.methodSpecifications)  );
     }
 
     public int hashCode()
diff --git a/src/proguard/ClassSpecificationVisitorFactory.java b/src/proguard/ClassSpecificationVisitorFactory.java
index 727e51b..c99ab2c 100644
--- a/src/proguard/ClassSpecificationVisitorFactory.java
+++ b/src/proguard/ClassSpecificationVisitorFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -38,15 +38,15 @@ public class ClassSpecificationVisitorFactory
      * Constructs a ClassPoolVisitor to efficiently travel to the specified
      * classes and class members.
      *
-     * @param keepSpecifications the list of KeepSpecification instances,
-     *                           defining of the classes and class members to
-     *                           visit.
-     * @param classVisitor       the ClassVisitor to be applied to matching
-     *                           classes.
-     * @param memberVisitor      the MemberVisitor to be applied to matching
-     *                           class members.
+     * @param keepClassSpecifications the list of KeepClassSpecification
+     *                                instances, defining of the classes and
+     *                                class members to visit.
+     * @param classVisitor            the ClassVisitor to be applied to matching
+     *                                classes.
+     * @param memberVisitor           the MemberVisitor to be applied to matching
+     *                                class members.
      */
-    public static ClassPoolVisitor createClassPoolVisitor(List          keepSpecifications,
+    public static ClassPoolVisitor createClassPoolVisitor(List          keepClassSpecifications,
                                                           ClassVisitor  classVisitor,
                                                           MemberVisitor memberVisitor,
                                                           boolean       shrinking,
@@ -55,19 +55,19 @@ public class ClassSpecificationVisitorFactory
     {
         MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
 
-        if (keepSpecifications != null)
+        if (keepClassSpecifications != null)
         {
-            for (int index = 0; index < keepSpecifications.size(); index++)
+            for (int index = 0; index < keepClassSpecifications.size(); index++)
             {
-                KeepSpecification keepSpecification =
-                    (KeepSpecification)keepSpecifications.get(index);
+                KeepClassSpecification keepClassSpecification =
+                    (KeepClassSpecification)keepClassSpecifications.get(index);
 
-                if ((shrinking   && !keepSpecification.allowShrinking)    ||
-                    (optimizing  && !keepSpecification.allowOptimization) ||
-                    (obfuscating && !keepSpecification.allowObfuscation))
+                if ((shrinking   && !keepClassSpecification.allowShrinking)    ||
+                    (optimizing  && !keepClassSpecification.allowOptimization) ||
+                    (obfuscating && !keepClassSpecification.allowObfuscation))
                 {
                     multiClassPoolVisitor.addClassPoolVisitor(
-                        createClassPoolVisitor(keepSpecification,
+                        createClassPoolVisitor(keepClassSpecification,
                                                classVisitor,
                                                memberVisitor));
                 }
@@ -118,44 +118,44 @@ public class ClassSpecificationVisitorFactory
      * Constructs a ClassPoolVisitor to efficiently travel to the specified
      * classes and class members.
      *
-     * @param keepSpecification the specifications of the class(es) and class
+     * @param keepClassSpecification the specifications of the class(es) and class
      *                          members to visit.
      * @param classVisitor      the ClassVisitor to be applied to matching
      *                          classes.
      * @param memberVisitor     the MemberVisitor to be applied to matching
      *                          class members.
      */
-    private static ClassPoolVisitor createClassPoolVisitor(KeepSpecification keepSpecification,
+    private static ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification,
                                                            ClassVisitor      classVisitor,
                                                            MemberVisitor     memberVisitor)
     {
         // Don't  visit the classes if not specified.
-        if (!keepSpecification.markClasses &&
-            !keepSpecification.markConditionally)
+        if (!keepClassSpecification.markClasses &&
+            !keepClassSpecification.markConditionally)
         {
             classVisitor = null;
         }
 
         // If specified, let the marker visit the class and its class
         // members conditionally.
-        if (keepSpecification.markConditionally)
+        if (keepClassSpecification.markConditionally)
         {
             // Combine both visitors.
             ClassVisitor composedClassVisitor =
-                createCombinedClassVisitor(keepSpecification,
+                createCombinedClassVisitor(keepClassSpecification,
                                            classVisitor,
                                            memberVisitor);
 
             // Replace the class visitor.
             classVisitor =
-                createClassMemberTester(keepSpecification,
+                createClassMemberTester(keepClassSpecification,
                                         composedClassVisitor);
 
             // Discard the member visitor, because it has already been included.
             memberVisitor = null;
         }
 
-        return createClassPoolVisitor((ClassSpecification)keepSpecification,
+        return createClassPoolVisitor((ClassSpecification)keepClassSpecification,
                                       classVisitor,
                                       memberVisitor);
     }
@@ -192,7 +192,7 @@ public class ClassSpecificationVisitorFactory
         // If wildcarded, only visit classes with matching names.
         if (className != null &&
             (extendsAnnotationType != null ||
-             extendsClassName           != null ||
+             extendsClassName      != null ||
              containsWildCards(className)))
         {
             composedClassVisitor =
diff --git a/src/proguard/Configuration.java b/src/proguard/Configuration.java
index 473a4fb..d513e2c 100644
--- a/src/proguard/Configuration.java
+++ b/src/proguard/Configuration.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -59,6 +59,15 @@ public class Configuration
     public boolean   skipNonPublicLibraryClassMembers = true;
 
     /**
+     * A list of <code>String</code>s specifying directories to be kept in
+     * the output directories or the output jars. A <code>null</code> list
+     * means no directories. An empty list means all directories. The directory
+     * names may contain "**", "*", or "?" wildcards, and they may be preceded
+     * by the "!" negator.
+     */
+    public List      keepDirectories;
+
+    /**
      * Specifies the version number of the output classes, or 0 if the version
      * number can be left unchanged.
      */
@@ -78,7 +87,7 @@ public class Configuration
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * A list of {@link KeepSpecification} instances, whose class names and
+     * A list of {@link KeepClassSpecification} instances, whose class names and
      * class member names are to be kept from shrinking, optimization, and/or
      * obfuscation.
      */
@@ -121,6 +130,14 @@ public class Configuration
     public boolean   optimize                         = true;
 
     /**
+     * A list of <code>String</code>s specifying the optimizations to be
+     * performed. A <code>null</code> list means all optimizations. The
+     * optimization names may contain "*" or "?" wildcards, and they may
+     * be preceded by the "!" negator.
+     */
+    public List      optimizations;
+
+    /**
      * Specifies the number of optimization passes.
      */
     public int       optimizationPasses               = 1;
@@ -192,6 +209,14 @@ public class Configuration
     public boolean   useMixedCaseClassNames           = true;
 
     /**
+     * A list of <code>String</code>s specifying package names to be kept.
+     * A <code>null</code> list means no names. An empty list means all
+     * names. The package names may contain "**", "*", or "?" wildcards, and
+     * they may be preceded by the "!" negator.
+     */
+    public List      keepPackageNames;
+
+    /**
      * An optional base package if the obfuscated package hierarchy is to be
      * flattened, <code>null</code> otherwise.
      */
@@ -217,6 +242,13 @@ public class Configuration
     public String    newSourceFileAttribute;
 
     /**
+     * A list of <code>String</code>s specifying a filter for clases whose
+     * string constants are to be adapted, based on corresponding obfuscated
+     * class names.
+     */
+    public List      adaptClassStrings;
+
+    /**
      * A list of <code>String</code>s specifying a filter for files whose
      * names are to be adapted, based on corresponding obfuscated class names.
      */
@@ -254,14 +286,20 @@ public class Configuration
     public boolean   verbose                          = false;
 
     /**
-     * Specifies whether to print any notes.
+     * A list of <code>String</code>s specifying a filter for the classes for
+     * which not to print notes, if there are noteworthy potential problems.
+     * A <code>null</code> list means all classes. The class names may contain
+     * "**", "*", or "?" wildcards, and they may be preceded by the "!" negator.
      */
-    public boolean   note                             = true;
+    public List      note                             = null;
 
     /**
-     * Specifies whether to print any warnings.
+     * A list of <code>String</code>s specifying a filter for the classes for
+     * which not to print warnings, if there are any problems.
+     * A <code>null</code> list means all classes. The class names may contain
+     * "**", "*", or "?" wildcards, and they may be preceded by the "!" negator.
      */
-    public boolean   warn                             = true;
+    public List      warn                             = null;
 
     /**
      * Specifies whether to ignore any warnings.
diff --git a/src/proguard/ConfigurationConstants.java b/src/proguard/ConfigurationConstants.java
index 1baf79f..694e006 100644
--- a/src/proguard/ConfigurationConstants.java
+++ b/src/proguard/ConfigurationConstants.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -53,6 +53,7 @@ class ConfigurationConstants
     public static final String WHY_ARE_YOU_KEEPING_OPTION = "-whyareyoukeeping";
 
     public static final String DONT_OPTIMIZE_OPTION                 = "-dontoptimize";
+    public static final String OPTIMIZATIONS                        = "-optimizations";
     public static final String OPTIMIZATION_PASSES                  = "-optimizationpasses";
     public static final String ASSUME_NO_SIDE_EFFECTS_OPTION        = "-assumenosideeffects";
     public static final String ALLOW_ACCESS_MODIFICATION_OPTION     = "-allowaccessmodification";
@@ -67,11 +68,13 @@ class ConfigurationConstants
     public static final String OVERLOAD_AGGRESSIVELY_OPTION           = "-overloadaggressively";
     public static final String USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION   = "-useuniqueclassmembernames";
     public static final String DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION = "-dontusemixedcaseclassnames";
+    public static final String KEEP_PACKAGE_NAMES_OPTION              = "-keeppackagenames";
     public static final String FLATTEN_PACKAGE_HIERARCHY_OPTION       = "-flattenpackagehierarchy";
     public static final String REPACKAGE_CLASSES_OPTION               = "-repackageclasses";
     public static final String DEFAULT_PACKAGE_OPTION                 = "-defaultpackage";
     public static final String KEEP_ATTRIBUTES_OPTION                 = "-keepattributes";
     public static final String RENAME_SOURCE_FILE_ATTRIBUTE_OPTION    = "-renamesourcefileattribute";
+    public static final String ADAPT_CLASS_STRINGS_OPTION             = "-adaptclassstrings";
     public static final String ADAPT_RESOURCE_FILE_NAMES_OPTION       = "-adaptresourcefilenames";
     public static final String ADAPT_RESOURCE_FILE_CONTENTS_OPTION    = "-adaptresourcefilecontents";
 
@@ -87,6 +90,7 @@ class ConfigurationConstants
     public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION       = "-dontskipnonpubliclibraryclasses";
     public static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION = "-dontskipnonpubliclibraryclassmembers";
     public static final String TARGET_OPTION                                     = "-target";
+    public static final String KEEP_DIRECTORIES_OPTION                           = "-keepdirectories";
     public static final String FORCE_PROCESSING_OPTION                           = "-forceprocessing";
 
     public static final String ANY_ATTRIBUTE_KEYWORD       = "*";
diff --git a/src/proguard/ConfigurationParser.java b/src/proguard/ConfigurationParser.java
index 7897763..e01809e 100644
--- a/src/proguard/ConfigurationParser.java
+++ b/src/proguard/ConfigurationParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -115,20 +115,24 @@ public class ConfigurationParser
             else if (ConfigurationConstants.TARGET_OPTION                                    .startsWith(nextWord)) configuration.targetClassVersion               = parseClassVersion();
             else if (ConfigurationConstants.FORCE_PROCESSING_OPTION                          .startsWith(nextWord)) configuration.lastModified                     = parseNoArgument(Long.MAX_VALUE);
 
-            else if (ConfigurationConstants.KEEP_OPTION                                      .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, true,  false, false);
-            else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION                        .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, false, false, false);
-            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION                 .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, false, true,  false);
-            else if (ConfigurationConstants.KEEP_NAMES_OPTION                                .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, true,  false, true);
-            else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION                   .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, false, false, true);
-            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION            .startsWith(nextWord)) configuration.keep                             = parseKeepSpecificationArguments(configuration.keep, false, true,  true);
+            else if (ConfigurationConstants.KEEP_OPTION                                      .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, true,  false, false);
+            else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION                        .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, false, false, false);
+            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION                 .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, false, true,  false);
+            else if (ConfigurationConstants.KEEP_NAMES_OPTION                                .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, true,  false, true);
+            else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION                   .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, false, false, true);
+            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION            .startsWith(nextWord)) configuration.keep                             = parseKeepClassSpecificationArguments(configuration.keep, false, true,  true);
             else if (ConfigurationConstants.PRINT_SEEDS_OPTION                               .startsWith(nextWord)) configuration.printSeeds                       = parseOptionalFile();
 
+            // After '-keep'.
+            else if (ConfigurationConstants.KEEP_DIRECTORIES_OPTION                          .startsWith(nextWord)) configuration.keepDirectories                  = parseCommaSeparatedList("directory name", true, true, false, false, true, false, false, configuration.keepDirectories);
+
             else if (ConfigurationConstants.DONT_SHRINK_OPTION                               .startsWith(nextWord)) configuration.shrink                           = parseNoArgument(false);
             else if (ConfigurationConstants.PRINT_USAGE_OPTION                               .startsWith(nextWord)) configuration.printUsage                       = parseOptionalFile();
             else if (ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION                       .startsWith(nextWord)) configuration.whyAreYouKeeping                 = parseClassSpecificationArguments(configuration.whyAreYouKeeping);
 
             else if (ConfigurationConstants.DONT_OPTIMIZE_OPTION                             .startsWith(nextWord)) configuration.optimize                         = parseNoArgument(false);
             else if (ConfigurationConstants.OPTIMIZATION_PASSES                              .startsWith(nextWord)) configuration.optimizationPasses               = parseIntegerArgument();
+            else if (ConfigurationConstants.OPTIMIZATIONS                                    .startsWith(nextWord)) configuration.optimizations                    = parseCommaSeparatedList("optimization name", true, false, false, false, false, false, false, configuration.optimizations);
             else if (ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION                    .startsWith(nextWord)) configuration.assumeNoSideEffects              = parseClassSpecificationArguments(configuration.assumeNoSideEffects);
             else if (ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION                 .startsWith(nextWord)) configuration.allowAccessModification          = parseNoArgument(true);
             else if (ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION             .startsWith(nextWord)) configuration.mergeInterfacesAggressively      = parseNoArgument(true);
@@ -142,20 +146,22 @@ public class ConfigurationParser
             else if (ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION                     .startsWith(nextWord)) configuration.overloadAggressively             = parseNoArgument(true);
             else if (ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION             .startsWith(nextWord)) configuration.useUniqueClassMemberNames        = parseNoArgument(true);
             else if (ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION           .startsWith(nextWord)) configuration.useMixedCaseClassNames           = parseNoArgument(false);
+            else if (ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION                        .startsWith(nextWord)) configuration.keepPackageNames                 = parseCommaSeparatedList("package name", true, true, false, true, false, true, false, configuration.keepPackageNames);
             else if (ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION                 .startsWith(nextWord)) configuration.flattenPackageHierarchy          = ClassUtil.internalClassName(parseOptionalArgument());
             else if (ConfigurationConstants.REPACKAGE_CLASSES_OPTION                         .startsWith(nextWord)) configuration.repackageClasses                 = ClassUtil.internalClassName(parseOptionalArgument());
             else if (ConfigurationConstants.DEFAULT_PACKAGE_OPTION                           .startsWith(nextWord)) configuration.repackageClasses                 = ClassUtil.internalClassName(parseOptionalArgument());
-            else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION                           .startsWith(nextWord)) configuration.keepAttributes                   = parseCommaSeparatedList("attribute name", true, true, false, true, false, configuration.keepAttributes);
+            else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION                           .startsWith(nextWord)) configuration.keepAttributes                   = parseCommaSeparatedList("attribute name", true, true, false, true, false, false, false, configuration.keepAttributes);
             else if (ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION              .startsWith(nextWord)) configuration.newSourceFileAttribute           = parseOptionalArgument();
-            else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION                 .startsWith(nextWord)) configuration.adaptResourceFileNames           = parseCommaSeparatedList("resource file name", true, true, false, false, false, configuration.adaptResourceFileNames);
-            else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION              .startsWith(nextWord)) configuration.adaptResourceFileContents        = parseCommaSeparatedList("resource file name", true, true, false, false, false, configuration.adaptResourceFileContents);
+            else if (ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION                       .startsWith(nextWord)) configuration.adaptClassStrings                = parseCommaSeparatedList("class name", true, true, false, true, false, true, false, configuration.adaptClassStrings);
+            else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION                 .startsWith(nextWord)) configuration.adaptResourceFileNames           = parseCommaSeparatedList("resource file name", true, true, false, false, false, false, false, configuration.adaptResourceFileNames);
+            else if (ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION              .startsWith(nextWord)) configuration.adaptResourceFileContents        = parseCommaSeparatedList("resource file name", true, true, false, false, false, false, false, configuration.adaptResourceFileContents);
 
             else if (ConfigurationConstants.DONT_PREVERIFY_OPTION                            .startsWith(nextWord)) configuration.preverify                        = parseNoArgument(false);
             else if (ConfigurationConstants.MICRO_EDITION_OPTION                             .startsWith(nextWord)) configuration.microEdition                     = parseNoArgument(true);
 
             else if (ConfigurationConstants.VERBOSE_OPTION                                   .startsWith(nextWord)) configuration.verbose                          = parseNoArgument(true);
-            else if (ConfigurationConstants.DONT_NOTE_OPTION                                 .startsWith(nextWord)) configuration.note                             = parseNoArgument(false);
-            else if (ConfigurationConstants.DONT_WARN_OPTION                                 .startsWith(nextWord)) configuration.warn                             = parseNoArgument(false);
+            else if (ConfigurationConstants.DONT_NOTE_OPTION                                 .startsWith(nextWord)) configuration.note                             = parseCommaSeparatedList("class name", true, true, false, true, false, true, false, configuration.note);
+            else if (ConfigurationConstants.DONT_WARN_OPTION                                 .startsWith(nextWord)) configuration.warn                             = parseCommaSeparatedList("class name", true, true, false, true, false, true, false, configuration.warn);
             else if (ConfigurationConstants.IGNORE_WARNINGS_OPTION                           .startsWith(nextWord)) configuration.ignoreWarnings                   = parseNoArgument(true);
             else if (ConfigurationConstants.PRINT_CONFIGURATION_OPTION                       .startsWith(nextWord)) configuration.printConfiguration               = parseOptionalFile();
             else if (ConfigurationConstants.DUMP_OPTION                                      .startsWith(nextWord)) configuration.dump                             = parseOptionalFile();
@@ -232,16 +238,14 @@ public class ConfigurationParser
                 ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord))
             {
                 // Read all filters in an array.
-                String[] filters = new String[5];
+                List[] filters = new List[5];
 
                 int counter = 0;
                 do
                 {
                     // Read the filter.
                     filters[counter++] =
-                        ListUtil.commaSeparatedString(
-                        parseCommaSeparatedList("filter", true,
-                                                false, true, false, true, null));
+                        parseCommaSeparatedList("filter", true, false, true, false, true, false, false, null);
                 }
                 while (counter < filters.length &&
                        ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord));
@@ -406,16 +410,16 @@ public class ConfigurationParser
     }
 
 
-    private List parseKeepSpecificationArguments(List    keepSpecifications,
-                                                 boolean markClasses,
-                                                 boolean markConditionally,
-                                                 boolean allowShrinking)
+    private List parseKeepClassSpecificationArguments(List    keepClassSpecifications,
+                                                      boolean markClasses,
+                                                      boolean markConditionally,
+                                                      boolean allowShrinking)
     throws ParseException, IOException
     {
         // Create a new List if necessary.
-        if (keepSpecifications == null)
+        if (keepClassSpecifications == null)
         {
-            keepSpecifications = new ArrayList();
+            keepClassSpecifications = new ArrayList();
         }
 
         //boolean allowShrinking    = false;
@@ -465,14 +469,13 @@ public class ConfigurationParser
             parseClassSpecificationArguments();
 
         // Create and add the keep configuration.
-        keepSpecifications.add(new KeepSpecification(markClasses,
-                                                     markConditionally,
-                                                     allowShrinking,
-                                                     allowOptimization,
-                                                     allowObfuscation,
-                                                     classSpecification));
-
-        return keepSpecifications;
+        keepClassSpecifications.add(new KeepClassSpecification(markClasses,
+                                                               markConditionally,
+                                                               allowShrinking,
+                                                               allowOptimization,
+                                                               allowObfuscation,
+                                                               classSpecification));
+        return keepClassSpecifications;
     }
 
 
@@ -549,10 +552,9 @@ public class ConfigurationParser
                 {
                     // Parse the annotation type.
                     annotationType =
-                        ClassUtil.internalType(
                         ListUtil.commaSeparatedString(
                         parseCommaSeparatedList("annotation type",
-                                                false, false, false, true, false, null)));
+                                                false, false, false, true, false, false, true, null));
 
                     continue;
                 }
@@ -592,7 +594,7 @@ public class ConfigurationParser
         String externalClassName =
             ListUtil.commaSeparatedString(
             parseCommaSeparatedList("class name or interface name",
-                                    true, false, false, true, false, null));
+                                    true, false, false, true, false, false, false, null));
 
         // For backward compatibility, allow a single "*" wildcard to match any
         // class.
@@ -602,7 +604,7 @@ public class ConfigurationParser
 
         // Clear the annotation type and the class name of the extends part.
         String extendsAnnotationType = null;
-        String extendsClassName           = null;
+        String extendsClassName      = null;
 
         if (!configurationEnd())
         {
@@ -616,16 +618,15 @@ public class ConfigurationParser
                 if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord))
                 {
                     extendsAnnotationType =
-                        ClassUtil.internalType(
                         ListUtil.commaSeparatedString(
                         parseCommaSeparatedList("annotation type",
-                                                true, false, false, true, false, null)));
+                                                true, false, false, true, false, false, true, null));
                 }
 
                 String externalExtendsClassName =
                     ListUtil.commaSeparatedString(
                     parseCommaSeparatedList("class name or interface name",
-                                            false, false, false, true, false, null));
+                                            false, false, false, true, false, false, false, null));
 
                 extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ?
                     null :
@@ -694,10 +695,9 @@ public class ConfigurationParser
             if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord))
             {
                 annotationType =
-                    ClassUtil.internalType(
                     ListUtil.commaSeparatedString(
                     parseCommaSeparatedList("annotation type",
-                                            true, false, false, true, false, null)));
+                                            true, false, false, true, false, false, true, null));
 
                 continue;
             }
@@ -875,7 +875,7 @@ public class ConfigurationParser
                 // Parse the method arguments.
                 String descriptor =
                     ClassUtil.internalMethodDescriptor(type,
-                                                       parseCommaSeparatedList("argument", true, true, true, true, false, null));
+                                                       parseCommaSeparatedList("argument", true, true, true, true, false, false, false, null));
 
                 if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord))
                 {
@@ -923,6 +923,8 @@ public class ConfigurationParser
                                          boolean expectClosingParenthesis,
                                          boolean checkJavaIdentifiers,
                                          boolean replaceSystemProperties,
+                                         boolean replaceExternalClassNames,
+                                         boolean replaceExternalTypes,
                                          List    list)
     throws ParseException, IOException
     {
@@ -972,6 +974,16 @@ public class ConfigurationParser
                 nextWord = replaceSystemProperties(nextWord);
             }
 
+            if (replaceExternalClassNames)
+            {
+                nextWord = ClassUtil.internalClassName(nextWord);
+            }
+
+            if (replaceExternalTypes)
+            {
+                nextWord = ClassUtil.internalType(nextWord);
+            }
+
             list.add(nextWord);
 
             if (expectClosingParenthesis)
diff --git a/src/proguard/ConfigurationWriter.java b/src/proguard/ConfigurationWriter.java
index 654dfec..5d112d6 100644
--- a/src/proguard/ConfigurationWriter.java
+++ b/src/proguard/ConfigurationWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -107,6 +107,7 @@ public class ConfigurationWriter
         // Write the other options.
         writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION,       !configuration.skipNonPublicLibraryClasses);
         writeOption(ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION, !configuration.skipNonPublicLibraryClassMembers);
+        writeOption(ConfigurationConstants.KEEP_DIRECTORIES_OPTION,                           configuration.keepDirectories);
         writeOption(ConfigurationConstants.TARGET_OPTION,                                     ClassUtil.externalClassVersion(configuration.targetClassVersion));
         writeOption(ConfigurationConstants.FORCE_PROCESSING_OPTION,                           configuration.lastModified == Long.MAX_VALUE);
 
@@ -114,6 +115,7 @@ public class ConfigurationWriter
         writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage);
 
         writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION,                 !configuration.optimize);
+        writeOption(ConfigurationConstants.OPTIMIZATIONS,                        configuration.optimize ? ListUtil.commaSeparatedString(configuration.optimizations) : null);
         writeOption(ConfigurationConstants.OPTIMIZATION_PASSES,                  configuration.optimizationPasses);
         writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION,     configuration.allowAccessModification);
         writeOption(ConfigurationConstants.MERGE_INTERFACES_AGGRESSIVELY_OPTION, configuration.mergeInterfacesAggressively);
@@ -127,19 +129,21 @@ public class ConfigurationWriter
         writeOption(ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION,           configuration.overloadAggressively);
         writeOption(ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION,   configuration.useUniqueClassMemberNames);
         writeOption(ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION, !configuration.useMixedCaseClassNames);
-        writeOption(ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION,       configuration.flattenPackageHierarchy == null ? null : ClassUtil.externalClassName(configuration.flattenPackageHierarchy));
-        writeOption(ConfigurationConstants.REPACKAGE_CLASSES_OPTION,               configuration.repackageClasses        == null ? null : ClassUtil.externalClassName(configuration.repackageClasses));
-        writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION,                 ListUtil.commaSeparatedString(configuration.keepAttributes));
+        writeOption(ConfigurationConstants.KEEP_PACKAGE_NAMES_OPTION,              configuration.keepPackageNames, true);
+        writeOption(ConfigurationConstants.FLATTEN_PACKAGE_HIERARCHY_OPTION,       configuration.flattenPackageHierarchy, true);
+        writeOption(ConfigurationConstants.REPACKAGE_CLASSES_OPTION,               configuration.repackageClasses, true);
+        writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION,                 configuration.keepAttributes);
         writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION,    configuration.newSourceFileAttribute);
-        writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION,       ListUtil.commaSeparatedString(configuration.adaptResourceFileNames));
-        writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION,    ListUtil.commaSeparatedString(configuration.adaptResourceFileContents));
+        writeOption(ConfigurationConstants.ADAPT_CLASS_STRINGS_OPTION,             configuration.adaptClassStrings, true);
+        writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_NAMES_OPTION,       configuration.adaptResourceFileNames);
+        writeOption(ConfigurationConstants.ADAPT_RESOURCE_FILE_CONTENTS_OPTION,    configuration.adaptResourceFileContents);
 
         writeOption(ConfigurationConstants.DONT_PREVERIFY_OPTION, !configuration.preverify);
         writeOption(ConfigurationConstants.MICRO_EDITION_OPTION,  configuration.microEdition);
 
         writeOption(ConfigurationConstants.VERBOSE_OPTION,             configuration.verbose);
-        writeOption(ConfigurationConstants.DONT_NOTE_OPTION,           !configuration.note);
-        writeOption(ConfigurationConstants.DONT_WARN_OPTION,           !configuration.warn);
+        writeOption(ConfigurationConstants.DONT_NOTE_OPTION,           configuration.note, true);
+        writeOption(ConfigurationConstants.DONT_WARN_OPTION,           configuration.warn, true);
         writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION,     configuration.ignoreWarnings);
         writeOption(ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration);
         writeOption(ConfigurationConstants.DUMP_OPTION,                configuration.dump);
@@ -200,7 +204,7 @@ public class ConfigurationWriter
     }
 
 
-    private boolean writeFilter(boolean filtered, String filter)
+    private boolean writeFilter(boolean filtered, List filter)
     {
         if (filtered)
         {
@@ -214,7 +218,15 @@ public class ConfigurationWriter
                 writer.print(ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD);
             }
 
-            writer.print(quotedString(filter));
+            for (int index = 0; index < filter.size(); index++)
+            {
+                if (index > 0)
+                {
+                    writer.print(ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD);
+                }
+
+                writer.print(quotedString((String)filter.get(index)));
+            }
 
             filtered = true;
         }
@@ -243,10 +255,56 @@ public class ConfigurationWriter
     }
 
 
+    private void writeOption(String optionName, List arguments)
+    {
+        writeOption(optionName, arguments, false);
+    }
+
+
+    private void writeOption(String  optionName,
+                             List    arguments,
+                             boolean replaceInternalClassNames)
+    {
+        if (arguments != null)
+        {
+            if (arguments.isEmpty())
+            {
+                writer.println(optionName);
+            }
+            else
+            {
+                String argumentString = ListUtil.commaSeparatedString(arguments);
+
+                if (replaceInternalClassNames)
+                {
+                    argumentString = ClassUtil.externalClassName(argumentString);
+                }
+
+                writer.print(optionName);
+                writer.print(' ');
+                writer.println(quotedString(argumentString));
+            }
+        }
+    }
+
+
     private void writeOption(String optionName, String arguments)
     {
+        writeOption(optionName, arguments, false);
+    }
+
+
+    private void writeOption(String  optionName,
+                             String  arguments,
+                             boolean replaceInternalClassNames)
+    {
         if (arguments != null)
         {
+            if (replaceInternalClassNames)
+            {
+                arguments = ClassUtil.externalClassName(arguments);
+            }
+
             writer.print(optionName);
             writer.print(' ');
             writer.println(quotedString(arguments));
@@ -273,46 +331,46 @@ public class ConfigurationWriter
 
 
     private void writeOptions(String[] optionNames,
-                              List     keepSpecifications)
+                              List     keepClassSpecifications)
     {
-        if (keepSpecifications != null)
+        if (keepClassSpecifications != null)
         {
-            for (int index = 0; index < keepSpecifications.size(); index++)
+            for (int index = 0; index < keepClassSpecifications.size(); index++)
             {
-                writeOption(optionNames, (KeepSpecification)keepSpecifications.get(index));
+                writeOption(optionNames, (KeepClassSpecification)keepClassSpecifications.get(index));
             }
         }
     }
 
 
-    private void writeOption(String[]          optionNames,
-                             KeepSpecification keepSpecification)
+    private void writeOption(String[]               optionNames,
+                             KeepClassSpecification keepClassSpecification)
     {
         // Compose the option name.
-        String optionName = optionNames[keepSpecification.markConditionally ? 2 :
-                                        keepSpecification.markClasses       ? 0 :
+        String optionName = optionNames[keepClassSpecification.markConditionally ? 2 :
+                                        keepClassSpecification.markClasses       ? 0 :
                                                                               1];
 
-        if (keepSpecification.allowShrinking)
+        if (keepClassSpecification.allowShrinking)
         {
             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
                           ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION;
         }
 
-        if (keepSpecification.allowOptimization)
+        if (keepClassSpecification.allowOptimization)
         {
             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
                           ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION;
         }
 
-        if (keepSpecification.allowObfuscation)
+        if (keepClassSpecification.allowObfuscation)
         {
             optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
                           ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION;
         }
 
         // Write out the option with the proper class specification.
-        writeOption(optionName, keepSpecification);
+        writeOption(optionName, keepClassSpecification);
     }
 
 
@@ -561,7 +619,8 @@ public class ConfigurationWriter
                string.indexOf('(') >= 0 ||
                string.indexOf(')') >= 0 ||
                string.indexOf(':') >= 0 ||
-               string.indexOf(';') >= 0  ? ("'" + string + "'") :
+               string.indexOf(';') >= 0 ||
+               string.indexOf(',') >= 0  ? ("'" + string + "'") :
                                            (      string      );
     }
 
diff --git a/src/proguard/DataEntryReaderFactory.java b/src/proguard/DataEntryReaderFactory.java
index 68e8eb7..a9724b5 100644
--- a/src/proguard/DataEntryReaderFactory.java
+++ b/src/proguard/DataEntryReaderFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,6 +23,8 @@ package proguard;
 import proguard.io.*;
 import proguard.util.*;
 
+import java.util.List;
+
 
 /**
  * This class can create DataEntryReader instances based on class path entries.
@@ -52,11 +54,11 @@ public class DataEntryReaderFactory
         boolean isEar = endsWithIgnoreCase(entryName, ".ear");
         boolean isZip = endsWithIgnoreCase(entryName, ".zip");
 
-        String filter    = classPathEntry.getFilter();
-        String jarFilter = classPathEntry.getJarFilter();
-        String warFilter = classPathEntry.getWarFilter();
-        String earFilter = classPathEntry.getEarFilter();
-        String zipFilter = classPathEntry.getZipFilter();
+        List filter    = classPathEntry.getFilter();
+        List jarFilter = classPathEntry.getJarFilter();
+        List warFilter = classPathEntry.getWarFilter();
+        List earFilter = classPathEntry.getEarFilter();
+        List zipFilter = classPathEntry.getZipFilter();
 
         System.out.println(messagePrefix +
                            (isJar ? "jar" :
@@ -107,7 +109,7 @@ public class DataEntryReaderFactory
      */
     private static DataEntryReader wrapInJarReader(DataEntryReader reader,
                                                    boolean         isJar,
-                                                   String          jarFilter,
+                                                   List            jarFilter,
                                                    String          jarExtension)
     {
         // Unzip any jars, if necessary.
diff --git a/src/proguard/DataEntryWriterFactory.java b/src/proguard/DataEntryWriterFactory.java
index 1adc75f..9fbc6d0 100644
--- a/src/proguard/DataEntryWriterFactory.java
+++ b/src/proguard/DataEntryWriterFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,6 +23,8 @@ package proguard;
 import proguard.io.*;
 import proguard.util.*;
 
+import java.util.List;
+
 /**
  * This class can create DataEntryWriter instances based on class paths. The
  * writers will wrap the output in the proper jars, wars, ears, and zips.
@@ -69,11 +71,11 @@ public class DataEntryWriterFactory
         boolean isEar = endsWithIgnoreCase(entryName, ".ear");
         boolean isZip = endsWithIgnoreCase(entryName, ".zip");
 
-        String filter    = classPathEntry.getFilter();
-        String jarFilter = classPathEntry.getJarFilter();
-        String warFilter = classPathEntry.getWarFilter();
-        String earFilter = classPathEntry.getEarFilter();
-        String zipFilter = classPathEntry.getZipFilter();
+        List filter    = classPathEntry.getFilter();
+        List jarFilter = classPathEntry.getJarFilter();
+        List warFilter = classPathEntry.getWarFilter();
+        List earFilter = classPathEntry.getEarFilter();
+        List zipFilter = classPathEntry.getZipFilter();
 
         System.out.println("Preparing output " +
                            (isJar ? "jar" :
@@ -120,7 +122,7 @@ public class DataEntryWriterFactory
      */
     private static DataEntryWriter wrapInJarWriter(DataEntryWriter writer,
                                                    boolean         isJar,
-                                                   String          jarFilter,
+                                                   List            jarFilter,
                                                    String          jarExtension,
                                                    boolean         dontWrap)
     {
diff --git a/src/proguard/DescriptorKeepChecker.java b/src/proguard/DescriptorKeepChecker.java
index e8547f7..1dfaf1a 100644
--- a/src/proguard/DescriptorKeepChecker.java
+++ b/src/proguard/DescriptorKeepChecker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -139,7 +139,9 @@ implements   MemberVisitor,
     {
         if (!KeepMarker.isKept(programClass))
         {
-            notePrinter.print("Note: the configuration keeps the entry point '" +
+            notePrinter.print(referencingClass.getName(),
+                              programClass.getName(),
+                              "Note: the configuration keeps the entry point '" +
                               ClassUtil.externalClassName(referencingClass.getName()) +
                               " { " +
                               (isField ?
diff --git a/src/proguard/DuplicateClassPrinter.java b/src/proguard/DuplicateClassPrinter.java
index 09a8b0c..21b6aa0 100644
--- a/src/proguard/DuplicateClassPrinter.java
+++ b/src/proguard/DuplicateClassPrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -48,14 +48,16 @@ public class DuplicateClassPrinter implements ClassVisitor
 
     public void visitProgramClass(ProgramClass programClass)
     {
-        notePrinter.print("Note: duplicate definition of program class [" +
+        notePrinter.print(programClass.getName(),
+                          "Note: duplicate definition of program class [" +
                           ClassUtil.externalClassName(programClass.getName()) + "]");
     }
 
 
     public void visitLibraryClass(LibraryClass libraryClass)
     {
-        notePrinter.print("Note: duplicate definition of library class [" +
+        notePrinter.print(libraryClass.getName(),
+                          "Note: duplicate definition of library class [" +
                           ClassUtil.externalClassName(libraryClass.getName()) + "]");
     }
 }
diff --git a/src/proguard/FileWordReader.java b/src/proguard/FileWordReader.java
index 8ea9767..fb9fa50 100644
--- a/src/proguard/FileWordReader.java
+++ b/src/proguard/FileWordReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/FullyQualifiedClassNameChecker.java b/src/proguard/FullyQualifiedClassNameChecker.java
index aeb7398..06949b5 100644
--- a/src/proguard/FullyQualifiedClassNameChecker.java
+++ b/src/proguard/FullyQualifiedClassNameChecker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -150,9 +150,11 @@ implements   ClassVisitor
         if (className != null                            &&
             !containsWildCards(className)                &&
             programClassPool.getClass(className) == null &&
-            libraryClassPool.getClass(className) == null)
+            libraryClassPool.getClass(className) == null &&
+            notePrinter.accepts(className))
         {
-            notePrinter.print("Note: the configuration refers to the unknown class '" +
+            notePrinter.print(className,
+                              "Note: the configuration refers to the unknown class '" +
                               ClassUtil.externalClassName(className) + "'");
 
             String fullyQualifiedClassName =
diff --git a/src/proguard/GPL.java b/src/proguard/GPL.java
index 64e37cb..272a837 100644
--- a/src/proguard/GPL.java
+++ b/src/proguard/GPL.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -166,6 +166,7 @@ public class GPL
                packageName.startsWith("org.eclipse")            ||
                packageName.startsWith("org.netbeans")           ||
                packageName.startsWith("com.sun.kvem")           ||
+               packageName.startsWith("net.certiv.proguarddt")  ||
                packageName.startsWith("eclipseme")              ||
                packageName.startsWith("jg.j2me")                ||
                packageName.startsWith("jg.common")              ||
diff --git a/src/proguard/Initializer.java b/src/proguard/Initializer.java
index 03048f2..41cf971 100644
--- a/src/proguard/Initializer.java
+++ b/src/proguard/Initializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -22,6 +22,7 @@ package proguard;
 
 import proguard.classfile.ClassPool;
 import proguard.classfile.attribute.visitor.AllAttributeVisitor;
+import proguard.classfile.constant.visitor.*;
 import proguard.classfile.instruction.visitor.AllInstructionVisitor;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
@@ -66,13 +67,8 @@ public class Initializer
         ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ?
             null : new ClassPool();
 
-        WarningPrinter classReferenceWarningPrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
-
-        WarningPrinter dependencyWarningPrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
+        WarningPrinter classReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn);
+        WarningPrinter dependencyWarningPrinter     = new WarningPrinter(System.err, configuration.warn);
 
         // Initialize the superclass hierarchies for program classes.
         programClassPool.classesAccept(
@@ -81,39 +77,47 @@ public class Initializer
                                                classReferenceWarningPrinter,
                                                null));
 
+        // Initialize the superclass hierarchy of all library classes, without
+        // warnings.
+        libraryClassPool.classesAccept(
+            new ClassSuperHierarchyInitializer(programClassPool,
+                                               libraryClassPool,
+                                               null,
+                                               dependencyWarningPrinter));
+
+        // Initialize the class references of program class members and
+        // attributes. Note that all superclass hierarchies have to be
+        // initialized for this purpose.
+        WarningPrinter memberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn);
+
+        programClassPool.classesAccept(
+            new ClassReferenceInitializer(programClassPool,
+                                          libraryClassPool,
+                                          classReferenceWarningPrinter,
+                                          memberReferenceWarningPrinter,
+                                          null));
+
         if (reducedLibraryClassPool != null)
         {
-            // Collect the library classes that are referenced by program
-            // classes.
+            // Collect the library classes that are directly referenced by
+            // program classes, without introspection.
             programClassPool.classesAccept(
                 new ReferencedClassVisitor(
                 new LibraryClassFilter(
                 new ClassPoolFiller(reducedLibraryClassPool))));
 
-            // Initialize the superclass hierarchy for referenced library
-            // classes, with warnings.
+            // Reinitialize the superclass hierarchies of referenced library
+            // classes, this time with warnings.
             reducedLibraryClassPool.classesAccept(
                 new ClassSuperHierarchyInitializer(programClassPool,
                                                    libraryClassPool,
                                                    classReferenceWarningPrinter,
-                                                   dependencyWarningPrinter));
+                                                   null));
         }
 
-        // Initialize the superclass hierarchy for library classes, without
-        // warnings.
-        libraryClassPool.classesAccept(
-            new ClassSuperHierarchyInitializer(programClassPool,
-                                               libraryClassPool,
-                                               null,
-                                               dependencyWarningPrinter));
-
-        WarningPrinter dynamicClassReferenceNotePrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
-
-        WarningPrinter classForNameNotePrinter = configuration.note ?
-            new WarningPrinter(System.out) :
-            null;
+        // Initialize the Class.forName references.
+        WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note);
+        WarningPrinter classForNameNotePrinter          = new WarningPrinter(System.out, configuration.note);
 
         programClassPool.classesAccept(
             new AllMethodVisitor(
@@ -126,21 +130,8 @@ public class Initializer
                                                  classForNameNotePrinter,
                                                  createClassNoteExceptionMatcher(configuration.keep))))));
 
-        // Initialize the class references of program class members and attributes.
-        WarningPrinter memberReferenceWarningPrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
-
-        programClassPool.classesAccept(
-            new ClassReferenceInitializer(programClassPool,
-                                          libraryClassPool,
-                                          memberReferenceWarningPrinter,
-                                          null));
-
         // Initialize the Class.get[Declared]{Field,Method} references.
-        WarningPrinter getMemberNotePrinter = configuration.note ?
-            new WarningPrinter(System.out) :
-            null;
+        WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note);
 
         programClassPool.classesAccept(
             new AllMethodVisitor(
@@ -152,34 +143,33 @@ public class Initializer
                                                   createClassMemberNoteExceptionMatcher(configuration.keep, true),
                                                   createClassMemberNoteExceptionMatcher(configuration.keep, false))))));
 
-        // Print various notes, if specified.
-        WarningPrinter fullyQualifiedClassNameNotePrinter = configuration.note ?
-            new WarningPrinter(System.out) :
-            null;
-
-        WarningPrinter descriptorKeepNotePrinter = configuration.note ?
-            new WarningPrinter(System.out) :
-            null;
-
-        if (fullyQualifiedClassNameNotePrinter != null)
+        // Initialize other string constant references, if requested.
+        if (configuration.adaptClassStrings != null)
         {
-            new FullyQualifiedClassNameChecker(programClassPool,
-                                               libraryClassPool,
-                                               fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep);
+            programClassPool.classesAccept(
+                new ClassNameFilter(configuration.adaptClassStrings,
+                new AllConstantVisitor(
+                new StringReferenceInitializer(programClassPool,
+                                               libraryClassPool))));
         }
 
-        if (descriptorKeepNotePrinter != null)
-        {
-            new DescriptorKeepChecker(programClassPool,
-                                      libraryClassPool,
-                                      descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep);
-        }
+        // Print various notes, if specified.
+        WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note);
+        WarningPrinter descriptorKeepNotePrinter          = new WarningPrinter(System.out, configuration.note);
+
+        new FullyQualifiedClassNameChecker(programClassPool,
+                                           libraryClassPool,
+                                           fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep);
+
+        new DescriptorKeepChecker(programClassPool,
+                                  libraryClassPool,
+                                  descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep);
 
         // Initialize the class references of library class members.
         if (reducedLibraryClassPool != null)
         {
             // Collect the library classes that are referenced by program
-            // classes.
+            // classes, directly or indirectly, with or without introspection.
             programClassPool.classesAccept(
                 new ReferencedClassVisitor(
                 new LibraryClassFilter(
@@ -187,11 +177,13 @@ public class Initializer
                 new LibraryClassFilter(
                 new ClassPoolFiller(reducedLibraryClassPool))))));
 
-            // Initialize the class references of library class members.
+            // Initialize the class references of referenced library
+            // classes, without warnings.
             reducedLibraryClassPool.classesAccept(
                 new ClassReferenceInitializer(programClassPool,
                                               libraryClassPool,
                                               null,
+                                              null,
                                               dependencyWarningPrinter));
 
             // Reset the library class pool.
@@ -221,6 +213,7 @@ public class Initializer
                 new ClassReferenceInitializer(programClassPool,
                                               libraryClassPool,
                                               null,
+                                              null,
                                               dependencyWarningPrinter));
         }
 
@@ -233,106 +226,95 @@ public class Initializer
         libraryClassPool.classesAccept(new StringSharer());
 
         // Print out a summary of the notes, if necessary.
-        if (fullyQualifiedClassNameNotePrinter != null)
+        int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount();
+        if (fullyQualifiedNoteCount > 0)
         {
-            int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount();
-            if (fullyQualifiedNoteCount > 0)
-            {
-                System.out.println("Note: there were " + fullyQualifiedNoteCount +
-                                   " references to unknown classes.");
-                System.out.println("      You should check your configuration for typos.");
-            }
+            System.out.println("Note: there were " + fullyQualifiedNoteCount +
+                               " references to unknown classes.");
+            System.out.println("      You should check your configuration for typos.");
         }
 
-        if (descriptorKeepNotePrinter != null)
+        int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount();
+        if (descriptorNoteCount > 0)
         {
-            int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount();
-            if (descriptorNoteCount > 0)
-            {
-                System.out.println("Note: there were " + descriptorNoteCount +
-                                   " unkept descriptor classes in kept class members.");
-                System.out.println("      You should consider explicitly keeping the mentioned classes");
-                System.out.println("      (using '-keep').");
-            }
+            System.out.println("Note: there were " + descriptorNoteCount +
+                               " unkept descriptor classes in kept class members.");
+            System.out.println("      You should consider explicitly keeping the mentioned classes");
+            System.out.println("      (using '-keep').");
         }
 
-        if (dynamicClassReferenceNotePrinter != null)
+        int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount();
+        if (dynamicClassReferenceNoteCount > 0)
         {
-            int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount();
-            if (dynamicClassReferenceNoteCount > 0)
-            {
-                System.out.println("Note: there were " + dynamicClassReferenceNoteCount +
-                                   " unresolved dynamic references to classes or interfaces.");
-                System.err.println("      You should check if you need to specify additional program jars.");
-            }
+            System.out.println("Note: there were " + dynamicClassReferenceNoteCount +
+                               " unresolved dynamic references to classes or interfaces.");
+            System.err.println("      You should check if you need to specify additional program jars.");
         }
 
-        if (classForNameNotePrinter != null)
+        int classForNameNoteCount = classForNameNotePrinter.getWarningCount();
+        if (classForNameNoteCount > 0)
         {
-            int classForNameNoteCount = classForNameNotePrinter.getWarningCount();
-            if (classForNameNoteCount > 0)
-            {
-                System.out.println("Note: there were " + classForNameNoteCount +
-                                   " class casts of dynamically created class instances.");
-                System.out.println("      You might consider explicitly keeping the mentioned classes and/or");
-                System.out.println("      their implementations (using '-keep').");
-            }
+            System.out.println("Note: there were " + classForNameNoteCount +
+                               " class casts of dynamically created class instances.");
+            System.out.println("      You might consider explicitly keeping the mentioned classes and/or");
+            System.out.println("      their implementations (using '-keep').");
         }
 
-        if (getMemberNotePrinter != null)
+        int getmemberNoteCount = getMemberNotePrinter.getWarningCount();
+        if (getmemberNoteCount > 0)
         {
-            int getmemberNoteCount = getMemberNotePrinter.getWarningCount();
-            if (getmemberNoteCount > 0)
-            {
-                System.out.println("Note: there were " + getmemberNoteCount +
-                                   " accesses to class members by means of introspection.");
-                System.out.println("      You should consider explicitly keeping the mentioned class members");
-                System.out.println("      (using '-keep' or '-keepclassmembers').");
-            }
+            System.out.println("Note: there were " + getmemberNoteCount +
+                               " accesses to class members by means of introspection.");
+            System.out.println("      You should consider explicitly keeping the mentioned class members");
+            System.out.println("      (using '-keep' or '-keepclassmembers').");
         }
 
         // Print out a summary of the warnings, if necessary.
-        if (classReferenceWarningPrinter  != null &&
-            dependencyWarningPrinter      != null &&
-            memberReferenceWarningPrinter != null   )
+        int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount();
+        if (classReferenceWarningCount > 0)
         {
-            int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount();
-            if (classReferenceWarningCount > 0)
-            {
-                System.err.println("Warning: there were " + classReferenceWarningCount +
-                                   " unresolved references to classes or interfaces.");
-                System.err.println("         You may need to specify additional library jars (using '-libraryjars'),");
-                System.err.println("         or perhaps the '-dontskipnonpubliclibraryclasses' option.");
-            }
+            System.err.println("Warning: there were " + classReferenceWarningCount +
+                               " unresolved references to classes or interfaces.");
+            System.err.println("         You may need to specify additional library jars (using '-libraryjars'),");
+            System.err.println("         or perhaps the '-dontskipnonpubliclibraryclasses' option.");
+        }
 
-            int dependencyWarningCount = dependencyWarningPrinter.getWarningCount();
-            if (dependencyWarningCount > 0)
-            {
-                System.err.println("Warning: there were " + dependencyWarningCount +
-                                   " instances of library classes depending on program classes.");
-                System.err.println("         You must avoid such dependencies, since the program classes will");
-                System.err.println("         be processed, while the library classes will remain unchanged.");
-            }
+        int dependencyWarningCount = dependencyWarningPrinter.getWarningCount();
+        if (dependencyWarningCount > 0)
+        {
+            System.err.println("Warning: there were " + dependencyWarningCount +
+                               " instances of library classes depending on program classes.");
+            System.err.println("         You must avoid such dependencies, since the program classes will");
+            System.err.println("         be processed, while the library classes will remain unchanged.");
+        }
 
-            int memberReferenceWarningCount = memberReferenceWarningPrinter.getWarningCount();
-            if (memberReferenceWarningCount > 0)
-            {
-                System.err.println("Warning: there were " + memberReferenceWarningCount +
-                                   " unresolved references to program class members.");
-                System.err.println("         Your input classes appear to be inconsistent.");
-                System.err.println("         You may need to recompile them and try again.");
-                System.err.println("         Alternatively, you may have to specify the options ");
-                System.err.println("         '-dontskipnonpubliclibraryclasses' and/or");
-                System.err.println("         '-dontskipnonpubliclibraryclassmembers'.");
-            }
+        int memberReferenceWarningCount = memberReferenceWarningPrinter.getWarningCount();
+        if (memberReferenceWarningCount > 0)
+        {
+            System.err.println("Warning: there were " + memberReferenceWarningCount +
+                               " unresolved references to program class members.");
+            System.err.println("         Your input classes appear to be inconsistent.");
+            System.err.println("         You may need to recompile them and try again.");
+            System.err.println("         Alternatively, you may have to specify the options ");
+            System.err.println("         '-dontskipnonpubliclibraryclasses' and/or");
+            System.err.println("         '-dontskipnonpubliclibraryclassmembers'.");
+        }
 
-            if ((classReferenceWarningCount   > 0 ||
-                 dependencyWarningCount       > 0 ||
-                 memberReferenceWarningCount  > 0) &&
-                !configuration.ignoreWarnings)
-            {
-                throw new IOException("Please correct the above warnings first.");
-            }
+        if ((classReferenceWarningCount   > 0 ||
+             dependencyWarningCount       > 0 ||
+             memberReferenceWarningCount  > 0) &&
+            !configuration.ignoreWarnings)
+        {
+            throw new IOException("Please correct the above warnings first.");
+        }
+
+        if ((configuration.note == null ||
+             !configuration.note.isEmpty()) &&
+            (configuration.warn != null &&
+             configuration.warn.isEmpty() ||
+             configuration.ignoreWarnings))
+        {
+            System.out.println("Note: You're ignoring all warnings!");
         }
 
         // Discard unused library classes.
@@ -356,18 +338,18 @@ public class Initializer
             List noteExceptionNames = new ArrayList(noteExceptions.size());
             for (int index = 0; index < noteExceptions.size(); index++)
             {
-                KeepSpecification keepSpecification = (KeepSpecification)noteExceptions.get(index);
-                if (keepSpecification.markClasses)
+                KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index);
+                if (keepClassSpecification.markClasses)
                 {
                     // If the class itself is being kept, it's ok.
-                    String className = keepSpecification.className;
+                    String className = keepClassSpecification.className;
                     if (className != null)
                     {
                         noteExceptionNames.add(className);
                     }
 
                     // If all of its extensions are being kept, it's ok too.
-                    String extendsClassName = keepSpecification.extendsClassName;
+                    String extendsClassName = keepClassSpecification.extendsClassName;
                     if (extendsClassName != null)
                     {
                         noteExceptionNames.add(extendsClassName);
@@ -397,10 +379,10 @@ public class Initializer
             List noteExceptionNames = new ArrayList();
             for (int index = 0; index < noteExceptions.size(); index++)
             {
-                KeepSpecification keepSpecification = (KeepSpecification)noteExceptions.get(index);
+                KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index);
                 List memberSpecifications = isField ?
-                    keepSpecification.fieldSpecifications :
-                    keepSpecification.methodSpecifications;
+                    keepClassSpecification.fieldSpecifications :
+                    keepClassSpecification.methodSpecifications;
 
                 if (memberSpecifications != null)
                 {
diff --git a/src/proguard/InputReader.java b/src/proguard/InputReader.java
index ebdc831..c088324 100644
--- a/src/proguard/InputReader.java
+++ b/src/proguard/InputReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -54,24 +54,23 @@ public class InputReader
     public void execute(ClassPool programClassPool,
                         ClassPool libraryClassPool) throws IOException
     {
-        WarningPrinter warningPrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
-
-        WarningPrinter notePrinter = configuration.note ?
-            new WarningPrinter(System.out) :
-            null;
-
-        DuplicateClassPrinter duplicateClassPrinter = configuration.note ?
-            new DuplicateClassPrinter(notePrinter) :
-            null;
-
         // Check if we have at least some input classes.
         if (configuration.programJars == null)
         {
             throw new IOException("The input is empty. You have to specify one or more '-injars' options");
         }
 
+        // Perform some sanity checks on the class paths.
+        checkInputOutput(configuration.libraryJars,
+                         configuration.programJars);
+        checkInputOutput(configuration.programJars,
+                         configuration.programJars);
+
+        WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
+        WarningPrinter notePrinter    = new WarningPrinter(System.out, configuration.note);
+
+        DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter);
+
         // Read the program class files.
         // Prepare a data entry reader to filter all classes,
         // which are then decoded to classes by a class reader,
@@ -111,32 +110,58 @@ public class InputReader
         }
 
         // Print out a summary of the notes, if necessary.
-        if (notePrinter != null)
+        int noteCount = notePrinter.getWarningCount();
+        if (noteCount > 0)
         {
-            int noteCount = notePrinter.getWarningCount();
-            if (noteCount > 0)
-            {
-                System.err.println("Note: there were " + noteCount +
-                                   " duplicate class definitions.");
-            }
+            System.err.println("Note: there were " + noteCount +
+                               " duplicate class definitions.");
         }
 
         // Print out a summary of the warnings, if necessary.
-        if (warningPrinter != null)
+        int warningCount = warningPrinter.getWarningCount();
+        if (warningCount > 0)
         {
-            int warningCount = warningPrinter.getWarningCount();
-            if (warningCount > 0)
+            System.err.println("Warning: there were " + warningCount +
+                               " classes in incorrectly named files.");
+            System.err.println("         You should make sure all file names correspond to their class names.");
+            System.err.println("         The directory hierarchies must correspond to the package hierarchies.");
+
+            if (!configuration.ignoreWarnings)
             {
-                System.err.println("Warning: there were " + warningCount +
-                                   " classes in incorrectly named files.");
-                System.err.println("         You should make sure all file names correspond to their class names.");
-                System.err.println("         The directory hierarchies must correspond to the package hierarchies.");
+                System.err.println("         If you don't mind the mentioned classes not being written out,");
+                System.err.println("         you could try your luck using the '-ignorewarnings' option.");
+                throw new IOException("Please correct the above warnings first.");
+            }
+        }
+    }
+
+
+    /**
+     * Performs some sanity checks on the class paths.
+     */
+    private void checkInputOutput(ClassPath inputClassPath,
+                                  ClassPath outputClassPath)
+    throws IOException
+    {
+        if (inputClassPath == null ||
+            outputClassPath == null)
+        {
+            return;
+        }
 
-                if (!configuration.ignoreWarnings)
+        for (int index1 = 0; index1 < inputClassPath.size(); index1++)
+        {
+            ClassPathEntry entry1 = inputClassPath.get(index1);
+            if (!entry1.isOutput())
+            {
+                for (int index2 = 0; index2 < outputClassPath.size(); index2++)
                 {
-                    System.err.println("         If you don't mind the mentioned classes not being written out,");
-                    System.err.println("         you could try your luck using the '-ignorewarnings' option.");
-                    throw new IOException("Please correct the above warnings first.");
+                    ClassPathEntry entry2 = outputClassPath.get(index2);
+                    if (entry2.isOutput() &&
+                        entry2.getName().equals(entry1.getName()))
+                    {
+                        throw new IOException("Input jars and output jars must be different ["+entry1.getName()+"]");
+                    }
                 }
             }
         }
diff --git a/src/proguard/KeepSpecification.java b/src/proguard/KeepClassSpecification.java
similarity index 79%
rename from src/proguard/KeepSpecification.java
rename to src/proguard/KeepClassSpecification.java
index 5fd4fae..29c0d3c 100644
--- a/src/proguard/KeepSpecification.java
+++ b/src/proguard/KeepClassSpecification.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,12 +21,11 @@
 package proguard;
 
 /**
- * This class stores a specification of keep option, with a purpose and a class
- * specification.
+ * This class represents a keep option with class specification.
  *
  * @author Eric Lafortune
  */
-public class KeepSpecification extends ClassSpecification
+public class KeepClassSpecification extends ClassSpecification
 {
     public final boolean markClasses;
     public final boolean markConditionally;
@@ -36,7 +35,7 @@ public class KeepSpecification extends ClassSpecification
 
 
     /**
-     * Creates a new KeepSpecification for all possible classes.
+     * Creates a new KeepClassSpecification for all possible classes.
      * @param markClasses        specifies whether to mark the classes.
      *                           If false, only class members are marked.
      *                           If true, the classes are marked as well.
@@ -48,11 +47,11 @@ public class KeepSpecification extends ClassSpecification
      * @param allowOptimization  specifies whether optimization is allowed.
      * @param allowObfuscation   specifies whether obfuscation is allowed.
      */
-    public KeepSpecification(boolean            markClasses,
-                             boolean            markConditionally,
-                             boolean            allowShrinking,
-                             boolean            allowOptimization,
-                             boolean            allowObfuscation)
+    public KeepClassSpecification(boolean markClasses,
+                                  boolean markConditionally,
+                                  boolean allowShrinking,
+                                  boolean allowOptimization,
+                                  boolean allowObfuscation)
     {
         this.markClasses       = markClasses;
         this.markConditionally = markConditionally;
@@ -63,7 +62,7 @@ public class KeepSpecification extends ClassSpecification
 
 
     /**
-     * Creates a new KeepSpecification.
+     * Creates a new KeepClassSpecification.
      * @param markClasses        specifies whether to mark the classes.
      *                           If false, only class members are marked.
      *                           If true, the classes are marked as well.
@@ -76,12 +75,12 @@ public class KeepSpecification extends ClassSpecification
      * @param allowObfuscation   specifies whether obfuscation is allowed.
      * @param classSpecification the specification of classes and class members.
      */
-    public KeepSpecification(boolean            markClasses,
-                             boolean            markConditionally,
-                             boolean            allowShrinking,
-                             boolean            allowOptimization,
-                             boolean            allowObfuscation,
-                             ClassSpecification classSpecification)
+    public KeepClassSpecification(boolean            markClasses,
+                                  boolean            markConditionally,
+                                  boolean            allowShrinking,
+                                  boolean            allowOptimization,
+                                  boolean            allowObfuscation,
+                                  ClassSpecification classSpecification)
     {
         super(classSpecification);
 
@@ -103,7 +102,7 @@ public class KeepSpecification extends ClassSpecification
             return false;
         }
 
-        KeepSpecification other = (KeepSpecification)object;
+        KeepClassSpecification other = (KeepClassSpecification)object;
         return
             this.markClasses       == other.markClasses       &&
             this.markConditionally == other.markConditionally &&
diff --git a/src/proguard/MemberSpecification.java b/src/proguard/MemberSpecification.java
index 1f9c2ca..1cfa962 100644
--- a/src/proguard/MemberSpecification.java
+++ b/src/proguard/MemberSpecification.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/OutputWriter.java b/src/proguard/OutputWriter.java
index 915e806..10c18fb 100644
--- a/src/proguard/OutputWriter.java
+++ b/src/proguard/OutputWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,10 +21,11 @@
 package proguard;
 
 import proguard.classfile.ClassPool;
+import proguard.classfile.util.ClassUtil;
 import proguard.io.*;
-import proguard.util.*;
 
 import java.io.IOException;
+import java.util.*;
 
 /**
  * This class writes the output class files.
@@ -154,43 +155,58 @@ public class OutputWriter
                 new ClassRewriter(programClassPool, writer);
 
             // The writer will also be used to write resource files.
-            DataEntryReader resourceRewriter =
+            DataEntryReader resourceCopier =
                 new DataEntryCopier(writer);
 
+            DataEntryReader resourceRewriter = resourceCopier;
+
             // Wrap the resource writer with a filter and a data entry rewriter,
             // if required.
             if (configuration.adaptResourceFileContents != null)
             {
-                DataEntryReader adaptedResourceRewriter =
-                    new DataEntryRewriter(programClassPool, writer);
-
-                resourceRewriter = configuration.adaptResourceFileContents.size() > 0 ?
-                    new FilteredDataEntryReader(
-                    new DataEntryNameFilter(
-                    new ListParser(new FileNameParser()).parse(configuration.adaptResourceFileContents)),
-                        adaptedResourceRewriter, resourceRewriter) :
-                    adaptedResourceRewriter;
+                resourceRewriter =
+                    new NameFilter(configuration.adaptResourceFileContents,
+                    new NameFilter("META-INF/**",
+                        new ManifestRewriter(programClassPool, writer),
+                        new DataEntryRewriter(programClassPool, writer)),
+                    resourceRewriter);
             }
 
             // Wrap the resource writer with a filter and a data entry renamer,
             // if required.
             if (configuration.adaptResourceFileNames != null)
             {
-                DataEntryReader adaptedResourceRewriter =
-                    new DataEntryRenamer(programClassPool, resourceRewriter);
-
-                resourceRewriter = configuration.adaptResourceFileNames.size() > 0 ?
-                    new FilteredDataEntryReader(
-                    new DataEntryNameFilter(
-                    new ListParser(new FileNameParser()).parse(configuration.adaptResourceFileNames)),
-                        adaptedResourceRewriter, resourceRewriter) :
-                    adaptedResourceRewriter;
+                Map packagePrefixMap = createPackagePrefixMap(programClassPool);
+
+                resourceRewriter =
+                    new NameFilter(configuration.adaptResourceFileNames,
+                    new DataEntryObfuscator(programClassPool,
+                                            packagePrefixMap,
+                                            resourceRewriter),
+                    resourceRewriter);
             }
 
-            // Create the reader that can write class files and copy resource
-            // files to the above writer.
+            DataEntryReader directoryRewriter = null;
+
+            // Wrap the directory writer with a filter and a data entry renamer,
+            // if required.
+            if (configuration.keepDirectories != null)
+            {
+                Map packagePrefixMap = createPackagePrefixMap(programClassPool);
+
+                directoryRewriter =
+                    new NameFilter(configuration.keepDirectories,
+                    new DataEntryRenamer(packagePrefixMap,
+                                         resourceCopier,
+                                         resourceCopier));
+            }
+
+            // Create the reader that can write class files and copy directories
+            // and resource files to the main writer.
             DataEntryReader reader =
-                new ClassFilter(classRewriter, resourceRewriter);
+                new ClassFilter(    classRewriter,
+                new DirectoryFilter(directoryRewriter,
+                                    resourceRewriter));
 
             // Go over the specified input entries and write their processed
             // versions.
@@ -208,4 +224,33 @@ public class OutputWriter
             throw new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")");
         }
     }
+
+
+    /**
+     * Creates a map of old package prefixes to new package prefixes, based on
+     * the given class pool.
+     */
+    private static Map createPackagePrefixMap(ClassPool classPool)
+    {
+        Map PackagePrefixMap = new HashMap();
+
+        Iterator iterator = classPool.classNames();
+        while (iterator.hasNext())
+        {
+            String className     = (String)iterator.next();
+            String PackagePrefix = ClassUtil.internalPackagePrefix(className);
+
+            String mappedNewPackagePrefix = (String)PackagePrefixMap.get(PackagePrefix);
+            if (mappedNewPackagePrefix == null ||
+                !mappedNewPackagePrefix.equals(PackagePrefix))
+            {
+                String newClassName     = classPool.getClass(className).getName();
+                String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName);
+
+                PackagePrefixMap.put(PackagePrefix, newPackagePrefix);
+            }
+        }
+
+        return PackagePrefixMap;
+    }
 }
diff --git a/src/proguard/ParseException.java b/src/proguard/ParseException.java
index 88378b3..9294200 100644
--- a/src/proguard/ParseException.java
+++ b/src/proguard/ParseException.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java
index a9f0e5c..8c30e10 100644
--- a/src/proguard/ProGuard.java
+++ b/src/proguard/ProGuard.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -37,7 +37,7 @@ import java.io.*;
  */
 public class ProGuard
 {
-    public static final String VERSION = "ProGuard, version 4.3";
+    public static final String VERSION = "ProGuard, version 4.4";
 
     private final Configuration configuration;
     private       ClassPool     programClassPool = new ClassPool();
diff --git a/src/proguard/SubclassedClassFilter.java b/src/proguard/SubclassedClassFilter.java
index 268a6fe..27cac11 100644
--- a/src/proguard/SubclassedClassFilter.java
+++ b/src/proguard/SubclassedClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/Targeter.java b/src/proguard/Targeter.java
index e2fcee8..5067205 100644
--- a/src/proguard/Targeter.java
+++ b/src/proguard/Targeter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -52,7 +52,7 @@ public class Targeter
      */
     public void execute(ClassPool programClassPool) throws IOException
     {
-        Set newerClassVersions = configuration.warn ? new HashSet() : null;
+        Set newerClassVersions = configuration.warn != null ? null : new HashSet();
 
         programClassPool.classesAccept(new ClassVersionSetter(configuration.targetClassVersion,
                                                               newerClassVersions));
diff --git a/src/proguard/UpToDateChecker.java b/src/proguard/UpToDateChecker.java
index 0586d12..9fa5d16 100644
--- a/src/proguard/UpToDateChecker.java
+++ b/src/proguard/UpToDateChecker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/WordReader.java b/src/proguard/WordReader.java
index 275f9b9..d73505a 100644
--- a/src/proguard/WordReader.java
+++ b/src/proguard/WordReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ant/ClassPathElement.java b/src/proguard/ant/ClassPathElement.java
index c644c88..b5c2df4 100644
--- a/src/proguard/ant/ClassPathElement.java
+++ b/src/proguard/ant/ClassPathElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,6 +23,7 @@ package proguard.ant;
 import org.apache.tools.ant.*;
 import org.apache.tools.ant.types.*;
 import proguard.*;
+import proguard.util.ListUtil;
 
 import java.io.File;
 
@@ -118,11 +119,11 @@ public class ClassPathElement extends Path
             ClassPathEntry entry =
                 new ClassPathEntry(file.isAbsolute() ? file : new File(baseDir, fileName),
                                    output);
-            entry.setFilter(filter);
-            entry.setJarFilter(jarFilter);
-            entry.setWarFilter(warFilter);
-            entry.setEarFilter(earFilter);
-            entry.setZipFilter(zipFilter);
+            entry.setFilter(ListUtil.commaSeparatedList(filter));
+            entry.setJarFilter(ListUtil.commaSeparatedList(jarFilter));
+            entry.setWarFilter(ListUtil.commaSeparatedList(warFilter));
+            entry.setEarFilter(ListUtil.commaSeparatedList(earFilter));
+            entry.setZipFilter(ListUtil.commaSeparatedList(zipFilter));
 
             // Add it to the class path.
             classPath.add(entry);
diff --git a/src/proguard/ant/ClassSpecificationElement.java b/src/proguard/ant/ClassSpecificationElement.java
index 4356f15..f4ea2ff 100644
--- a/src/proguard/ant/ClassSpecificationElement.java
+++ b/src/proguard/ant/ClassSpecificationElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -92,7 +92,7 @@ public class ClassSpecificationElement extends DataType
             new ClassSpecification(null,
                                    requiredAccessFlags(true,  access, type),
                                    requiredAccessFlags(false, access, type),
-                                   annotation        != null ? ClassUtil.internalType(annotation)   : null,
+                                   annotation        != null ? ClassUtil.internalType(annotation)        : null,
                                    name              != null ? ClassUtil.internalClassName(name)         : null,
                                    extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null,
                                    extends_          != null ? ClassUtil.internalClassName(extends_)     : null);
diff --git a/src/proguard/ant/ConfigurationElement.java b/src/proguard/ant/ConfigurationElement.java
index b9d52af..76e9418 100644
--- a/src/proguard/ant/ConfigurationElement.java
+++ b/src/proguard/ant/ConfigurationElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ant/ConfigurationTask.java b/src/proguard/ant/ConfigurationTask.java
index 66b87b6..0d2f04f 100644
--- a/src/proguard/ant/ConfigurationTask.java
+++ b/src/proguard/ant/ConfigurationTask.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -43,23 +43,47 @@ public class ConfigurationTask extends Task
     public void appendTo(Configuration configuration)
     {
         // Append all of these configuration entries to the given configuration.
-        configuration.programJars = extendClassPath(configuration.programJars,
-                                                    this.configuration.programJars);
+        configuration.programJars               = extendClassPath(configuration.programJars,
+                                                                  this.configuration.programJars);
 
-        configuration.libraryJars = extendClassPath(configuration.libraryJars,
-                                                    this.configuration.libraryJars);
+        configuration.libraryJars               = extendClassPath(configuration.libraryJars,
+                                                                  this.configuration.libraryJars);
 
-        configuration.keep = extendClassSpecifications(configuration.keep,
-                                                       this.configuration.keep);
+        configuration.keep                      = extendClassSpecifications(configuration.keep,
+                                                                            this.configuration.keep);
 
-        configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping,
-                                                                   this.configuration.whyAreYouKeeping);
+        configuration.keepDirectories           = extendList(configuration.keepDirectories,
+                                                             this.configuration.keepDirectories);
 
-        configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects,
-                                                                      this.configuration.assumeNoSideEffects);
+        configuration.whyAreYouKeeping          = extendClassSpecifications(configuration.whyAreYouKeeping,
+                                                                            this.configuration.whyAreYouKeeping);
+
+        configuration.optimizations             = extendClassSpecifications(configuration.optimizations,
+                                                                            this.configuration.optimizations);
+
+        configuration.assumeNoSideEffects       = extendClassSpecifications(configuration.assumeNoSideEffects,
+                                                                            this.configuration.assumeNoSideEffects);
+
+        configuration.keepPackageNames          = extendList(configuration.keepPackageNames,
+                                                             this.configuration.keepPackageNames);
+
+        configuration.keepAttributes            = extendList(configuration.keepAttributes,
+                                                             this.configuration.keepAttributes);
 
-        configuration.keepAttributes = extendList(configuration.keepAttributes,
-                                                  this.configuration.keepAttributes);
+        configuration.adaptClassStrings         = extendList(configuration.adaptClassStrings,
+                                                             this.configuration.adaptClassStrings);
+
+        configuration.adaptResourceFileNames    = extendList(configuration.adaptResourceFileNames,
+                                                             this.configuration.adaptResourceFileNames);
+
+        configuration.adaptResourceFileContents = extendList(configuration.adaptResourceFileContents,
+                                                             this.configuration.adaptResourceFileContents);
+
+        configuration.note                      = extendList(configuration.note,
+                                                             this.configuration.note);
+
+        configuration.warn                      = extendList(configuration.warn,
+                                                             this.configuration.warn);
     }
 
 
@@ -89,6 +113,20 @@ public class ConfigurationTask extends Task
     }
 
 
+    public void addConfiguredKeepdirectory(FilterElement filterElement)
+    {
+        configuration.keepDirectories = extendFilter(configuration.keepDirectories,
+                                                     filterElement);
+    }
+
+
+    public void addConfiguredKeepdirectories(FilterElement filterElement)
+    {
+        configuration.keepDirectories = extendFilter(configuration.keepDirectories,
+                                                     filterElement);
+    }
+
+
     public void addConfiguredKeep(KeepSpecificationElement keepSpecificationElement)
     {
         configuration.keep = extendKeepSpecifications(configuration.keep,
@@ -166,10 +204,52 @@ public class ConfigurationTask extends Task
     }
 
 
-    public void addConfiguredKeepattribute(KeepAttributeElement keepAttributeElement)
+    public void addConfiguredOptimizations(FilterElement filterElement)
+    {
+        addConfiguredOptimization(filterElement);
+    }
+
+
+    public void addConfiguredOptimization(FilterElement filterElement)
     {
-        configuration.keepAttributes = extendAttributes(configuration.keepAttributes,
-                                                        keepAttributeElement);
+        configuration.optimizations = extendFilter(configuration.optimizations,
+                                                   filterElement);
+    }
+
+
+    public void addConfiguredKeeppackagename(FilterElement filterElement)
+    {
+        configuration.keepPackageNames = extendFilter(configuration.keepPackageNames,
+                                                      filterElement,
+                                                      true);
+    }
+
+
+    public void addConfiguredKeeppackagenames(FilterElement filterElement)
+    {
+        configuration.keepPackageNames = extendFilter(configuration.keepPackageNames,
+                                                      filterElement,
+                                                      true);
+    }
+
+
+    public void addConfiguredKeepattributes(FilterElement filterElement)
+    {
+        addConfiguredKeepattribute(filterElement);
+    }
+
+
+    public void addConfiguredKeepattribute(FilterElement filterElement)
+    {
+        configuration.keepAttributes = extendFilter(configuration.keepAttributes,
+                                                    filterElement);
+    }
+
+
+    public void addConfiguredAdaptclassstrings(FilterElement filterElement)
+    {
+        configuration.adaptClassStrings = extendFilter(configuration.adaptClassStrings,
+                                                       filterElement, true);
     }
 
 
@@ -187,6 +267,18 @@ public class ConfigurationTask extends Task
     }
 
 
+    public void addConfiguredDontnote(FilterElement filterElement)
+    {
+        configuration.note = extendFilter(configuration.note, filterElement, true);
+    }
+
+
+    public void addConfiguredDontwarn(FilterElement filterElement)
+    {
+        configuration.warn = extendFilter(configuration.warn, filterElement, true);
+    }
+
+
     public void addConfiguredConfiguration(ConfigurationElement configurationElement)
     {
         configurationElement.appendTo(configuration);
@@ -308,29 +400,23 @@ public class ConfigurationTask extends Task
     }
 
 
-    private List extendAttributes(List                 attributes,
-                                  KeepAttributeElement keepAttributeElement)
+    private List extendFilter(List          filter,
+                              FilterElement filterElement)
     {
-        if (attributes == null)
-        {
-            attributes = new ArrayList();
-        }
-
-        keepAttributeElement.appendTo(attributes);
-
-        return attributes;
+        return extendFilter(filter, filterElement, false);
     }
 
 
     private List extendFilter(List          filter,
-                              FilterElement filterElement)
+                              FilterElement filterElement,
+                              boolean       internal)
     {
         if (filter == null)
         {
             filter = new ArrayList();
         }
 
-        filterElement.appendTo(filter);
+        filterElement.appendTo(filter, internal);
 
         return filter;
     }
diff --git a/src/proguard/ant/FilterElement.java b/src/proguard/ant/FilterElement.java
index 66d25ed..d792c5d 100644
--- a/src/proguard/ant/FilterElement.java
+++ b/src/proguard/ant/FilterElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,6 +21,7 @@
 package proguard.ant;
 
 import org.apache.tools.ant.types.DataType;
+import proguard.classfile.util.ClassUtil;
 import proguard.util.ListUtil;
 
 import java.util.List;
@@ -37,9 +38,11 @@ public class FilterElement extends DataType
 
     /**
      * Adds the contents of this element to the given name filter.
-     * @param filter the list of attributes to be extended.
+     * @param filter   the list of attributes to be extended.
+     * @param internal specifies whether the filter string should be converted
+     *                 to internal types.
      */
-    public void appendTo(List filter)
+    public void appendTo(List filter, boolean internal)
     {
         // Get the referenced element, or else this one.
         FilterElement filterElement = isReference() ?
@@ -56,6 +59,11 @@ public class FilterElement extends DataType
         }
         else
         {
+            if (internal)
+            {
+                filterString = ClassUtil.internalClassName(filterString);
+            }
+
             // Append the filter.
             filter.addAll(ListUtil.commaSeparatedList(filterString));
         }
@@ -64,6 +72,12 @@ public class FilterElement extends DataType
 
     // Ant task attributes.
 
+    public void setName(String name)
+    {
+        this.filter = name;
+    }
+
+
     public void setFilter(String filter)
     {
         this.filter = filter;
diff --git a/src/proguard/ant/KeepAttributeElement.java b/src/proguard/ant/KeepAttributeElement.java
deleted file mode 100644
index ac066a8..0000000
--- a/src/proguard/ant/KeepAttributeElement.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 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.ant;
-
-import org.apache.tools.ant.types.DataType;
-
-import java.util.List;
-
-/**
- * This DataType represents a named attribute in Ant.
- *
- * @author Eric Lafortune
- */
-public class KeepAttributeElement extends DataType
-{
-    private String name;
-
-
-    /**
-     * Adds the contents of this element to the given list of attributes.
-     * @param keepAttributes the list of attributes to be extended.
-     */
-    public void appendTo(List keepAttributes)
-    {
-        // Get the referenced element, or else this one.
-        KeepAttributeElement keepAttributeElement = isReference() ?
-            (KeepAttributeElement)getCheckedRef(this.getClass(),
-                                                this.getClass().getName()) :
-            this;
-
-        String name = keepAttributeElement.name;
-
-        if (name == null)
-        {
-            // Clear the list to keep all attributes.
-            keepAttributes.clear();
-        }
-        else
-        {
-            // Add the attibute name to the list.
-            keepAttributes.add(name);
-        }
-    }
-
-
-    // Ant task attributes.
-
-    public void setName(String name)
-    {
-        this.name = name;
-    }
-}
diff --git a/src/proguard/ant/KeepSpecificationElement.java b/src/proguard/ant/KeepSpecificationElement.java
index 9bc2856..e36b744 100644
--- a/src/proguard/ant/KeepSpecificationElement.java
+++ b/src/proguard/ant/KeepSpecificationElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,7 +20,7 @@
  */
 package proguard.ant;
 
-import proguard.KeepSpecification;
+import proguard.KeepClassSpecification;
 
 import java.util.List;
 
@@ -53,8 +53,8 @@ public class KeepSpecificationElement extends ClassSpecificationElement
                                                      this.getClass().getName()) :
             this;
 
-        KeepSpecification keepSpecification =
-            new KeepSpecification(markClasses,
+        KeepClassSpecification keepClassSpecification =
+            new KeepClassSpecification(markClasses,
                                   markConditionally,
                                   allowShrinking,
                                   allowOptimization,
@@ -62,7 +62,7 @@ public class KeepSpecificationElement extends ClassSpecificationElement
                                   createClassSpecification(keepSpecificationElement));
 
         // Add it to the list.
-        keepSpecifications.add(keepSpecification);
+        keepSpecifications.add(keepClassSpecification);
     }
 
 
diff --git a/src/proguard/ant/MemberSpecificationElement.java b/src/proguard/ant/MemberSpecificationElement.java
index a52541e..d4bb4a9 100644
--- a/src/proguard/ant/MemberSpecificationElement.java
+++ b/src/proguard/ant/MemberSpecificationElement.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/ant/ProGuardTask.java b/src/proguard/ant/ProGuardTask.java
index 0dbd1e3..b7fc361 100644
--- a/src/proguard/ant/ProGuardTask.java
+++ b/src/proguard/ant/ProGuardTask.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -25,6 +25,7 @@ import proguard.*;
 import proguard.classfile.util.ClassUtil;
 
 import java.io.*;
+import java.util.ArrayList;
 
 /**
  * This Task allows to configure and run ProGuard from Ant.
@@ -240,13 +241,13 @@ public class ProGuardTask extends ConfigurationTask
 
     public void setNote(boolean note)
     {
-        configuration.note = note;
+        configuration.note = note ? null : new ArrayList();
     }
 
 
     public void setWarn(boolean warn)
     {
-        configuration.warn = warn;
+        configuration.warn = warn ? null : new ArrayList();
     }
 
 
diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java
index e873e1f..3b243e0 100644
--- a/src/proguard/classfile/ClassConstants.java
+++ b/src/proguard/classfile/ClassConstants.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/ClassPool.java b/src/proguard/classfile/ClassPool.java
index 3ccc787..57728a5 100644
--- a/src/proguard/classfile/ClassPool.java
+++ b/src/proguard/classfile/ClassPool.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/Clazz.java b/src/proguard/classfile/Clazz.java
index e279598..da37d9a 100644
--- a/src/proguard/classfile/Clazz.java
+++ b/src/proguard/classfile/Clazz.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/Field.java b/src/proguard/classfile/Field.java
index 03fe5cd..ba1315a 100644
--- a/src/proguard/classfile/Field.java
+++ b/src/proguard/classfile/Field.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/LibraryClass.java b/src/proguard/classfile/LibraryClass.java
index 77d5f33..0a27593 100644
--- a/src/proguard/classfile/LibraryClass.java
+++ b/src/proguard/classfile/LibraryClass.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/LibraryField.java b/src/proguard/classfile/LibraryField.java
index 91b9070..2908c37 100644
--- a/src/proguard/classfile/LibraryField.java
+++ b/src/proguard/classfile/LibraryField.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/LibraryMember.java b/src/proguard/classfile/LibraryMember.java
index 4dc29c2..41ccb60 100644
--- a/src/proguard/classfile/LibraryMember.java
+++ b/src/proguard/classfile/LibraryMember.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/LibraryMethod.java b/src/proguard/classfile/LibraryMethod.java
index 17e6208..a49a5f1 100644
--- a/src/proguard/classfile/LibraryMethod.java
+++ b/src/proguard/classfile/LibraryMethod.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/Member.java b/src/proguard/classfile/Member.java
index 170b296..1400b9c 100644
--- a/src/proguard/classfile/Member.java
+++ b/src/proguard/classfile/Member.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/Method.java b/src/proguard/classfile/Method.java
index 2131ca8..ebcae2b 100644
--- a/src/proguard/classfile/Method.java
+++ b/src/proguard/classfile/Method.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/ProgramClass.java b/src/proguard/classfile/ProgramClass.java
index 5b1333a..9d0fc0c 100644
--- a/src/proguard/classfile/ProgramClass.java
+++ b/src/proguard/classfile/ProgramClass.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/ProgramField.java b/src/proguard/classfile/ProgramField.java
index 05b3b10..5991b00 100644
--- a/src/proguard/classfile/ProgramField.java
+++ b/src/proguard/classfile/ProgramField.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/ProgramMember.java b/src/proguard/classfile/ProgramMember.java
index a81bf99..ea6f46d 100644
--- a/src/proguard/classfile/ProgramMember.java
+++ b/src/proguard/classfile/ProgramMember.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/ProgramMethod.java b/src/proguard/classfile/ProgramMethod.java
index 70e6b8c..943c3d6 100644
--- a/src/proguard/classfile/ProgramMethod.java
+++ b/src/proguard/classfile/ProgramMethod.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/VisitorAccepter.java b/src/proguard/classfile/VisitorAccepter.java
index a312767..e38f888 100644
--- a/src/proguard/classfile/VisitorAccepter.java
+++ b/src/proguard/classfile/VisitorAccepter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/Attribute.java b/src/proguard/classfile/attribute/Attribute.java
index 0f46fbc..2e16e22 100644
--- a/src/proguard/classfile/attribute/Attribute.java
+++ b/src/proguard/classfile/attribute/Attribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/CodeAttribute.java b/src/proguard/classfile/attribute/CodeAttribute.java
index 4206105..92ff9ea 100644
--- a/src/proguard/classfile/attribute/CodeAttribute.java
+++ b/src/proguard/classfile/attribute/CodeAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/ConstantValueAttribute.java b/src/proguard/classfile/attribute/ConstantValueAttribute.java
index a3b5793..3ae991e 100644
--- a/src/proguard/classfile/attribute/ConstantValueAttribute.java
+++ b/src/proguard/classfile/attribute/ConstantValueAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/DeprecatedAttribute.java b/src/proguard/classfile/attribute/DeprecatedAttribute.java
index cd5b39a..4180950 100644
--- a/src/proguard/classfile/attribute/DeprecatedAttribute.java
+++ b/src/proguard/classfile/attribute/DeprecatedAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java
index 4314865..9275b3a 100644
--- a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java
+++ b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/ExceptionInfo.java b/src/proguard/classfile/attribute/ExceptionInfo.java
index 765c250..082efab 100644
--- a/src/proguard/classfile/attribute/ExceptionInfo.java
+++ b/src/proguard/classfile/attribute/ExceptionInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/ExceptionsAttribute.java b/src/proguard/classfile/attribute/ExceptionsAttribute.java
index 2e91add..d22c4a6 100644
--- a/src/proguard/classfile/attribute/ExceptionsAttribute.java
+++ b/src/proguard/classfile/attribute/ExceptionsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/InnerClassesAttribute.java b/src/proguard/classfile/attribute/InnerClassesAttribute.java
index 15a5460..2f7e310 100644
--- a/src/proguard/classfile/attribute/InnerClassesAttribute.java
+++ b/src/proguard/classfile/attribute/InnerClassesAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/InnerClassesInfo.java b/src/proguard/classfile/attribute/InnerClassesInfo.java
index e8b7fc4..1bdd6c3 100644
--- a/src/proguard/classfile/attribute/InnerClassesInfo.java
+++ b/src/proguard/classfile/attribute/InnerClassesInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/src/proguard/classfile/attribute/LineNumberInfo.java
index cbd42aa..f58083a 100644
--- a/src/proguard/classfile/attribute/LineNumberInfo.java
+++ b/src/proguard/classfile/attribute/LineNumberInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -37,4 +37,14 @@ public class LineNumberInfo
     public LineNumberInfo()
     {
     }
+
+
+    /**
+     * Creates an initialized LineNumberInfo.
+     */
+    public LineNumberInfo(int u2startPC, int u2lineNumber)
+    {
+        this.u2startPC    = u2startPC;
+        this.u2lineNumber = u2lineNumber;
+    }
 }
diff --git a/src/proguard/classfile/attribute/LineNumberTableAttribute.java b/src/proguard/classfile/attribute/LineNumberTableAttribute.java
index 4e3784d..4d507d9 100644
--- a/src/proguard/classfile/attribute/LineNumberTableAttribute.java
+++ b/src/proguard/classfile/attribute/LineNumberTableAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/LocalVariableInfo.java b/src/proguard/classfile/attribute/LocalVariableInfo.java
index 03bf668..4e54c22 100644
--- a/src/proguard/classfile/attribute/LocalVariableInfo.java
+++ b/src/proguard/classfile/attribute/LocalVariableInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -46,6 +46,31 @@ public class LocalVariableInfo
 
 
     /**
+     * Creates an uninitialized LocalVariableInfo.
+     */
+    public LocalVariableInfo()
+    {
+    }
+
+
+    /**
+     * Creates an initialized LocalVariableInfo.
+     */
+    public LocalVariableInfo(int   u2startPC,
+                             int   u2length,
+                             int   u2nameIndex,
+                             int   u2descriptorIndex,
+                             int   u2index)
+    {
+        this.u2startPC         = u2startPC;
+        this.u2length          = u2length;
+        this.u2nameIndex       = u2nameIndex;
+        this.u2descriptorIndex = u2descriptorIndex;
+        this.u2index           = u2index;
+    }
+
+
+    /**
      * Lets the referenced class accept the given visitor.
      */
     public void referencedClassAccept(ClassVisitor classVisitor)
diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java
index 445b15f..9c3f115 100644
--- a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java
+++ b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
index 3f9a960..1b71f35 100644
--- a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
+++ b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -47,6 +47,31 @@ public class LocalVariableTypeInfo
 
 
     /**
+     * Creates an uninitialized LocalVariableTypeInfo.
+     */
+    public LocalVariableTypeInfo()
+    {
+    }
+
+
+    /**
+     * Creates an initialized LocalVariableTypeInfo.
+     */
+    public LocalVariableTypeInfo(int   u2startPC,
+                                 int   u2length,
+                                 int   u2nameIndex,
+                                 int   u2signatureIndex,
+                                 int   u2index)
+    {
+        this.u2startPC        = u2startPC;
+        this.u2length         = u2length;
+        this.u2nameIndex      = u2nameIndex;
+        this.u2signatureIndex = u2signatureIndex;
+        this.u2index          = u2index;
+    }
+
+
+    /**
      * Applies the given visitor to all referenced classes.
      */
     public void referencedClassesAccept(ClassVisitor classVisitor)
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java
index f037d2a..fd856fe 100644
--- a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java
+++ b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/SignatureAttribute.java b/src/proguard/classfile/attribute/SignatureAttribute.java
index 71844f3..c7585fe 100644
--- a/src/proguard/classfile/attribute/SignatureAttribute.java
+++ b/src/proguard/classfile/attribute/SignatureAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/SourceDirAttribute.java b/src/proguard/classfile/attribute/SourceDirAttribute.java
index bdad238..a26e8b1 100644
--- a/src/proguard/classfile/attribute/SourceDirAttribute.java
+++ b/src/proguard/classfile/attribute/SourceDirAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/SourceFileAttribute.java b/src/proguard/classfile/attribute/SourceFileAttribute.java
index 876eb60..24269b7 100644
--- a/src/proguard/classfile/attribute/SourceFileAttribute.java
+++ b/src/proguard/classfile/attribute/SourceFileAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/SyntheticAttribute.java b/src/proguard/classfile/attribute/SyntheticAttribute.java
index af9fc44..6ccb1b5 100644
--- a/src/proguard/classfile/attribute/SyntheticAttribute.java
+++ b/src/proguard/classfile/attribute/SyntheticAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/UnknownAttribute.java b/src/proguard/classfile/attribute/UnknownAttribute.java
index 1c9a665..2f138bd 100644
--- a/src/proguard/classfile/attribute/UnknownAttribute.java
+++ b/src/proguard/classfile/attribute/UnknownAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/Annotation.java b/src/proguard/classfile/attribute/annotation/Annotation.java
index aa48e4d..41bb8e3 100644
--- a/src/proguard/classfile/attribute/annotation/Annotation.java
+++ b/src/proguard/classfile/attribute/annotation/Annotation.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java
index 0bb2302..b378cd2 100644
--- a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
index 59f888d..29129d0 100644
--- a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java
index 2bd564d..8117077 100644
--- a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
index 8bc9148..25b8b9f 100644
--- a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
index 91838b5..ba51641 100644
--- a/src/proguard/classfile/attribute/annotation/ClassElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
index 4b54ad7..3ebe5e5 100644
--- a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/src/proguard/classfile/attribute/annotation/ElementValue.java
index 543f2d0..39f8953 100644
--- a/src/proguard/classfile/attribute/annotation/ElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
index 70c180a..d46bb7f 100644
--- a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java
index 672478a..3c700c8 100644
--- a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java
index 5948cc1..9c8180c 100644
--- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
index 55deb6b..7e41656 100644
--- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java
index 9141f3a..380c52e 100644
--- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
index d2c6b4d..626fbda 100644
--- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java
index 412ebd7..bce7170 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java
index 388fd0d..7a1d7c6 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java
index d521b34..c206c16 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java
index bf11340..d869fd2 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
index 73101f9..16b2a56 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java
index 0d5540a..112084a 100644
--- a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/DoubleType.java b/src/proguard/classfile/attribute/preverification/DoubleType.java
index 7f151d4..d574dcb 100644
--- a/src/proguard/classfile/attribute/preverification/DoubleType.java
+++ b/src/proguard/classfile/attribute/preverification/DoubleType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/FloatType.java b/src/proguard/classfile/attribute/preverification/FloatType.java
index 2d79cab..2f24720 100644
--- a/src/proguard/classfile/attribute/preverification/FloatType.java
+++ b/src/proguard/classfile/attribute/preverification/FloatType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/FullFrame.java b/src/proguard/classfile/attribute/preverification/FullFrame.java
index a4cc663..adf5684 100644
--- a/src/proguard/classfile/attribute/preverification/FullFrame.java
+++ b/src/proguard/classfile/attribute/preverification/FullFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/IntegerType.java b/src/proguard/classfile/attribute/preverification/IntegerType.java
index fc72343..55e3abe 100644
--- a/src/proguard/classfile/attribute/preverification/IntegerType.java
+++ b/src/proguard/classfile/attribute/preverification/IntegerType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java
index b60c3ca..fcc8e0a 100644
--- a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java
+++ b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/LongType.java b/src/proguard/classfile/attribute/preverification/LongType.java
index 2142d68..9b14dd6 100644
--- a/src/proguard/classfile/attribute/preverification/LongType.java
+++ b/src/proguard/classfile/attribute/preverification/LongType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java
index 40af23c..881f188 100644
--- a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java
+++ b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/NullType.java b/src/proguard/classfile/attribute/preverification/NullType.java
index a993a0f..f35cefd 100644
--- a/src/proguard/classfile/attribute/preverification/NullType.java
+++ b/src/proguard/classfile/attribute/preverification/NullType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/ObjectType.java b/src/proguard/classfile/attribute/preverification/ObjectType.java
index f414971..fbdeec7 100644
--- a/src/proguard/classfile/attribute/preverification/ObjectType.java
+++ b/src/proguard/classfile/attribute/preverification/ObjectType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/SameOneFrame.java b/src/proguard/classfile/attribute/preverification/SameOneFrame.java
index 7b9d215..db6747b 100644
--- a/src/proguard/classfile/attribute/preverification/SameOneFrame.java
+++ b/src/proguard/classfile/attribute/preverification/SameOneFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java
index f51df42..64b17f5 100644
--- a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java
+++ b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java
index 1a99d0f..db53ff1 100644
--- a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java
+++ b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/StackMapFrame.java b/src/proguard/classfile/attribute/preverification/StackMapFrame.java
index 2214ed7..aa3e1f2 100644
--- a/src/proguard/classfile/attribute/preverification/StackMapFrame.java
+++ b/src/proguard/classfile/attribute/preverification/StackMapFrame.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java
index 7818804..0cddf70 100644
--- a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java
+++ b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/TopType.java b/src/proguard/classfile/attribute/preverification/TopType.java
index c5176e6..bde8dda 100644
--- a/src/proguard/classfile/attribute/preverification/TopType.java
+++ b/src/proguard/classfile/attribute/preverification/TopType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java
index 564ac1d..dc4654f 100644
--- a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java
+++ b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/UninitializedType.java b/src/proguard/classfile/attribute/preverification/UninitializedType.java
index aa61dd1..a495f1f 100644
--- a/src/proguard/classfile/attribute/preverification/UninitializedType.java
+++ b/src/proguard/classfile/attribute/preverification/UninitializedType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/VerificationType.java b/src/proguard/classfile/attribute/preverification/VerificationType.java
index a1adc3c..f33d511 100644
--- a/src/proguard/classfile/attribute/preverification/VerificationType.java
+++ b/src/proguard/classfile/attribute/preverification/VerificationType.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java
index 1c09367..f8ef7e0 100644
--- a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java
+++ b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java
index 518d4a8..7db246c 100644
--- a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java
+++ b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java
index 669a28f..e9931f8 100644
--- a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java
+++ b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java
index 8c327d0..61b0f1a 100644
--- a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java
similarity index 71%
copy from src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
copy to src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java
index 32c507e..839e104 100644
--- a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/AllExceptionInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -18,29 +18,28 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile.instruction.visitor;
+package proguard.classfile.attribute.visitor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.visitor.AttributeVisitor;
 import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttributeVisitor lets a given InstructionVisitor visit all Instruction
+ * This AttributeVisitor lets a given ExceptionInfoVisitor visit all exceptions
  * objects of the CodeAttribute objects it visits.
  *
  * @author Eric Lafortune
  */
-public class AllInstructionVisitor
+public class AllExceptionInfoVisitor
 extends      SimplifiedVisitor
 implements   AttributeVisitor
 {
-    private final InstructionVisitor instructionVisitor;
+    private final ExceptionInfoVisitor exceptionInfoVisitor;
 
 
-    public AllInstructionVisitor(InstructionVisitor instructionVisitor)
+    public AllExceptionInfoVisitor(ExceptionInfoVisitor exceptionInfoVisitor)
     {
-        this.instructionVisitor = instructionVisitor;
+        this.exceptionInfoVisitor = exceptionInfoVisitor;
     }
 
 
@@ -51,6 +50,6 @@ implements   AttributeVisitor
 
     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        codeAttribute.instructionsAccept(clazz, method, instructionVisitor);
+        codeAttribute.exceptionsAccept(clazz, method, exceptionInfoVisitor);
     }
 }
diff --git a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java
index d7ad85a..aa81ce0 100644
--- a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java
+++ b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java
index d55c4d9..e8f226b 100644
--- a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
index 2717aa7..7c85e53 100644
--- a/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
index 0191bbb..91267b0 100644
--- a/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
index 397b8f2..e59ed7b 100644
--- a/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
index 57901c9..8647cb3 100644
--- a/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
index 33a4f25..9ad38e0 100644
--- a/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java
index a457a85..870ba94 100644
--- a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java
index df88bed..92099f9 100644
--- a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java
+++ b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java
index 28750ef..401f188 100644
--- a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java
+++ b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -276,7 +276,7 @@ implements   AttributeVisitor,
 
     /**
      * Evaluates a block of instructions that hasn't been handled before,
-     * starting at the given offset and ending a branch instruction, a return
+     * starting at the given offset and ending at a branch instruction, a return
      * instruction, or a throw instruction. Branch instructions are handled
      * recursively.
      */
diff --git a/src/proguard/classfile/constant/ClassConstant.java b/src/proguard/classfile/constant/ClassConstant.java
index 374affb..d217bf6 100644
--- a/src/proguard/classfile/constant/ClassConstant.java
+++ b/src/proguard/classfile/constant/ClassConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/Constant.java b/src/proguard/classfile/constant/Constant.java
index 11e6177..30ce5df 100644
--- a/src/proguard/classfile/constant/Constant.java
+++ b/src/proguard/classfile/constant/Constant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/DoubleConstant.java b/src/proguard/classfile/constant/DoubleConstant.java
index 48cff3b..61779b5 100644
--- a/src/proguard/classfile/constant/DoubleConstant.java
+++ b/src/proguard/classfile/constant/DoubleConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/FieldrefConstant.java b/src/proguard/classfile/constant/FieldrefConstant.java
index fd7b0ba..d4afce5 100644
--- a/src/proguard/classfile/constant/FieldrefConstant.java
+++ b/src/proguard/classfile/constant/FieldrefConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/FloatConstant.java b/src/proguard/classfile/constant/FloatConstant.java
index 399f0f9..578f567 100644
--- a/src/proguard/classfile/constant/FloatConstant.java
+++ b/src/proguard/classfile/constant/FloatConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/IntegerConstant.java b/src/proguard/classfile/constant/IntegerConstant.java
index 760c856..8a476c6 100644
--- a/src/proguard/classfile/constant/IntegerConstant.java
+++ b/src/proguard/classfile/constant/IntegerConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java
index 2bac149..ddee42f 100644
--- a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java
+++ b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/LongConstant.java b/src/proguard/classfile/constant/LongConstant.java
index 2a8eb29..ea66e07 100644
--- a/src/proguard/classfile/constant/LongConstant.java
+++ b/src/proguard/classfile/constant/LongConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/MethodrefConstant.java b/src/proguard/classfile/constant/MethodrefConstant.java
index 82cce97..858eec9 100644
--- a/src/proguard/classfile/constant/MethodrefConstant.java
+++ b/src/proguard/classfile/constant/MethodrefConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/NameAndTypeConstant.java b/src/proguard/classfile/constant/NameAndTypeConstant.java
index c224f36..e83d2f1 100644
--- a/src/proguard/classfile/constant/NameAndTypeConstant.java
+++ b/src/proguard/classfile/constant/NameAndTypeConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/RefConstant.java b/src/proguard/classfile/constant/RefConstant.java
index c6179ef..4e4d019 100644
--- a/src/proguard/classfile/constant/RefConstant.java
+++ b/src/proguard/classfile/constant/RefConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/StringConstant.java b/src/proguard/classfile/constant/StringConstant.java
index 0a4b3a9..9a8d453 100644
--- a/src/proguard/classfile/constant/StringConstant.java
+++ b/src/proguard/classfile/constant/StringConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/Utf8Constant.java b/src/proguard/classfile/constant/Utf8Constant.java
index ad677c7..ae419c9 100644
--- a/src/proguard/classfile/constant/Utf8Constant.java
+++ b/src/proguard/classfile/constant/Utf8Constant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -32,24 +32,22 @@ import java.io.UnsupportedEncodingException;
  */
 public class Utf8Constant extends Constant
 {
-    private static final String ENCODING = "UTF-8";
-
     private static final char TWO_BYTE_LIMIT     = 0x80;
-    private static final byte TWO_BYTE_CONSTANT1 = (byte)0xc0;
-    private static final byte TWO_BYTE_CONSTANT2 = (byte)0x80;
+    private static final int  TWO_BYTE_CONSTANT1 = 0xc0;
+    private static final int  TWO_BYTE_CONSTANT2 = 0x80;
     private static final int  TWO_BYTE_SHIFT1    = 6;
-    private static final byte TWO_BYTE_MASK1     = (byte)0x1f;
-    private static final byte TWO_BYTE_MASK2     = (byte)0x3f;
+    private static final int  TWO_BYTE_MASK1     = 0x1f;
+    private static final int  TWO_BYTE_MASK2     = 0x3f;
 
     private static final char THREE_BYTE_LIMIT     = 0x800;
-    private static final byte THREE_BYTE_CONSTANT1 = (byte)0xe0;
-    private static final byte THREE_BYTE_CONSTANT2 = (byte)0x80;
-    private static final byte THREE_BYTE_CONSTANT3 = (byte)0x80;
+    private static final int  THREE_BYTE_CONSTANT1 = 0xe0;
+    private static final int  THREE_BYTE_CONSTANT2 = 0x80;
+    private static final int  THREE_BYTE_CONSTANT3 = 0x80;
     private static final int  THREE_BYTE_SHIFT1    = 12;
     private static final int  THREE_BYTE_SHIFT2    = 6;
-    private static final byte THREE_BYTE_MASK1     = (byte)0x0f;
-    private static final byte THREE_BYTE_MASK2     = (byte)0x3f;
-    private static final byte THREE_BYTE_MASK3     = (byte)0x3f;
+    private static final int  THREE_BYTE_MASK1     = 0x0f;
+    private static final int  THREE_BYTE_MASK2     = 0x3f;
+    private static final int  THREE_BYTE_MASK3     = 0x3f;
 
 
     // There are a lot of Utf8Constant objects, so we're optimising their storage.
@@ -60,7 +58,7 @@ public class Utf8Constant extends Constant
     //private int u2length;
     private byte[] bytes;
 
-    private String utf8string;
+    private String string;
 
 
     /**
@@ -75,10 +73,10 @@ public class Utf8Constant extends Constant
     /**
      * Creates a Utf8Constant containing the given string.
      */
-    public Utf8Constant(String utf8string)
+    public Utf8Constant(String string)
     {
-        this.bytes      = null;
-        this.utf8string = utf8string;
+        this.bytes  = null;
+        this.string = string;
     }
 
 
@@ -87,8 +85,8 @@ public class Utf8Constant extends Constant
      */
     public void setBytes(byte[] bytes)
     {
-        this.bytes      = bytes;
-        this.utf8string = null;
+        this.bytes  = bytes;
+        this.string = null;
     }
 
 
@@ -99,12 +97,14 @@ public class Utf8Constant extends Constant
     {
         try
         {
-            return getByteArrayRepresentation();
+            switchToByteArrayRepresentation();
         }
         catch (UnsupportedEncodingException ex)
         {
             throw new RuntimeException(ex.getMessage());
         }
+
+        return bytes;
     }
 
 
@@ -113,8 +113,8 @@ public class Utf8Constant extends Constant
      */
     public void setString(String utf8String)
     {
-        this.bytes      = null;
-        this.utf8string = utf8String;
+        this.bytes  = null;
+        this.string = utf8String;
     }
 
 
@@ -132,9 +132,10 @@ public class Utf8Constant extends Constant
             throw new RuntimeException(ex.getMessage());
         }
 
-        return utf8string;
+        return string;
     }
 
+
     // Implementations for Constant.
 
     public int getTag()
@@ -148,42 +149,49 @@ public class Utf8Constant extends Constant
     }
 
 
+    // Small utility methods.
+
     /**
-     * Switches to a String representation of the UTF-8 data.
+     * Switches to a byte array representation of the UTF-8 data.
      */
-    private void switchToStringRepresentation() throws UnsupportedEncodingException
+    private void switchToByteArrayRepresentation() throws UnsupportedEncodingException
     {
-        if (utf8string == null)
+        if (bytes == null)
         {
-            utf8string = new String(bytes, ENCODING);
-            bytes      = null;
+            bytes  = getByteArrayRepresentation(string);
+            string = null;
         }
     }
 
 
     /**
-     * Transforms UTF-8 bytes to the slightly modified UTF-8 representation that
-     * is used by classes.
+     * Switches to a String representation of the UTF-8 data.
      */
-    private byte[] getByteArrayRepresentation() throws UnsupportedEncodingException
+    private void switchToStringRepresentation() throws UnsupportedEncodingException
     {
-        // Do we still have the byte array representation?
-        if (bytes != null)
+        if (string == null)
         {
-            // Then return that one.
-            return bytes;
+            string = getStringRepresentation(bytes);
+            bytes  = null;
         }
+    }
+
 
+    /**
+     * Returns the modified UTF-8 byte array representation of the given string.
+     */
+    private byte[] getByteArrayRepresentation(String string) throws UnsupportedEncodingException
+    {
         // We're computing the byte array ourselves, because the implementation
         // of String.getBytes("UTF-8") has a bug, at least up to JRE 1.4.2.
         // Also note the special treatment of the 0 character.
 
         // Compute the byte array length.
         int byteLength   = 0;
-        int stringLength = utf8string.length();
+        int stringLength = string.length();
         for (int stringIndex = 0; stringIndex < stringLength; stringIndex++)
         {
-            char c = utf8string.charAt(stringIndex);
+            char c = string.charAt(stringIndex);
 
             // The character is represented by one, two, or three bytes.
             byteLength += c == 0                ? 2 :
@@ -199,12 +207,12 @@ public class Utf8Constant extends Constant
         int byteIndex = 0;
         for (int stringIndex = 0; stringIndex < stringLength; stringIndex++)
         {
-            char c = utf8string.charAt(stringIndex);
+            char c = string.charAt(stringIndex);
             if (c == 0)
             {
                 // The 0 character gets a two-byte representation in classes.
-                bytes[byteIndex++] = TWO_BYTE_CONSTANT1;
-                bytes[byteIndex++] = TWO_BYTE_CONSTANT2;
+                bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT1;
+                bytes[byteIndex++] = (byte)TWO_BYTE_CONSTANT2;
             }
             else if (c < TWO_BYTE_LIMIT)
             {
@@ -228,4 +236,50 @@ public class Utf8Constant extends Constant
 
         return bytes;
     }
+
+
+    /**
+     * Returns the String representation of the given modified UTF-8 byte array.
+     */
+    private String getStringRepresentation(byte[] bytes) throws UnsupportedEncodingException
+    {
+        // We're computing the string ourselves, because the implementation
+        // of "new String(bytes)" doesn't honor the special treatment of
+        // the 0 character in JRE 1.6_u11.
+
+        // Allocate the byte array with the computed length.
+        char[] chars  = new char[bytes.length];
+
+        // Fill out the array.
+        int charIndex = 0;
+        int byteIndex = 0;
+        while (byteIndex < bytes.length)
+        {
+
+            int b = bytes[byteIndex++] & 0xff;
+
+            // Depending on the flag bits in the first byte, the character
+            // is represented by a single byte, by two bytes, or by three
+            // bytes. We're not checking the redundant flag bits in the
+            // second byte and the third byte.
+            try
+            {
+                chars[charIndex++] =
+                    (char)(b < TWO_BYTE_CONSTANT1   ? b                                                          :
+
+                           b < THREE_BYTE_CONSTANT1 ? ((b                  & TWO_BYTE_MASK1) << TWO_BYTE_SHIFT1) |
+                                                      ((bytes[byteIndex++] & TWO_BYTE_MASK2)                   ) :
+
+                                                      ((b                  & THREE_BYTE_MASK1) << THREE_BYTE_SHIFT1) |
+                                                      ((bytes[byteIndex++] & THREE_BYTE_MASK2) << THREE_BYTE_SHIFT2) |
+                                                      ((bytes[byteIndex++] & THREE_BYTE_MASK3)                     ));
+            }
+            catch (ArrayIndexOutOfBoundsException e)
+            {
+                throw new UnsupportedEncodingException("Missing UTF-8 bytes after initial byte [0x"+Integer.toHexString(b)+"] in string ["+new String(chars, 0, charIndex)+"]");
+            }
+        }
+
+        return new String(chars, 0, charIndex);
+    }
 }
diff --git a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java
index 3530660..d2d3c2c 100644
--- a/src/proguard/classfile/constant/visitor/AllConstantVisitor.java
+++ b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/visitor/ConstantVisitor.java b/src/proguard/classfile/constant/visitor/ConstantVisitor.java
index 6c7a851..6cae352 100644
--- a/src/proguard/classfile/constant/visitor/ConstantVisitor.java
+++ b/src/proguard/classfile/constant/visitor/ConstantVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java
index fc823c8..fbb3e52 100644
--- a/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java
+++ b/src/proguard/classfile/constant/visitor/ExceptClassConstantFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/AccessFixer.java b/src/proguard/classfile/editor/AccessFixer.java
index 786e241..7d6274e 100644
--- a/src/proguard/classfile/editor/AccessFixer.java
+++ b/src/proguard/classfile/editor/AccessFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/AnnotationAdder.java b/src/proguard/classfile/editor/AnnotationAdder.java
index 92ae382..359164a 100644
--- a/src/proguard/classfile/editor/AnnotationAdder.java
+++ b/src/proguard/classfile/editor/AnnotationAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -27,7 +27,8 @@ import proguard.classfile.util.SimplifiedVisitor;
 
 /**
  * This AnnotationVisitor adds all annotations that it visits to the given
- * target annotation element value or target annotation attribute.
+ * target annotation element value, target annotation attribute, or target
+ * parameter annotation attribute.
  *
  * @author Eric Lafortune
  */
@@ -38,11 +39,12 @@ implements   AnnotationVisitor
     private static final ElementValue[] EMPTY_ELEMENT_VALUES = new ElementValue[0];
 
 
-    private final ProgramClass           targetClass;
-    private final AnnotationElementValue targetAnnotationElementValue;
+    private final ProgramClass                        targetClass;
+    private final AnnotationElementValue              targetAnnotationElementValue;
+    private final AnnotationsAttributeEditor          annotationsAttributeEditor;
+    private final ParameterAnnotationsAttributeEditor parameterAnnotationsAttributeEditor;
 
-    private final ConstantAdder              constantAdder;
-    private final AnnotationsAttributeEditor annotationsAttributeEditor;
+    private final ConstantAdder constantAdder;
 
 
     /**
@@ -52,26 +54,44 @@ implements   AnnotationVisitor
     public AnnotationAdder(ProgramClass           targetClass,
                            AnnotationElementValue targetAnnotationElementValue)
     {
-        this.targetClass                  = targetClass;
-        this.targetAnnotationElementValue = targetAnnotationElementValue;
+        this.targetClass                         = targetClass;
+        this.targetAnnotationElementValue        = targetAnnotationElementValue;
+        this.annotationsAttributeEditor          = null;
+        this.parameterAnnotationsAttributeEditor = null;
 
-        constantAdder              = new ConstantAdder(targetClass);
-        annotationsAttributeEditor = null;
+        constantAdder = new ConstantAdder(targetClass);
     }
 
 
     /**
      * Creates a new AnnotationAdder that will copy annotations into the given
-     * target annotation.
+     * target annotations attribute.
      */
     public AnnotationAdder(ProgramClass         targetClass,
                            AnnotationsAttribute targetAnnotationsAttribute)
     {
-        this.targetClass                  = targetClass;
-        this.targetAnnotationElementValue = null;
+        this.targetClass                         = targetClass;
+        this.targetAnnotationElementValue        = null;
+        this.annotationsAttributeEditor          = new AnnotationsAttributeEditor(targetAnnotationsAttribute);
+        this.parameterAnnotationsAttributeEditor = null;
 
-        constantAdder              = new ConstantAdder(targetClass);
-        annotationsAttributeEditor = new AnnotationsAttributeEditor(targetAnnotationsAttribute);
+        constantAdder = new ConstantAdder(targetClass);
+    }
+
+
+    /**
+     * Creates a new AnnotationAdder that will copy annotations into the given
+     * target parameter annotations attribute.
+     */
+    public AnnotationAdder(ProgramClass                  targetClass,
+                           ParameterAnnotationsAttribute targetParameterAnnotationsAttribute)
+    {
+        this.targetClass                         = targetClass;
+        this.targetAnnotationElementValue        = null;
+        this.annotationsAttributeEditor          = null;
+        this.parameterAnnotationsAttributeEditor = new ParameterAnnotationsAttributeEditor(targetParameterAnnotationsAttribute);
+
+        constantAdder = new ConstantAdder(targetClass);
     }
 
 
@@ -111,6 +131,23 @@ implements   AnnotationVisitor
 
     public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation)
     {
-        // TODO: Handle parameter annotations.
+        Annotation newAnnotation =
+            new Annotation(constantAdder.addConstant(clazz, annotation.u2typeIndex),
+                           0,
+                           annotation.u2elementValuesCount > 0 ?
+                               new ElementValue[annotation.u2elementValuesCount] :
+                               EMPTY_ELEMENT_VALUES);
+
+        // TODO: Clone array.
+        newAnnotation.referencedClasses = annotation.referencedClasses;
+
+        // Add the element values.
+        annotation.elementValuesAccept(clazz,
+                                       new ElementValueAdder(targetClass,
+                                                             newAnnotation,
+                                                             false));
+
+        // Add the completed annotation.
+        parameterAnnotationsAttributeEditor.addAnnotation(parameterIndex, newAnnotation);
     }
 }
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java
index a579ff7..bf8852c 100644
--- a/src/proguard/classfile/editor/AnnotationsAttributeEditor.java
+++ b/src/proguard/classfile/editor/AnnotationsAttributeEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/AttributeAdder.java b/src/proguard/classfile/editor/AttributeAdder.java
index 004019f..2b610b7 100644
--- a/src/proguard/classfile/editor/AttributeAdder.java
+++ b/src/proguard/classfile/editor/AttributeAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -256,13 +256,17 @@ implements   AttributeVisitor
 
         CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
 
-        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength + 32);
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
 
         // Add the instructions.
         codeAttribute.instructionsAccept(clazz,
                                          method,
                                          new InstructionAdder(targetClass,
                                                               codeAttributeComposer));
+
+        // Append a label just after the code.
+        codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
+
         // Add the exceptions.
         codeAttribute.exceptionsAccept(clazz,
                                        method,
@@ -303,19 +307,58 @@ implements   AttributeVisitor
 
     public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
-        // TODO: Implement method.
+        // Create a new line number table attribute.
+        LineNumberTableAttribute newLineNumberTableAttribute =
+            new LineNumberTableAttribute(constantAdder.addConstant(clazz, lineNumberTableAttribute.u2attributeNameIndex),
+                                         0,
+                                         new LineNumberInfo[lineNumberTableAttribute.u2lineNumberTableLength]);
+
+        // Add the line numbers.
+        lineNumberTableAttribute.lineNumbersAccept(clazz,
+                                                   method,
+                                                   codeAttribute,
+                                                   new LineNumberInfoAdder(newLineNumberTableAttribute));
+
+        // Add it to the target.
+        attributesEditor.addAttribute(newLineNumberTableAttribute);
     }
 
 
     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-        // TODO: Implement method.
+        // Create a new local variable table attribute.
+        LocalVariableTableAttribute newLocalVariableTableAttribute =
+            new LocalVariableTableAttribute(constantAdder.addConstant(clazz, localVariableTableAttribute.u2attributeNameIndex),
+                                            0,
+                                            new LocalVariableInfo[localVariableTableAttribute.u2localVariableTableLength]);
+
+        // Add the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz,
+                                                         method,
+                                                         codeAttribute,
+                                                         new LocalVariableInfoAdder(targetClass, newLocalVariableTableAttribute));
+
+        // Add it to the target.
+        attributesEditor.addAttribute(newLocalVariableTableAttribute);
     }
 
 
     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-        // TODO: Implement method.
+        // Create a new local variable type table attribute.
+        LocalVariableTypeTableAttribute newLocalVariableTypeTableAttribute =
+            new LocalVariableTypeTableAttribute(constantAdder.addConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex),
+                                            0,
+                                            new LocalVariableTypeInfo[localVariableTypeTableAttribute.u2localVariableTypeTableLength]);
+
+        // Add the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz,
+                                                             method,
+                                                             codeAttribute,
+                                                             new LocalVariableTypeInfoAdder(targetClass, newLocalVariableTypeTableAttribute));
+
+        // Add it to the target.
+        attributesEditor.addAttribute(newLocalVariableTypeTableAttribute);
     }
 
 
@@ -357,13 +400,41 @@ implements   AttributeVisitor
 
     public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
     {
-        // TODO: Implement method.
+        // Create a new annotations attribute.
+        RuntimeVisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute =
+            new RuntimeVisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeVisibleParameterAnnotationsAttribute.u2attributeNameIndex),
+                                                            0,
+                                                            new int[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount],
+                                                            new Annotation[runtimeVisibleParameterAnnotationsAttribute.u2parametersCount][]);
+
+        // Add the annotations.
+        runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz,
+                                                                      method,
+                                                                      new AnnotationAdder(targetClass,
+                                                                                          newParameterAnnotationsAttribute));
+
+        // Add it to the target.
+        attributesEditor.addAttribute(newParameterAnnotationsAttribute);
     }
 
 
     public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
     {
-        // TODO: Implement method.
+        // Create a new annotations attribute.
+        RuntimeInvisibleParameterAnnotationsAttribute newParameterAnnotationsAttribute =
+            new RuntimeInvisibleParameterAnnotationsAttribute(constantAdder.addConstant(clazz, runtimeInvisibleParameterAnnotationsAttribute.u2attributeNameIndex),
+                                                              0,
+                                                              new int[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount],
+                                                              new Annotation[runtimeInvisibleParameterAnnotationsAttribute.u2parametersCount][]);
+
+        // Add the annotations.
+        runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz,
+                                                                        method,
+                                                                        new AnnotationAdder(targetClass,
+                                                                                            newParameterAnnotationsAttribute));
+
+        // Add it to the target.
+        attributesEditor.addAttribute(newParameterAnnotationsAttribute);
     }
 
 
diff --git a/src/proguard/classfile/editor/AttributeSorter.java b/src/proguard/classfile/editor/AttributeSorter.java
index 4e24d5a..d8e3367 100644
--- a/src/proguard/classfile/editor/AttributeSorter.java
+++ b/src/proguard/classfile/editor/AttributeSorter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/AttributesEditor.java b/src/proguard/classfile/editor/AttributesEditor.java
index efd681e..10846cc 100644
--- a/src/proguard/classfile/editor/AttributesEditor.java
+++ b/src/proguard/classfile/editor/AttributesEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ClassEditor.java b/src/proguard/classfile/editor/ClassEditor.java
index 9887600..e503ea3 100644
--- a/src/proguard/classfile/editor/ClassEditor.java
+++ b/src/proguard/classfile/editor/ClassEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ClassElementSorter.java b/src/proguard/classfile/editor/ClassElementSorter.java
index df250f7..3256c88 100644
--- a/src/proguard/classfile/editor/ClassElementSorter.java
+++ b/src/proguard/classfile/editor/ClassElementSorter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ClassMemberSorter.java b/src/proguard/classfile/editor/ClassMemberSorter.java
index 66de832..f31fcd0 100644
--- a/src/proguard/classfile/editor/ClassMemberSorter.java
+++ b/src/proguard/classfile/editor/ClassMemberSorter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ClassReferenceFixer.java b/src/proguard/classfile/editor/ClassReferenceFixer.java
index 34624f6..9857903 100644
--- a/src/proguard/classfile/editor/ClassReferenceFixer.java
+++ b/src/proguard/classfile/editor/ClassReferenceFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java
index d810403..e783203 100644
--- a/src/proguard/classfile/editor/CodeAttributeComposer.java
+++ b/src/proguard/classfile/editor/CodeAttributeComposer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -69,9 +69,9 @@ implements   AttributeVisitor,
 
     private final int[]   codeFragmentOffsets  = new int[MAXIMUM_LEVELS];
     private final int[]   codeFragmentLengths  = new int[MAXIMUM_LEVELS];
-    private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH];
+    private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH + 1];
 
-    private ExceptionInfo[] exceptionTable   =  new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
+    private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
 
     private int expectedStackMapFrameOffset;
 
@@ -136,16 +136,8 @@ implements   AttributeVisitor,
 
         // Make sure there is sufficient space for adding the code fragment.
         maximumCodeLength += maximumCodeFragmentLength;
-        if (code.length < maximumCodeLength)
-        {
-            byte[] newCode = new byte[maximumCodeLength];
-            System.arraycopy(code, 0, newCode, 0, codeLength);
-            code = newCode;
 
-            int[] newOldInstructionOffsets = new int[maximumCodeLength];
-            System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength);
-            oldInstructionOffsets = newOldInstructionOffsets;
-        }
+        ensureCodeLength(maximumCodeLength);
 
         // Try to reuse the previous array for this code fragment.
         if (instructionOffsetMap[level].length <= maximumCodeFragmentLength)
@@ -180,6 +172,11 @@ implements   AttributeVisitor,
             println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
         }
 
+        // Make sure the code array is large enough.
+        int newCodeLength = codeLength + instruction.length(codeLength);
+
+        ensureCodeLength(newCodeLength);
+
         // Remember the old offset of the appended instruction.
         oldInstructionOffsets[codeLength] = oldInstructionOffset;
 
@@ -195,7 +192,7 @@ implements   AttributeVisitor,
         instructionOffsetMap[level][oldInstructionOffset] = codeLength;
 
         // Continue appending at the next instruction offset.
-        codeLength += instruction.length(codeLength);
+        codeLength = newCodeLength;
     }
 
 
@@ -316,7 +313,7 @@ implements   AttributeVisitor,
                 int handlerPC = -exceptionInfo.u2handlerPC;
                 if (handlerPC > 0)
                 {
-                    if (instructionOffsetMap[level][handlerPC] < codeLength)
+                    if (remappableInstructionOffset(handlerPC))
                     {
                         exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC);
                     }
@@ -492,10 +489,10 @@ implements   AttributeVisitor,
         // handlers are negated, in order to mark them as external.
         int handlerPC = exceptionInfo.u2handlerPC;
         exceptionInfo.u2handlerPC =
-            allowExternalExceptionHandlers &&
-            instructionOffsetMap[level][handlerPC] >= codeLength ?
-                -handlerPC :
-                remapInstructionOffset(handlerPC);
+            !allowExternalExceptionHandlers ||
+            remappableInstructionOffset(handlerPC) ?
+                remapInstructionOffset(handlerPC) :
+                -handlerPC;
     }
 
 
@@ -603,6 +600,27 @@ implements   AttributeVisitor,
     // Small utility methods.
 
     /**
+     * Make sure the code arrays have at least the given size.
+     */
+    private void ensureCodeLength(int newCodeLength)
+    {
+        if (code.length < newCodeLength)
+        {
+            // Add 20% to avoid extending the arrays too often.
+            newCodeLength = newCodeLength * 6 / 5;
+
+            byte[] newCode = new byte[newCodeLength];
+            System.arraycopy(code, 0, newCode, 0, codeLength);
+            code = newCode;
+
+            int[] newOldInstructionOffsets = new int[newCodeLength];
+            System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength);
+            oldInstructionOffsets = newOldInstructionOffsets;
+        }
+    }
+
+
+    /**
      * Adjusts the given jump offsets for the instruction at the given offset.
      */
     private void remapJumpOffsets(int offset, int[] jumpOffsets)
@@ -656,6 +674,17 @@ implements   AttributeVisitor,
 
 
     /**
+     * Returns whether the given old instruction offset can be remapped at the
+     */
+    private boolean remappableInstructionOffset(int oldInstructionOffset)
+    {
+        return
+            oldInstructionOffset <= codeFragmentLengths[level] &&
+            instructionOffsetMap[level][oldInstructionOffset] > INVALID;
+    }
+
+
+    /**
      * Returns the given list of exceptions, without the ones that have empty
      * code blocks.
      */
@@ -798,12 +827,12 @@ implements   AttributeVisitor,
     {
         CodeAttributeComposer composer = new CodeAttributeComposer();
 
-        composer.beginCodeFragment(10);
+        composer.beginCodeFragment(4);
         composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0));
         composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0));
         composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1));
 
-        composer.beginCodeFragment(10);
+        composer.beginCodeFragment(4);
         composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1));
         composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0));
         composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5));
diff --git a/src/proguard/classfile/editor/CodeAttributeEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java
index dc7915c..9658c98 100644
--- a/src/proguard/classfile/editor/CodeAttributeEditor.java
+++ b/src/proguard/classfile/editor/CodeAttributeEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -28,6 +28,7 @@ import proguard.classfile.attribute.visitor.*;
 import proguard.classfile.instruction.*;
 import proguard.classfile.instruction.visitor.InstructionVisitor;
 import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassPrinter;
 
 /**
  * This AttributeVisitor accumulates specified changes to code, and then applies
@@ -142,6 +143,28 @@ implements   AttributeVisitor,
 
 
     /**
+     * Remembers to place the given instructions right before the instruction
+     * at the given offset.
+     * @param instructionOffset the offset of the instruction.
+     * @param instructions      the new instructions.
+     */
+    public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        preInsertions[instructionOffset] = new CompositeInstruction(instructions);
+
+        modified = true;
+        simple   = false;
+
+    }
+
+
+    /**
      * Remembers to replace the instruction at the given offset by the given
      * instruction.
      * @param instructionOffset the offset of the instruction to be replaced.
@@ -162,6 +185,26 @@ implements   AttributeVisitor,
 
 
     /**
+     * Remembers to replace the instruction at the given offset by the given
+     * instructions.
+     * @param instructionOffset the offset of the instruction to be replaced.
+     * @param instructions      the new instructions.
+     */
+    public void replaceInstruction(int instructionOffset, Instruction[] instructions)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        replacements[instructionOffset] = new CompositeInstruction(instructions);
+
+        modified = true;
+    }
+
+
+    /**
      * Remembers to place the given instruction right after the instruction
      * at the given offset.
      * @param instructionOffset the offset of the instruction.
@@ -183,6 +226,27 @@ implements   AttributeVisitor,
 
 
     /**
+     * Remembers to place the given instructions right after the instruction
+     * at the given offset.
+     * @param instructionOffset the offset of the instruction.
+     * @param instructions      the new instructions.
+     */
+    public void insertAfterInstruction(int instructionOffset, Instruction[] instructions)
+    {
+        if (instructionOffset < 0 ||
+            instructionOffset >= codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
+        }
+
+        postInsertions[instructionOffset] = new CompositeInstruction(instructions);
+
+        modified = true;
+        simple   = false;
+    }
+
+
+    /**
      * Remembers to delete the instruction at the given offset.
      * @param instructionOffset the offset of the instruction to be deleted.
      */
@@ -610,15 +674,13 @@ implements   AttributeVisitor,
         Instruction preInstruction = preInsertions[oldOffset];
         if (preInstruction != null)
         {
-            // Remap the instruction.
-            preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
-
             if (DEBUG)
             {
                 System.out.println("  Pre-inserted  "+preInstruction.toString(newOffset));
             }
 
-            newOffset += preInstruction.length(newOffset);
+            // Remap the instruction.
+            preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
         }
 
         // Remap and insert the replacement instruction, or the current
@@ -626,42 +688,35 @@ implements   AttributeVisitor,
         Instruction replacementInstruction = replacements[oldOffset];
         if (replacementInstruction != null)
         {
-            // Remap the instruction.
-            replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
-
             if (DEBUG)
             {
                 System.out.println("  Replaced      "+replacementInstruction.toString(newOffset));
             }
-
-            newOffset += replacementInstruction.length(newOffset);
+            // Remap the instruction.
+            replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
         }
         else if (!deleted[oldOffset])
         {
-            // Remap the instruction.
-            instruction.accept(clazz, method, codeAttribute, oldOffset, this);
-
             if (DEBUG)
             {
                 System.out.println("  Copied        "+instruction.toString(newOffset));
             }
 
-            newOffset += instruction.length(newOffset);
+            // Remap the instruction.
+            instruction.accept(clazz, method, codeAttribute, oldOffset, this);
         }
 
         // Remap and insert the post-inserted instruction, if any.
         Instruction postInstruction = postInsertions[oldOffset];
         if (postInstruction != null)
         {
-            // Remap the instruction.
-            postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
-
             if (DEBUG)
             {
                 System.out.println("  Post-inserted "+postInstruction.toString(newOffset));
             }
 
-            newOffset += postInstruction.length(newOffset);
+            // Remap the instruction.
+            postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
         }
     }
 
@@ -676,6 +731,8 @@ implements   AttributeVisitor,
                                                  codeAttribute,
                                                  newOffset,
                                                  simpleInstruction);
+
+        newOffset += simpleInstruction.length(newOffset);
     }
 
 
@@ -687,6 +744,8 @@ implements   AttributeVisitor,
                                                    codeAttribute,
                                                    newOffset,
                                                    constantInstruction);
+
+        newOffset += constantInstruction.length(newOffset);
     }
 
 
@@ -698,6 +757,8 @@ implements   AttributeVisitor,
                                                    codeAttribute,
                                                    newOffset,
                                                    variableInstruction);
+
+        newOffset += variableInstruction.length(newOffset);
     }
 
 
@@ -713,6 +774,8 @@ implements   AttributeVisitor,
                                                  codeAttribute,
                                                  newOffset,
                                                  branchInstruction);
+
+        newOffset += branchInstruction.length(newOffset);
     }
 
 
@@ -732,6 +795,8 @@ implements   AttributeVisitor,
                                                       codeAttribute,
                                                       newOffset,
                                                       tableSwitchInstruction);
+
+        newOffset += tableSwitchInstruction.length(newOffset);
     }
 
 
@@ -751,6 +816,8 @@ implements   AttributeVisitor,
                                                        codeAttribute,
                                                        newOffset,
                                                        lookUpSwitchInstruction);
+
+        newOffset += lookUpSwitchInstruction.length(newOffset);
     }
 
 
@@ -996,4 +1063,101 @@ implements   AttributeVisitor,
 
         return newIndex;
     }
+
+
+    private class CompositeInstruction
+    extends       Instruction
+    {
+        private Instruction[] instructions;
+
+
+        private CompositeInstruction(Instruction[] instructions)
+        {
+            this.instructions = instructions;
+        }
+
+
+        // Implementations for Instruction.
+
+        public Instruction shrink()
+        {
+            for (int index = 0; index < instructions.length; index++)
+            {
+                instructions[index] = instructions[index].shrink();
+            }
+
+            return this;
+        }
+
+
+        public void write(byte[] code, int offset)
+        {
+            for (int index = 0; index < instructions.length; index++)
+            {
+                Instruction instruction = instructions[index];
+
+                instruction.write(code, offset);
+
+                offset += instruction.length(offset);
+            }
+        }
+
+
+        protected void readInfo(byte[] code, int offset)
+        {
+            throw new UnsupportedOperationException("Can't read composite instruction");
+        }
+
+
+        protected void writeInfo(byte[] code, int offset)
+        {
+            throw new UnsupportedOperationException("Can't write composite instruction");
+        }
+
+
+        public int length(int offset)
+        {
+            int newOffset = offset;
+
+            for (int index = 0; index < instructions.length; index++)
+            {
+                newOffset += instructions[index].length(newOffset);
+            }
+
+            return newOffset - offset;
+        }
+
+
+        public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
+        {
+            if (instructionVisitor != CodeAttributeEditor.this)
+            {
+                throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]");
+            }
+
+            for (int index = 0; index < instructions.length; index++)
+            {
+                Instruction instruction = instructions[index];
+
+                instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this);
+
+                offset += instruction.length(offset);
+            }
+        }
+
+
+        // Implementations for Object.
+
+        public String toString()
+        {
+            StringBuffer stringBuffer = new StringBuffer();
+
+            for (int index = 0; index < instructions.length; index++)
+            {
+                stringBuffer.append(instructions[index].toString()).append("; ");
+            }
+
+            return stringBuffer.toString();
+        }
+    }
 }
diff --git a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java
index 53b0a73..9962ea5 100644
--- a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java
+++ b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ComparableConstant.java b/src/proguard/classfile/editor/ComparableConstant.java
index 21a8f0b..bb81221 100644
--- a/src/proguard/classfile/editor/ComparableConstant.java
+++ b/src/proguard/classfile/editor/ComparableConstant.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ConstantAdder.java b/src/proguard/classfile/editor/ConstantAdder.java
index e2b620d..2b74f5f 100644
--- a/src/proguard/classfile/editor/ConstantAdder.java
+++ b/src/proguard/classfile/editor/ConstantAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/src/proguard/classfile/editor/ConstantPoolEditor.java
index 509963e..8663dee 100644
--- a/src/proguard/classfile/editor/ConstantPoolEditor.java
+++ b/src/proguard/classfile/editor/ConstantPoolEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/src/proguard/classfile/editor/ConstantPoolRemapper.java
index 33c4d9f..7430d3d 100644
--- a/src/proguard/classfile/editor/ConstantPoolRemapper.java
+++ b/src/proguard/classfile/editor/ConstantPoolRemapper.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/src/proguard/classfile/editor/ConstantPoolSorter.java
index 2828e7f..faae318 100644
--- a/src/proguard/classfile/editor/ConstantPoolSorter.java
+++ b/src/proguard/classfile/editor/ConstantPoolSorter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ElementValueAdder.java b/src/proguard/classfile/editor/ElementValueAdder.java
index dbf5aa2..8cbd11d 100644
--- a/src/proguard/classfile/editor/ElementValueAdder.java
+++ b/src/proguard/classfile/editor/ElementValueAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -85,7 +85,6 @@ implements   ElementValueVisitor
                              ArrayElementValue targetArrayElementValue,
                              boolean           replaceElementValues)
     {
-System.out.println("ElementValueAdder.ElementValueAdder "+targetClass.getName());
         this.targetClass                      = targetClass;
         this.targetAnnotationDefaultAttribute = null;
 
diff --git a/src/proguard/classfile/editor/ElementValuesEditor.java b/src/proguard/classfile/editor/ElementValuesEditor.java
index 5e06768..bfc4e9f 100644
--- a/src/proguard/classfile/editor/ElementValuesEditor.java
+++ b/src/proguard/classfile/editor/ElementValuesEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ExceptionAdder.java b/src/proguard/classfile/editor/ExceptionAdder.java
index 48decfe..1ccb1a6 100644
--- a/src/proguard/classfile/editor/ExceptionAdder.java
+++ b/src/proguard/classfile/editor/ExceptionAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ExceptionInfoAdder.java b/src/proguard/classfile/editor/ExceptionInfoAdder.java
index b1c55ae..e0cc9c5 100644
--- a/src/proguard/classfile/editor/ExceptionInfoAdder.java
+++ b/src/proguard/classfile/editor/ExceptionInfoAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java
index 08daa0b..4509a9a 100644
--- a/src/proguard/classfile/editor/ExceptionsAttributeEditor.java
+++ b/src/proguard/classfile/editor/ExceptionsAttributeEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/InstructionAdder.java b/src/proguard/classfile/editor/InstructionAdder.java
index ffd3d81..60fde6d 100644
--- a/src/proguard/classfile/editor/InstructionAdder.java
+++ b/src/proguard/classfile/editor/InstructionAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/InstructionWriter.java b/src/proguard/classfile/editor/InstructionWriter.java
index c7e3010..d842358 100644
--- a/src/proguard/classfile/editor/InstructionWriter.java
+++ b/src/proguard/classfile/editor/InstructionWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/InterfaceAdder.java b/src/proguard/classfile/editor/InterfaceAdder.java
index 881b034..e095af6 100644
--- a/src/proguard/classfile/editor/InterfaceAdder.java
+++ b/src/proguard/classfile/editor/InterfaceAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/InterfaceSorter.java b/src/proguard/classfile/editor/InterfaceSorter.java
index 30a86da..6521369 100644
--- a/src/proguard/classfile/editor/InterfaceSorter.java
+++ b/src/proguard/classfile/editor/InterfaceSorter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/InterfacesEditor.java b/src/proguard/classfile/editor/InterfacesEditor.java
index fb35ec9..d3170e1 100644
--- a/src/proguard/classfile/editor/InterfacesEditor.java
+++ b/src/proguard/classfile/editor/InterfacesEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/LineNumberInfoAdder.java b/src/proguard/classfile/editor/LineNumberInfoAdder.java
new file mode 100644
index 0000000..aa8c0c4
--- /dev/null
+++ b/src/proguard/classfile/editor/LineNumberInfoAdder.java
@@ -0,0 +1,59 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.visitor.LineNumberInfoVisitor;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+/**
+ * This LineNumberInfoVisitor adds all line numbers that it visits to the given
+ * target line number attribute.
+ */
+public class LineNumberInfoAdder
+implements   LineNumberInfoVisitor
+{
+    private final LineNumberTableAttributeEditor lineNumberTableAttributeEditor;
+
+
+    /**
+     * Creates a new LineNumberInfoAdder that will copy line numbers into the
+     * given target line number table.
+     */
+    public LineNumberInfoAdder(LineNumberTableAttribute targetLineNumberTableAttribute)
+    {
+        this.lineNumberTableAttributeEditor = new LineNumberTableAttributeEditor(targetLineNumberTableAttribute);
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
+    {
+        // Create a new line number.
+        LineNumberInfo newLineNumberInfo =
+            new LineNumberInfo(lineNumberInfo.u2startPC,
+                               lineNumberInfo.u2lineNumber);
+
+        // Add it to the target.
+        lineNumberTableAttributeEditor.addLineNumberInfo(newLineNumberInfo);
+    }
+}
diff --git a/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java
new file mode 100644
index 0000000..ab96b38
--- /dev/null
+++ b/src/proguard/classfile/editor/LineNumberTableAttributeEditor.java
@@ -0,0 +1,67 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.*;
+
+/**
+ * This class can add line numbers to a given line number table attribute.
+ * Line numbers to be added must have been filled out beforehand.
+ *
+ * @author Eric Lafortune
+ */
+public class LineNumberTableAttributeEditor
+{
+    private LineNumberTableAttribute targetLineNumberTableAttribute;
+
+
+    /**
+     * Creates a new LineNumberTableAttributeEditor that will edit line numbers
+     * in the given line number table attribute.
+     */
+    public LineNumberTableAttributeEditor(LineNumberTableAttribute targetLineNumberTableAttribute)
+    {
+        this.targetLineNumberTableAttribute = targetLineNumberTableAttribute;
+    }
+
+
+    /**
+     * Adds a given line number to the line number table attribute.
+     */
+    public void addLineNumberInfo(LineNumberInfo lineNumberInfo)
+    {
+        int              lineNumberTableLength = targetLineNumberTableAttribute.u2lineNumberTableLength;
+        LineNumberInfo[] lineNumberTable       = targetLineNumberTableAttribute.lineNumberTable;
+
+        // Make sure there is enough space for the new lineNumberInfo.
+        if (lineNumberTable.length <= lineNumberTableLength)
+        {
+            targetLineNumberTableAttribute.lineNumberTable = new LineNumberInfo[lineNumberTableLength+1];
+            System.arraycopy(lineNumberTable, 0,
+                             targetLineNumberTableAttribute.lineNumberTable, 0,
+                             lineNumberTableLength);
+            lineNumberTable = targetLineNumberTableAttribute.lineNumberTable;
+        }
+
+        // Add the lineNumberInfo.
+        lineNumberTable[targetLineNumberTableAttribute.u2lineNumberTableLength++] = lineNumberInfo;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/LocalVariableInfoAdder.java b/src/proguard/classfile/editor/LocalVariableInfoAdder.java
new file mode 100644
index 0000000..f285e4f
--- /dev/null
+++ b/src/proguard/classfile/editor/LocalVariableInfoAdder.java
@@ -0,0 +1,67 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.visitor.LocalVariableInfoVisitor;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+/**
+ * This LocalVariableInfoVisitor adds all line numbers that it visits to the given
+ * target line number attribute.
+ */
+public class LocalVariableInfoAdder
+implements   LocalVariableInfoVisitor
+{
+    private final ConstantAdder                     constantAdder;
+    private final LocalVariableTableAttributeEditor localVariableTableAttributeEditor;
+
+
+    /**
+     * Creates a new LocalVariableInfoAdder that will copy line numbers into the
+     * given target line number table.
+     */
+    public LocalVariableInfoAdder(ProgramClass                targetClass,
+                                  LocalVariableTableAttribute targetLocalVariableTableAttribute)
+    {
+        this.constantAdder                     = new ConstantAdder(targetClass);
+        this.localVariableTableAttributeEditor = new LocalVariableTableAttributeEditor(targetLocalVariableTableAttribute);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        // Create a new line number.
+        LocalVariableInfo newLocalVariableInfo =
+            new LocalVariableInfo(localVariableInfo.u2startPC,
+                                  localVariableInfo.u2length,
+                                  constantAdder.addConstant(clazz, localVariableInfo.u2nameIndex),
+                                  constantAdder.addConstant(clazz, localVariableInfo.u2descriptorIndex),
+                                  localVariableInfo.u2index);
+
+        newLocalVariableInfo.referencedClass = localVariableInfo.referencedClass;
+
+        // Add it to the target.
+        localVariableTableAttributeEditor.addLocalVariableInfo(newLocalVariableInfo);
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java
new file mode 100644
index 0000000..053b628
--- /dev/null
+++ b/src/proguard/classfile/editor/LocalVariableTableAttributeEditor.java
@@ -0,0 +1,67 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.*;
+
+/**
+ * This class can add local variables to a given local variable table attribute.
+ * Local variables to be added must have been filled out beforehand.
+ *
+ * @author Eric Lafortune
+ */
+public class LocalVariableTableAttributeEditor
+{
+    private LocalVariableTableAttribute targetLocalVariableTableAttribute;
+
+
+    /**
+     * Creates a new LocalVariableTableAttributeEditor that will edit line numbers
+     * in the given line number table attribute.
+     */
+    public LocalVariableTableAttributeEditor(LocalVariableTableAttribute targetLocalVariableTableAttribute)
+    {
+        this.targetLocalVariableTableAttribute = targetLocalVariableTableAttribute;
+    }
+
+
+    /**
+     * Adds a given line number to the line number table attribute.
+     */
+    public void addLocalVariableInfo(LocalVariableInfo localVariableInfo)
+    {
+        int                 localVariableTableLength = targetLocalVariableTableAttribute.u2localVariableTableLength;
+        LocalVariableInfo[] localVariableTable       = targetLocalVariableTableAttribute.localVariableTable;
+
+        // Make sure there is enough space for the new localVariableInfo.
+        if (localVariableTable.length <= localVariableTableLength)
+        {
+            targetLocalVariableTableAttribute.localVariableTable = new LocalVariableInfo[localVariableTableLength+1];
+            System.arraycopy(localVariableTable, 0,
+                             targetLocalVariableTableAttribute.localVariableTable, 0,
+                             localVariableTableLength);
+            localVariableTable = targetLocalVariableTableAttribute.localVariableTable;
+        }
+
+        // Add the localVariableInfo.
+        localVariableTable[targetLocalVariableTableAttribute.u2localVariableTableLength++] = localVariableInfo;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java
new file mode 100644
index 0000000..ca50f3f
--- /dev/null
+++ b/src/proguard/classfile/editor/LocalVariableTypeInfoAdder.java
@@ -0,0 +1,68 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.visitor.LocalVariableTypeInfoVisitor;
+import proguard.classfile.attribute.*;
+import proguard.classfile.*;
+
+/**
+ * This LocalVariableTypeInfoVisitor adds all line numbers that it visits to the given
+ * target line number attribute.
+ */
+public class LocalVariableTypeInfoAdder
+implements   LocalVariableTypeInfoVisitor
+{
+    private final ConstantAdder                     constantAdder;
+    private final LocalVariableTypeTableAttributeEditor localVariableTypeTableAttributeEditor;
+
+
+    /**
+     * Creates a new LocalVariableTypeInfoAdder that will copy line numbers into the
+     * given target line number table.
+     */
+    public LocalVariableTypeInfoAdder(ProgramClass                    targetClass,
+                                      LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute)
+    {
+        this.constantAdder                         = new ConstantAdder(targetClass);
+        this.localVariableTypeTableAttributeEditor = new LocalVariableTypeTableAttributeEditor(targetLocalVariableTypeTableAttribute);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Create a new line number.
+        LocalVariableTypeInfo newLocalVariableTypeInfo =
+            new LocalVariableTypeInfo(localVariableTypeInfo.u2startPC,
+                                      localVariableTypeInfo.u2length,
+                                      constantAdder.addConstant(clazz, localVariableTypeInfo.u2nameIndex),
+                                      constantAdder.addConstant(clazz, localVariableTypeInfo.u2signatureIndex),
+                                      localVariableTypeInfo.u2index);
+
+        // TODO: Clone array.
+        newLocalVariableTypeInfo.referencedClasses = localVariableTypeInfo.referencedClasses;
+
+        // Add it to the target.
+        localVariableTypeTableAttributeEditor.addLocalVariableTypeInfo(newLocalVariableTypeInfo);
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java
new file mode 100644
index 0000000..fe5a64d
--- /dev/null
+++ b/src/proguard/classfile/editor/LocalVariableTypeTableAttributeEditor.java
@@ -0,0 +1,68 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.*;
+
+/**
+ * This class can add local variables to a given local variable type table
+ * attribute.
+ * Local variable types to be added must have been filled out beforehand.
+ *
+ * @author Eric Lafortune
+ */
+public class LocalVariableTypeTableAttributeEditor
+{
+    private LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute;
+
+
+    /**
+     * Creates a new LocalVariableTypeTableAttributeEditor that will edit line numbers
+     * in the given line number table attribute.
+     */
+    public LocalVariableTypeTableAttributeEditor(LocalVariableTypeTableAttribute targetLocalVariableTypeTableAttribute)
+    {
+        this.targetLocalVariableTypeTableAttribute = targetLocalVariableTypeTableAttribute;
+    }
+
+
+    /**
+     * Adds a given line number to the line number table attribute.
+     */
+    public void addLocalVariableTypeInfo(LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        int                     localVariableTypeTableLength = targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength;
+        LocalVariableTypeInfo[] localVariableTypeTable       = targetLocalVariableTypeTableAttribute.localVariableTypeTable;
+
+        // Make sure there is enough space for the new localVariableTypeInfo.
+        if (localVariableTypeTable.length <= localVariableTypeTableLength)
+        {
+            targetLocalVariableTypeTableAttribute.localVariableTypeTable = new LocalVariableTypeInfo[localVariableTypeTableLength+1];
+            System.arraycopy(localVariableTypeTable, 0,
+                             targetLocalVariableTypeTableAttribute.localVariableTypeTable, 0,
+                             localVariableTypeTableLength);
+            localVariableTypeTable = targetLocalVariableTypeTableAttribute.localVariableTypeTable;
+        }
+
+        // Add the localVariableTypeInfo.
+        localVariableTypeTable[targetLocalVariableTypeTableAttribute.u2localVariableTypeTableLength++] = localVariableTypeInfo;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/MemberAdder.java b/src/proguard/classfile/editor/MemberAdder.java
index c2083af..5f939bb 100644
--- a/src/proguard/classfile/editor/MemberAdder.java
+++ b/src/proguard/classfile/editor/MemberAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -139,6 +139,7 @@ implements   MemberVisitor
                                  EMPTY_ATTRIBUTES,
                              programField.referencedClass);
 
+        // Link to its visitor info.
         newProgramField.setVisitorInfo(programField);
 
         // Copy its attributes.
@@ -228,6 +229,7 @@ implements   MemberVisitor
                                   (Clazz[])programMethod.referencedClasses.clone() :
                                   null);
 
+        // Link to its visitor info.
         newProgramMethod.setVisitorInfo(programMethod);
 
         // Copy its attributes.
diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/src/proguard/classfile/editor/MemberReferenceFixer.java
index d78a08c..4bd8af5 100644
--- a/src/proguard/classfile/editor/MemberReferenceFixer.java
+++ b/src/proguard/classfile/editor/MemberReferenceFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -31,8 +31,8 @@ import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
 
 /**
- * This ClassVisitor fixes constant pool field and method references to
- * fields and methods whose names or descriptors have changed.
+ * This ClassVisitor fixes constant pool field and method references to fields
+ * and methods whose names or descriptors have changed.
  *
  * @author Eric Lafortune
  */
diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java
index d60bb36..ef76012 100644
--- a/src/proguard/classfile/editor/MethodInvocationFixer.java
+++ b/src/proguard/classfile/editor/MethodInvocationFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/NamedAttributeDeleter.java b/src/proguard/classfile/editor/NamedAttributeDeleter.java
index a8019f2..0c4d339 100644
--- a/src/proguard/classfile/editor/NamedAttributeDeleter.java
+++ b/src/proguard/classfile/editor/NamedAttributeDeleter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java
new file mode 100644
index 0000000..4cad6b8
--- /dev/null
+++ b/src/proguard/classfile/editor/ParameterAnnotationsAttributeEditor.java
@@ -0,0 +1,71 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.annotation.*;
+
+/**
+ * This class can add annotations to a given parameter annotations attribute.
+ * Annotations to be added must have been filled out beforehand.
+ *
+ * @author Eric Lafortune
+ */
+public class ParameterAnnotationsAttributeEditor
+{
+    private ParameterAnnotationsAttribute targetParameterAnnotationsAttribute;
+
+
+    /**
+     * Creates a new ParameterAnnotationsAttributeEditor that will edit
+     * annotations in the given parameter annotations attribute.
+     */
+    public ParameterAnnotationsAttributeEditor(ParameterAnnotationsAttribute targetParameterAnnotationsAttribute)
+    {
+        this.targetParameterAnnotationsAttribute = targetParameterAnnotationsAttribute;
+    }
+
+
+    /**
+     * Adds a given annotation to the annotations attribute.
+     */
+    public void addAnnotation(int parameterIndex, Annotation annotation)
+    {
+        int          annotationsCount = targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex];
+        Annotation[] annotations      = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex];
+
+        // Make sure there is enough space for the new annotation.
+        if (annotations == null ||
+            annotations.length <= annotationsCount)
+        {
+            targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex] = new Annotation[annotationsCount+1];
+            if (annotations != null)
+            {
+                System.arraycopy(annotations, 0,
+                                 targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex], 0,
+                                 annotationsCount);
+            }
+            annotations = targetParameterAnnotationsAttribute.parameterAnnotations[parameterIndex];
+        }
+
+        // Add the annotation.
+        annotations[targetParameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]++] = annotation;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/src/proguard/classfile/editor/StackSizeUpdater.java
index 358e720..94e0519 100644
--- a/src/proguard/classfile/editor/StackSizeUpdater.java
+++ b/src/proguard/classfile/editor/StackSizeUpdater.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/SubclassAdder.java b/src/proguard/classfile/editor/SubclassAdder.java
index 9e204ff..6b9fd64 100644
--- a/src/proguard/classfile/editor/SubclassAdder.java
+++ b/src/proguard/classfile/editor/SubclassAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/SubclassToAdder.java b/src/proguard/classfile/editor/SubclassToAdder.java
index 97d96b3..deb242f 100644
--- a/src/proguard/classfile/editor/SubclassToAdder.java
+++ b/src/proguard/classfile/editor/SubclassToAdder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/VariableCleaner.java b/src/proguard/classfile/editor/VariableCleaner.java
new file mode 100644
index 0000000..1e93c15
--- /dev/null
+++ b/src/proguard/classfile/editor/VariableCleaner.java
@@ -0,0 +1,135 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.optimize.info.VariableUsageMarker;
+
+/**
+ * This AttributeVisitor cleans up unused variables in all attributes that it
+ * visits.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableCleaner
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private final VariableUsageMarker variableUsageMarker = new VariableUsageMarker();
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Figure out the local variables that are used by the code.
+        variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Clean up the variables of the attributes.
+        codeAttribute.attributesAccept(clazz, method, this);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Clean up local variables that aren't used.
+        localVariableTableAttribute.u2localVariableTableLength =
+            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
+                                      localVariableTableAttribute.u2localVariableTableLength,
+                                      codeAttribute.u2maxLocals);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Clean up local variables that aren't used.
+        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
+            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
+                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength,
+                                          codeAttribute.u2maxLocals);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the given list of local variables, without the ones that aren't
+     * used
+     */
+    private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
+                                          int                 localVariableInfoCount,
+                                          int                 maxLocals)
+    {
+        // Overwrite all empty local variable entries.
+        int newIndex = 0;
+        for (int index = 0; index < localVariableInfoCount && index < maxLocals; index++)
+        {
+            if (variableUsageMarker.isVariableUsed(index))
+            {
+                localVariableInfos[newIndex++] = localVariableInfos[index];
+            }
+        }
+
+        // Clean up any remaining array elements.
+        for (int index = newIndex; index < localVariableInfoCount; index++)
+        {
+            localVariableInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of local variable types, without the ones that
+     * aren't used
+     */
+    private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
+                                              int                     localVariableTypeInfoCount,
+                                              int                     maxLocals)
+    {
+        // Overwrite all empty local variable type entries.
+        int newIndex = 0;
+        for (int index = 0; index < localVariableTypeInfoCount && index < maxLocals; index++)
+        {
+            if (variableUsageMarker.isVariableUsed(index))
+            {
+                localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index];
+            }
+        }
+
+        // Clean up any remaining array elements.
+        for (int index = newIndex; index < localVariableTypeInfoCount; index++)
+        {
+            localVariableTypeInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java
index 76556ed..a583b49 100644
--- a/src/proguard/classfile/editor/VariableEditor.java
+++ b/src/proguard/classfile/editor/VariableEditor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/VariableRemapper.java b/src/proguard/classfile/editor/VariableRemapper.java
index 4728525..590cd4e 100644
--- a/src/proguard/classfile/editor/VariableRemapper.java
+++ b/src/proguard/classfile/editor/VariableRemapper.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/src/proguard/classfile/editor/VariableSizeUpdater.java
index fe0c62a..18958c5 100644
--- a/src/proguard/classfile/editor/VariableSizeUpdater.java
+++ b/src/proguard/classfile/editor/VariableSizeUpdater.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/src/proguard/classfile/instruction/BranchInstruction.java
index afac36e..2baa917 100644
--- a/src/proguard/classfile/instruction/BranchInstruction.java
+++ b/src/proguard/classfile/instruction/BranchInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/ConstantInstruction.java b/src/proguard/classfile/instruction/ConstantInstruction.java
index 1950b02..6c2d1a3 100644
--- a/src/proguard/classfile/instruction/ConstantInstruction.java
+++ b/src/proguard/classfile/instruction/ConstantInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/Instruction.java b/src/proguard/classfile/instruction/Instruction.java
index bdd5c7f..8437495 100644
--- a/src/proguard/classfile/instruction/Instruction.java
+++ b/src/proguard/classfile/instruction/Instruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -690,7 +690,7 @@ public abstract class Instruction
     /**
      * Writes the Instruction at the given offset in the given code array.
      */
-    public final void write(byte[] code, int offset)
+    public void write(byte[] code, int offset)
     {
         // Write the wide opcode, if necessary.
         if (isWide())
diff --git a/src/proguard/classfile/instruction/InstructionConstants.java b/src/proguard/classfile/instruction/InstructionConstants.java
index 2f8e20d..78730b3 100644
--- a/src/proguard/classfile/instruction/InstructionConstants.java
+++ b/src/proguard/classfile/instruction/InstructionConstants.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/InstructionFactory.java b/src/proguard/classfile/instruction/InstructionFactory.java
index bf6f8f9..f898471 100644
--- a/src/proguard/classfile/instruction/InstructionFactory.java
+++ b/src/proguard/classfile/instruction/InstructionFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/InstructionUtil.java b/src/proguard/classfile/instruction/InstructionUtil.java
index f5e8d2b..a3a328a 100644
--- a/src/proguard/classfile/instruction/InstructionUtil.java
+++ b/src/proguard/classfile/instruction/InstructionUtil.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
index a188e0b..178cce5 100644
--- a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
+++ b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/src/proguard/classfile/instruction/SimpleInstruction.java
index c351baa..84e6344 100644
--- a/src/proguard/classfile/instruction/SimpleInstruction.java
+++ b/src/proguard/classfile/instruction/SimpleInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/SwitchInstruction.java b/src/proguard/classfile/instruction/SwitchInstruction.java
index 96fab5f..b98c2fb 100644
--- a/src/proguard/classfile/instruction/SwitchInstruction.java
+++ b/src/proguard/classfile/instruction/SwitchInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/TableSwitchInstruction.java b/src/proguard/classfile/instruction/TableSwitchInstruction.java
index ec94dde..ee81af5 100644
--- a/src/proguard/classfile/instruction/TableSwitchInstruction.java
+++ b/src/proguard/classfile/instruction/TableSwitchInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/src/proguard/classfile/instruction/VariableInstruction.java
index 220dc47..309f802 100644
--- a/src/proguard/classfile/instruction/VariableInstruction.java
+++ b/src/proguard/classfile/instruction/VariableInstruction.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -137,16 +137,29 @@ public class VariableInstruction extends Instruction
 
 
     /**
+     * Returns whether this instruction stores the value of a variable.
+     * The value is false for the ret instruction, but true for the iinc
+     * instruction.
+     */
+    public boolean isStore()
+    {
+        // A store instruction can be recognized as follows. Note that this
+        // excludes the ret instruction, which has a negative opcode.
+        return opcode >= InstructionConstants.OP_ISTORE ||
+               opcode == InstructionConstants.OP_IINC;
+    }
+
+
+    /**
      * Returns whether this instruction loads the value of a variable.
-     * The value is true for the ret instruction, but false for the iinc
+     * The value is true for the ret instruction and for the iinc
      * instruction.
      */
     public boolean isLoad()
     {
         // A load instruction can be recognized as follows. Note that this
         // includes the ret instruction, which has a negative opcode.
-        return opcode <  InstructionConstants.OP_ISTORE &&
-               opcode != InstructionConstants.OP_IINC;
+        return opcode < InstructionConstants.OP_ISTORE;
     }
 
 
diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
index 32c507e..71b2cde 100644
--- a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
+++ b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/visitor/InstructionCounter.java b/src/proguard/classfile/instruction/visitor/InstructionCounter.java
index 5658953..1d10980 100644
--- a/src/proguard/classfile/instruction/visitor/InstructionCounter.java
+++ b/src/proguard/classfile/instruction/visitor/InstructionCounter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java
index 1a6b586..11af131 100644
--- a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java
+++ b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
index 2c373fb..aada455 100644
--- a/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
+++ b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/io/LibraryClassReader.java b/src/proguard/classfile/io/LibraryClassReader.java
index 8f17031..f14471c 100644
--- a/src/proguard/classfile/io/LibraryClassReader.java
+++ b/src/proguard/classfile/io/LibraryClassReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/io/ProgramClassReader.java b/src/proguard/classfile/io/ProgramClassReader.java
index b536e9a..476a346 100644
--- a/src/proguard/classfile/io/ProgramClassReader.java
+++ b/src/proguard/classfile/io/ProgramClassReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/io/ProgramClassWriter.java b/src/proguard/classfile/io/ProgramClassWriter.java
index 041de2f..e56f08a 100644
--- a/src/proguard/classfile/io/ProgramClassWriter.java
+++ b/src/proguard/classfile/io/ProgramClassWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -278,9 +278,7 @@ implements   ClassVisitor,
         public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
         {
             // Write the unknown information.
-            byte[] info = new byte[unknownAttribute.u4attributeLength];
-            dataOutput.write(info);
-            unknownAttribute.info = info;
+            dataOutput.write(unknownAttribute.info);
         }
 
 
diff --git a/src/proguard/classfile/io/RuntimeDataInput.java b/src/proguard/classfile/io/RuntimeDataInput.java
index b123b9f..104b7c9 100644
--- a/src/proguard/classfile/io/RuntimeDataInput.java
+++ b/src/proguard/classfile/io/RuntimeDataInput.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/io/RuntimeDataOutput.java b/src/proguard/classfile/io/RuntimeDataOutput.java
index 89dc71d..ffde3af 100644
--- a/src/proguard/classfile/io/RuntimeDataOutput.java
+++ b/src/proguard/classfile/io/RuntimeDataOutput.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java
index 0f2ab64..3ad6961 100644
--- a/src/proguard/classfile/util/AccessUtil.java
+++ b/src/proguard/classfile/util/AccessUtil.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java
index ff88b76..b1f2c27 100644
--- a/src/proguard/classfile/util/ClassReferenceInitializer.java
+++ b/src/proguard/classfile/util/ClassReferenceInitializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -63,7 +63,8 @@ implements   ClassVisitor,
 {
     private final ClassPool      programClassPool;
     private final ClassPool      libraryClassPool;
-    private final WarningPrinter missingWarningPrinter;
+    private final WarningPrinter missingClassWarningPrinter;
+    private final WarningPrinter missingMemberWarningPrinter;
     private final WarningPrinter dependencyWarningPrinter;
 
     private final MemberFinder memberFinder = new MemberFinder();
@@ -72,17 +73,19 @@ implements   ClassVisitor,
     /**
      * Creates a new ClassReferenceInitializer that initializes the references
      * of all visited class files, optionally printing warnings if some classes
-     * can't be found or if they are in the program class pool.
+     * or class members can't be found or if they are in the program class pool.
      */
     public ClassReferenceInitializer(ClassPool      programClassPool,
                                      ClassPool      libraryClassPool,
-                                     WarningPrinter missingWarningPrinter,
+                                     WarningPrinter missingClassWarningPrinter,
+                                     WarningPrinter missingMemberWarningPrinter,
                                      WarningPrinter dependencyWarningPrinter)
     {
-        this.programClassPool         = programClassPool;
-        this.libraryClassPool         = libraryClassPool;
-        this.missingWarningPrinter    = missingWarningPrinter;
-        this.dependencyWarningPrinter = dependencyWarningPrinter;
+        this.programClassPool            = programClassPool;
+        this.libraryClassPool            = libraryClassPool;
+        this.missingClassWarningPrinter  = missingClassWarningPrinter;
+        this.missingMemberWarningPrinter = missingMemberWarningPrinter;
+        this.dependencyWarningPrinter    = dependencyWarningPrinter;
     }
 
 
@@ -189,18 +192,19 @@ implements   ClassVisitor,
                                                                    isFieldRef);
             refConstant.referencedClass  = memberFinder.correspondingClass();
 
-            if (refConstant.referencedMember == null &&
-                missingWarningPrinter != null)
+            if (refConstant.referencedMember == null)
             {
                 // We've haven't found the class member anywhere in the hierarchy.
-                missingWarningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(clazz.getName()) +
-                                     ": can't find referenced " +
-                                     (isFieldRef ?
-                                         "field '"  + ClassUtil.externalFullFieldDescription(0, name, type) :
-                                         "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
-                                     "' in class " +
-                                     ClassUtil.externalClassName(className));
+                missingMemberWarningPrinter.print(clazz.getName(),
+                                                  className,
+                                                  "Warning: " +
+                                                  ClassUtil.externalClassName(clazz.getName()) +
+                                                  ": can't find referenced " +
+                                                  (isFieldRef ?
+                                                      "field '"  + ClassUtil.externalFullFieldDescription(0, name, type) :
+                                                      "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
+                                                  "' in class " +
+                                                  ClassUtil.externalClassName(className));
             }
         }
     }
@@ -236,14 +240,12 @@ implements   ClassVisitor,
         if (referencedClass == null)
         {
             // We couldn't find the enclosing class.
-            if (missingWarningPrinter != null)
-            {
-                missingWarningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(className) +
-                                     ": can't find enclosing class " +
-                                     ClassUtil.externalClassName(enclosingClassName));
-            }
-
+            missingClassWarningPrinter.print(className,
+                                             enclosingClassName,
+                                             "Warning: " +
+                                             ClassUtil.externalClassName(className) +
+                                             ": can't find enclosing class " +
+                                             ClassUtil.externalClassName(enclosingClassName));
             return;
         }
 
@@ -262,16 +264,14 @@ implements   ClassVisitor,
         if (referencedMethod == null)
         {
             // We couldn't find the enclosing method.
-            if (missingWarningPrinter != null)
-            {
-                missingWarningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(className) +
-                                     ": can't find enclosing method '" +
-                                     ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) +
-                                     "' in class " +
-                                     ClassUtil.externalClassName(enclosingClassName));
-            }
-
+            missingMemberWarningPrinter.print(className,
+                                              enclosingClassName,
+                                              "Warning: " +
+                                              ClassUtil.externalClassName(className) +
+                                              ": can't find enclosing method '" +
+                                              ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) +
+                                              "' in class " +
+                                              ClassUtil.externalClassName(enclosingClassName));
             return;
         }
 
@@ -517,20 +517,24 @@ implements   ClassVisitor,
             clazz = libraryClassPool.getClass(name);
 
             if (clazz == null &&
-                missingWarningPrinter != null)
+                missingClassWarningPrinter != null)
             {
                 // We didn't find the superclass or interface. Print a warning.
-                missingWarningPrinter.print("Warning: " +
-                                            ClassUtil.externalClassName(referencingClassName) +
-                                            ": can't find referenced class " +
-                                            ClassUtil.externalClassName(name));
+                missingClassWarningPrinter.print(referencingClassName,
+                                                 name,
+                                                 "Warning: " +
+                                                 ClassUtil.externalClassName(referencingClassName) +
+                                                 ": can't find referenced class " +
+                                                 ClassUtil.externalClassName(name));
             }
         }
         else if (dependencyWarningPrinter != null)
         {
             // The superclass or interface was found in the program class pool.
             // Print a warning.
-            dependencyWarningPrinter.print("Warning: library class " +
+            dependencyWarningPrinter.print(referencingClassName,
+                                           name,
+                                           "Warning: library class " +
                                            ClassUtil.externalClassName(referencingClassName) +
                                            " depends on program class " +
                                            ClassUtil.externalClassName(name));
diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java
index c65b512..30fd526 100644
--- a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java
+++ b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java
index 2896a63..af2a209 100644
--- a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java
+++ b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -143,7 +143,9 @@ implements   ClassVisitor,
                 missingWarningPrinter != null)
             {
                 // We didn't find the superclass or interface. Print a warning.
-                missingWarningPrinter.print("Warning: " +
+                missingWarningPrinter.print(referencingClassName,
+                                            name,
+                                            "Warning: " +
                                             ClassUtil.externalClassName(referencingClassName) +
                                             ": can't find superclass or interface " +
                                             ClassUtil.externalClassName(name));
@@ -153,7 +155,9 @@ implements   ClassVisitor,
         {
             // The superclass or interface was found in the program class pool.
             // Print a warning.
-            dependencyWarningPrinter.print("Warning: library class " +
+            dependencyWarningPrinter.print(referencingClassName,
+                                           name,
+                                           "Warning: library class " +
                                            ClassUtil.externalClassName(referencingClassName) +
                                            " extends or implements program class " +
                                            ClassUtil.externalClassName(name));
diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java
index dc46ef3..5f25f01 100644
--- a/src/proguard/classfile/util/ClassUtil.java
+++ b/src/proguard/classfile/util/ClassUtil.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java
index 4523722..0bee2d5 100644
--- a/src/proguard/classfile/util/DescriptorClassEnumeration.java
+++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -22,6 +22,8 @@ package proguard.classfile.util;
 
 import proguard.classfile.ClassConstants;
 
+import java.util.Stack;
+
 /**
  * A <code>DescriptorClassEnumeration</code> provides an enumeration of all
  * classes mentioned in a given descriptor or signature.
@@ -36,6 +38,7 @@ public class DescriptorClassEnumeration
     private int     nestingLevel;
     private boolean isInnerClassName;
     private String  accumulatedClassName;
+    private Stack   accumulatedClassNames;
 
 
     /**
@@ -95,11 +98,26 @@ public class DescriptorClassEnumeration
                 case ClassConstants.INTERNAL_TYPE_GENERIC_START:
                 {
                     nestingLevel++;
+
+                    // Make sure we have a stack.
+                    if (accumulatedClassNames == null)
+                    {
+                        accumulatedClassNames = new Stack();
+                    }
+
+                    // Remember the accumulated class name.
+                    accumulatedClassNames.push(accumulatedClassName);
+
                     break;
                 }
                 case ClassConstants.INTERNAL_TYPE_GENERIC_END:
                 {
                     nestingLevel--;
+
+                    // Return to the accumulated class name outside the
+                    // generic block.
+                    accumulatedClassName = (String)accumulatedClassNames.pop();
+
                     continue loop;
                 }
                 case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND:
diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
index d849121..09ffdd0 100644
--- a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
+++ b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -313,11 +313,12 @@ implements   InstructionVisitor,
     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
         // Print out a note about the class cast.
-        if (notePrinter != null &&
-            (noteExceptionMatcher == null ||
-             !noteExceptionMatcher.matches(classConstant.getName(clazz))))
+        if (noteExceptionMatcher == null ||
+            !noteExceptionMatcher.matches(classConstant.getName(clazz)))
         {
-            notePrinter.print("Note: " +
+            notePrinter.print(clazz.getName(),
+                              classConstant.getName(clazz),
+                              "Note: " +
                               ClassUtil.externalClassName(clazz.getName()) +
                               " calls '(" +
                               ClassUtil.externalClassName(classConstant.getName(clazz)) +
@@ -452,7 +453,9 @@ implements   InstructionVisitor,
                 missingNotePrinter != null)
             {
                 // We didn't find the superclass or interface. Print a note.
-                missingNotePrinter.print("Note: " +
+                missingNotePrinter.print(referencingClassName,
+                                         name,
+                                         "Note: " +
                                          ClassUtil.externalClassName(referencingClassName) +
                                          ": can't find dynamically referenced class " +
                                          ClassUtil.externalClassName(name));
@@ -462,7 +465,9 @@ implements   InstructionVisitor,
         {
             // The superclass or interface was found in the program class pool.
             // Print a warning.
-            dependencyWarningPrinter.print("Warning: library class " +
+            dependencyWarningPrinter.print(referencingClassName,
+                                           name,
+                                           "Warning: library class " +
                                            ClassUtil.externalClassName(referencingClassName) +
                                            " depends dynamically on program class " +
                                            ClassUtil.externalClassName(name));
diff --git a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java
index b6cc51d..1f57708 100644
--- a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java
+++ b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -457,7 +457,8 @@ implements   InstructionVisitor,
                                             boolean                    isDeclared)
     {
         // Print out a note about the dynamic invocation.
-        if (notePrinter != null)
+        if (notePrinter != null &&
+            notePrinter.accepts(clazz.getName()))
         {
             // Is the class member name in the list of exceptions?
             StringMatcher noteExceptionMatcher = isField ?
@@ -495,7 +496,8 @@ implements   InstructionVisitor,
                 }
 
                 // Print out the actual note.
-                notePrinter.print("Note: " +
+                notePrinter.print(clazz.getName(),
+                                  "Note: " +
                                   ClassUtil.externalClassName(clazz.getName()) +
                                   " accesses a " +
                                   (isDeclared ? "declared " : "") +
@@ -551,40 +553,52 @@ implements   InstructionVisitor,
 
     public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        System.out.println("      Maybe this is program field '" +
-                           ClassUtil.externalFullClassDescription(0, programClass.getName()) +
-                           " { " +
-                           ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) +
-                           "; }'");
+        if (notePrinter.accepts(programClass.getName()))
+        {
+            System.out.println("      Maybe this is program field '" +
+                               ClassUtil.externalFullClassDescription(0, programClass.getName()) +
+                               " { " +
+                               ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) +
+                               "; }'");
+        }
     }
 
 
     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        System.out.println("      Maybe this is program method '" +
-                           ClassUtil.externalFullClassDescription(0, programClass.getName()) +
-                           " { " +
-                           ClassUtil.externalFullMethodDescription(null, 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) +
-                           "; }'");
+        if (notePrinter.accepts(programClass.getName()))
+        {
+            System.out.println("      Maybe this is program method '" +
+                               ClassUtil.externalFullClassDescription(0, programClass.getName()) +
+                               " { " +
+                               ClassUtil.externalFullMethodDescription(null, 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) +
+                               "; }'");
+        }
     }
 
 
     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
-        System.out.println("      Maybe this is library field '" +
-                           ClassUtil.externalFullClassDescription(0, libraryClass.getName()) +
-                           " { " +
-                           ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) +
-                           "; }'");
+        if (notePrinter.accepts(libraryClass.getName()))
+        {
+            System.out.println("      Maybe this is library field '" +
+                               ClassUtil.externalFullClassDescription(0, libraryClass.getName()) +
+                               " { " +
+                               ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) +
+                               "; }'");
+        }
     }
 
 
     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        System.out.println("      Maybe this is library method '" +
-                           ClassUtil.externalFullClassDescription(0, libraryClass.getName()) +
-                           " { " +
-                           ClassUtil.externalFullMethodDescription(null, 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) +
-                           "; }'");
+        if (notePrinter.accepts(libraryClass.getName()))
+        {
+            System.out.println("      Maybe this is library method '" +
+                               ClassUtil.externalFullClassDescription(0, libraryClass.getName()) +
+                               " { " +
+                               ClassUtil.externalFullMethodDescription(null, 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) +
+                               "; }'");
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java
index bf43501..6371888 100644
--- a/src/proguard/classfile/util/ExternalTypeEnumeration.java
+++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/src/proguard/classfile/util/InstructionSequenceMatcher.java
index 22b88c0..8a689d5 100644
--- a/src/proguard/classfile/util/InstructionSequenceMatcher.java
+++ b/src/proguard/classfile/util/InstructionSequenceMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -60,12 +60,13 @@ implements   InstructionVisitor,
     private final Constant[]    patternConstants;
     private final Instruction[] patternInstructions;
 
-    private boolean matching;
-    private int   patternInstructionIndex;
+    private boolean     matching;
+    private boolean     matchingAnyWildCards;
+    private int         patternInstructionIndex;
     private final int[] matchedInstructionOffsets;
-    private int   matchedArgumentFlags;
+    private int         matchedArgumentFlags;
     private final int[] matchedArguments = new int[7];
-    private int   matchedConstantFlags;
+    private long        matchedConstantFlags;
     private final int[] matchedConstantIndices;
 
     // Fields acting as a parameter and a return value for visitor methods.
@@ -97,7 +98,7 @@ implements   InstructionVisitor,
     {
         patternInstructionIndex = 0;
         matchedArgumentFlags    = 0;
-        matchedConstantFlags    = 0;
+        matchedConstantFlags    = 0L;
     }
 
 
@@ -107,6 +108,12 @@ implements   InstructionVisitor,
     }
 
 
+    public boolean isMatchingAnyWildcards()
+    {
+        return matchingAnyWildCards;
+    }
+
+
     public int instructionCount()
     {
         return patternInstructions.length;
@@ -487,7 +494,7 @@ implements   InstructionVisitor,
             // Check the constant index.
             return matchingArguments(constantIndex1, constantIndex2);
         }
-        else if ((matchedConstantFlags & (1 << constantIndex2)) == 0)
+        else if ((matchedConstantFlags & (1L << constantIndex2)) == 0)
         {
             // Check the actual constant.
             matchingConstant = false;
@@ -501,7 +508,7 @@ implements   InstructionVisitor,
                 {
                     // Store the constant index.
                     matchedConstantIndices[constantIndex2] = constantIndex1;
-                    matchedConstantFlags |= 1 << constantIndex2;
+                    matchedConstantFlags |= 1L << constantIndex2;
                 }
             }
 
@@ -588,6 +595,9 @@ implements   InstructionVisitor,
             // Did we match all instructions in the sequence?
             matching = patternInstructionIndex == patternInstructions.length;
 
+            // Did we match any wildcards along the way?
+            matchingAnyWildCards = matchedArgumentFlags != 0;
+
             if (matching)
             {
                 if (DEBUG)
diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java
index 80bb608..76f7e84 100644
--- a/src/proguard/classfile/util/InternalTypeEnumeration.java
+++ b/src/proguard/classfile/util/InternalTypeEnumeration.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java
index c066e10..0fdeec0 100644
--- a/src/proguard/classfile/util/MemberFinder.java
+++ b/src/proguard/classfile/util/MemberFinder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/MethodLinker.java b/src/proguard/classfile/util/MethodLinker.java
index 7857cc3..5f2ea6f 100644
--- a/src/proguard/classfile/util/MethodLinker.java
+++ b/src/proguard/classfile/util/MethodLinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/SimplifiedVisitor.java b/src/proguard/classfile/util/SimplifiedVisitor.java
index 126562f..87b7fe4 100644
--- a/src/proguard/classfile/util/SimplifiedVisitor.java
+++ b/src/proguard/classfile/util/SimplifiedVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/StringReferenceInitializer.java b/src/proguard/classfile/util/StringReferenceInitializer.java
new file mode 100644
index 0000000..3884a04
--- /dev/null
+++ b/src/proguard/classfile/util/StringReferenceInitializer.java
@@ -0,0 +1,89 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This ConstantVisitor initializes any class references of all string constants
+ * it visits. More specifically, it fills out the references of string constant
+ * pool entries that happen to refer to a class in the program class pool or in
+ * the library class pool.
+ *
+ * @author Eric Lafortune
+ */
+public class StringReferenceInitializer
+extends      SimplifiedVisitor
+implements   ConstantVisitor
+{
+    private final ClassPool programClassPool;
+    private final ClassPool libraryClassPool;
+
+
+    /**
+     * Creates a new StringReferenceInitializer.
+     */
+    public StringReferenceInitializer(ClassPool programClassPool,
+                                      ClassPool libraryClassPool)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        if (stringConstant.referencedClass == null)
+        {
+            // See if we can find the referenced class.
+            stringConstant.referencedClass =
+                findClass(ClassUtil.internalClassName(stringConstant.getString(clazz)));
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the class with the given name, either for the program class pool
+     * or from the library class pool, or <code>null</code> if it can't be found.
+     */
+    private Clazz findClass(String name)
+    {
+        // First look for the class in the program class pool.
+        Clazz clazz = programClassPool.getClass(name);
+
+        // Otherwise look for the class in the library class pool.
+        if (clazz == null)
+        {
+            clazz = libraryClassPool.getClass(name);
+        }
+
+        return clazz;
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/util/StringSharer.java b/src/proguard/classfile/util/StringSharer.java
index 3d9005b..56de7c5 100644
--- a/src/proguard/classfile/util/StringSharer.java
+++ b/src/proguard/classfile/util/StringSharer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/util/WarningPrinter.java b/src/proguard/classfile/util/WarningPrinter.java
index 88de32f..87d8978 100644
--- a/src/proguard/classfile/util/WarningPrinter.java
+++ b/src/proguard/classfile/util/WarningPrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,7 +20,10 @@
  */
 package proguard.classfile.util;
 
+import proguard.util.*;
+
 import java.io.PrintStream;
+import java.util.List;
 
 /**
  * This class prints out and counts warnings.
@@ -29,8 +32,9 @@ import java.io.PrintStream;
  */
 public class WarningPrinter
 {
-    private final PrintStream printStream;
-    private int         warningCount;
+    private final PrintStream   printStream;
+    private final StringMatcher classFilter;
+    private int                 warningCount;
 
 
     /**
@@ -48,13 +52,73 @@ public class WarningPrinter
     public WarningPrinter(PrintStream printStream)
     {
         this.printStream = printStream;
+        this.classFilter = null;
+    }
+
+
+    /**
+     * Creates a new WarningPrinter that prints to the given print stream,
+     * except if the names of any involved classes matches the given filter.
+     */
+    public WarningPrinter(PrintStream printStream, List classFilter)
+    {
+        this.printStream = printStream;
+        this.classFilter = classFilter == null ? null :
+            new ListParser(new ClassNameParser()).parse(classFilter);
+    }
+
+
+    /**
+     * Prints out the given warning and increments the warning count, if
+     * the given class name passes the class name filter.
+     */
+    public void print(String className, String warning)
+    {
+        if (accepts(className))
+        {
+            print(warning);
+        }
+    }
+
+
+    /**
+     * Returns whether the given class name passes the class name filter.
+     */
+    public boolean accepts(String className)
+    {
+        return classFilter == null ||
+            !classFilter.matches(className);
+    }
+
+
+    /**
+     * Prints out the given warning and increments the warning count, if
+     * the given class names pass the class name filter.
+     */
+    public void print(String className1, String className2, String warning)
+    {
+        if (accepts(className1, className2))
+        {
+            print(warning);
+        }
+    }
+
+
+    /**
+     * Returns whether the given class names pass the class name filter.
+     */
+    public boolean accepts(String className1, String className2)
+    {
+        return classFilter == null ||
+            !(classFilter.matches(className1) ||
+              classFilter.matches(className2));
     }
 
 
     /**
      * Prints out the given warning and increments the warning count.
      */
-    public void print(String warning)
+    private void print(String warning)
     {
         printStream.println(warning);
 
diff --git a/src/proguard/classfile/visitor/AllClassVisitor.java b/src/proguard/classfile/visitor/AllClassVisitor.java
index e8c67db..06aca2c 100644
--- a/src/proguard/classfile/visitor/AllClassVisitor.java
+++ b/src/proguard/classfile/visitor/AllClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/src/proguard/classfile/visitor/AllFieldVisitor.java
index eb0047c..8bff7d4 100644
--- a/src/proguard/classfile/visitor/AllFieldVisitor.java
+++ b/src/proguard/classfile/visitor/AllFieldVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/AllMemberVisitor.java b/src/proguard/classfile/visitor/AllMemberVisitor.java
index d22dfd2..448470e 100644
--- a/src/proguard/classfile/visitor/AllMemberVisitor.java
+++ b/src/proguard/classfile/visitor/AllMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/src/proguard/classfile/visitor/AllMethodVisitor.java
index 70cbf51..75b919d 100644
--- a/src/proguard/classfile/visitor/AllMethodVisitor.java
+++ b/src/proguard/classfile/visitor/AllMethodVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/BottomClassFilter.java b/src/proguard/classfile/visitor/BottomClassFilter.java
index 7aaaabc..8f5bdd1 100644
--- a/src/proguard/classfile/visitor/BottomClassFilter.java
+++ b/src/proguard/classfile/visitor/BottomClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassAccessFilter.java b/src/proguard/classfile/visitor/ClassAccessFilter.java
index 90aee34..a8815b6 100644
--- a/src/proguard/classfile/visitor/ClassAccessFilter.java
+++ b/src/proguard/classfile/visitor/ClassAccessFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassCleaner.java b/src/proguard/classfile/visitor/ClassCleaner.java
index 5bab937..36165ef 100644
--- a/src/proguard/classfile/visitor/ClassCleaner.java
+++ b/src/proguard/classfile/visitor/ClassCleaner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassCollector.java b/src/proguard/classfile/visitor/ClassCollector.java
index 24b6ed9..a69fe76 100644
--- a/src/proguard/classfile/visitor/ClassCollector.java
+++ b/src/proguard/classfile/visitor/ClassCollector.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/visitor/ClassCounter.java
index 9aadce6..c58c090 100644
--- a/src/proguard/classfile/visitor/ClassCounter.java
+++ b/src/proguard/classfile/visitor/ClassCounter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassForNameClassVisitor.java b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java
index 7c93e78..ee028f8 100644
--- a/src/proguard/classfile/visitor/ClassForNameClassVisitor.java
+++ b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java
index b0d9da2..2e1755e 100644
--- a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java
+++ b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassNameFilter.java b/src/proguard/classfile/visitor/ClassNameFilter.java
index f924efb..c016a34 100644
--- a/src/proguard/classfile/visitor/ClassNameFilter.java
+++ b/src/proguard/classfile/visitor/ClassNameFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,6 +23,8 @@ package proguard.classfile.visitor;
 import proguard.classfile.*;
 import proguard.util.*;
 
+import java.util.List;
+
 /**
  * This <code>ClassVisitor</code> delegates its visits to another given
  * <code>ClassVisitor</code>, but only when the visited class has a name that
@@ -53,6 +55,21 @@ public class ClassNameFilter implements ClassVisitor
 
     /**
      * Creates a new ClassNameFilter.
+     * @param regularExpression the regular expression against which class names
+     *                          will be matched.
+     * @param classVisitor      the <code>ClassVisitor</code> to which visits
+     *                          will be delegated.
+     */
+    public ClassNameFilter(List         regularExpression,
+                           ClassVisitor classVisitor)
+    {
+        this(new ListParser(new ClassNameParser()).parse(regularExpression),
+             classVisitor);
+    }
+
+
+    /**
+     * Creates a new ClassNameFilter.
      * @param regularExpressionMatcher the regular expression against which
      *                                 class names will be matched.
      * @param classVisitor             the <code>ClassVisitor</code> to which
diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/ClassPoolFiller.java
index 3fd7a72..e1773de 100644
--- a/src/proguard/classfile/visitor/ClassPoolFiller.java
+++ b/src/proguard/classfile/visitor/ClassPoolFiller.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/src/proguard/classfile/visitor/ClassPoolVisitor.java
index 9b15722..0b659dc 100644
--- a/src/proguard/classfile/visitor/ClassPoolVisitor.java
+++ b/src/proguard/classfile/visitor/ClassPoolVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassPresenceFilter.java b/src/proguard/classfile/visitor/ClassPresenceFilter.java
index 3b6294f..429c340 100644
--- a/src/proguard/classfile/visitor/ClassPresenceFilter.java
+++ b/src/proguard/classfile/visitor/ClassPresenceFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassPrinter.java b/src/proguard/classfile/visitor/ClassPrinter.java
index 4d09c13..1da7d16 100644
--- a/src/proguard/classfile/visitor/ClassPrinter.java
+++ b/src/proguard/classfile/visitor/ClassPrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -397,11 +397,16 @@ implements   ClassVisitor,
     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
         println(visitorInfo(enclosingMethodAttribute) +
-                " Enclosing method attribute [" +
-                clazz.getClassName(enclosingMethodAttribute.u2classIndex)  +
-                (enclosingMethodAttribute.u2nameAndTypeIndex == 0 ?  "" : "." +
-                 clazz.getName(enclosingMethodAttribute.u2nameAndTypeIndex) + " " +
-                 clazz.getType(enclosingMethodAttribute.u2nameAndTypeIndex)) + "]");
+                " Enclosing method attribute:");
+
+        indent();
+        clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2classIndex, this);
+
+        if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
+        {
+            clazz.constantPoolEntryAccept(enclosingMethodAttribute.u2nameAndTypeIndex, this);
+        }
+        outdent();
     }
 
 
diff --git a/src/proguard/classfile/visitor/ClassVersionFilter.java b/src/proguard/classfile/visitor/ClassVersionFilter.java
index 1465ded..578cabf 100644
--- a/src/proguard/classfile/visitor/ClassVersionFilter.java
+++ b/src/proguard/classfile/visitor/ClassVersionFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassVersionSetter.java b/src/proguard/classfile/visitor/ClassVersionSetter.java
index 2f99af9..34dfbc1 100644
--- a/src/proguard/classfile/visitor/ClassVersionSetter.java
+++ b/src/proguard/classfile/visitor/ClassVersionSetter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ClassVisitor.java b/src/proguard/classfile/visitor/ClassVisitor.java
index 3219e7e..fdba2df 100644
--- a/src/proguard/classfile/visitor/ClassVisitor.java
+++ b/src/proguard/classfile/visitor/ClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
index de8f2b1..ec3fe68 100644
--- a/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
+++ b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/DotClassClassVisitor.java b/src/proguard/classfile/visitor/DotClassClassVisitor.java
index aa4b672..263dbd5 100644
--- a/src/proguard/classfile/visitor/DotClassClassVisitor.java
+++ b/src/proguard/classfile/visitor/DotClassClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ExceptClassFilter.java b/src/proguard/classfile/visitor/ExceptClassFilter.java
index c13cc94..924485e 100644
--- a/src/proguard/classfile/visitor/ExceptClassFilter.java
+++ b/src/proguard/classfile/visitor/ExceptClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ExceptClassesFilter.java b/src/proguard/classfile/visitor/ExceptClassesFilter.java
index 11a1348..7380c40 100644
--- a/src/proguard/classfile/visitor/ExceptClassesFilter.java
+++ b/src/proguard/classfile/visitor/ExceptClassesFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ExceptionCounter.java b/src/proguard/classfile/visitor/ExceptionCounter.java
index 6c37579..c324129 100644
--- a/src/proguard/classfile/visitor/ExceptionCounter.java
+++ b/src/proguard/classfile/visitor/ExceptionCounter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java
index 0989002..3911e39 100644
--- a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java
+++ b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -47,7 +47,7 @@ implements   ExceptionInfoVisitor
     public ExceptionExcludedOffsetFilter(int                  instructionOffset,
                                          ExceptionInfoVisitor exceptionInfoVisitor)
     {
-        this.instructionOffset          = instructionOffset;
+        this.instructionOffset    = instructionOffset;
         this.exceptionInfoVisitor = exceptionInfoVisitor;
     }
 
diff --git a/src/proguard/classfile/visitor/ExceptionCounter.java b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java
similarity index 58%
copy from src/proguard/classfile/visitor/ExceptionCounter.java
copy to src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java
index 6c37579..e0fdec3 100644
--- a/src/proguard/classfile/visitor/ExceptionCounter.java
+++ b/src/proguard/classfile/visitor/ExceptionHandlerConstantVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,25 +21,31 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.visitor.ExceptionInfoVisitor;
 
 /**
- * This ExceptionInfoVisitor counts the number of exceptions that has been visited.
+ * This <code>ExceptionInfoVisitor</code> lets a given
+ * <code>ConstantVisitor</code> visit all catch class constants of exceptions
+ * that it visits.
  *
  * @author Eric Lafortune
  */
-public class ExceptionCounter implements ExceptionInfoVisitor
+public class ExceptionHandlerConstantVisitor
+implements   ExceptionInfoVisitor
 {
-    private int count;
+    private final ConstantVisitor constantVisitor;
 
 
     /**
-     * Returns the number of exceptions that has been visited so far.
+     * Creates a new ExceptionHandlerConstantVisitor.
+     * @param constantVisitor the ConstantVisitor that will visit the catch
+     *                        class constants.
      */
-    public int getCount()
+    public ExceptionHandlerConstantVisitor(ConstantVisitor constantVisitor)
     {
-        return count;
+        this.constantVisitor = constantVisitor;
     }
 
 
@@ -47,6 +53,10 @@ public class ExceptionCounter implements ExceptionInfoVisitor
 
     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
-        count++;
+        int catchType = exceptionInfo.u2catchType;
+        if (catchType != 0)
+        {
+            clazz.constantPoolEntryAccept(catchType, constantVisitor);
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/visitor/ExceptionRangeFilter.java b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java
similarity index 74%
copy from src/proguard/classfile/visitor/ExceptionRangeFilter.java
copy to src/proguard/classfile/visitor/ExceptionHandlerFilter.java
index a2e0f93..a90fb56 100644
--- a/src/proguard/classfile/visitor/ExceptionRangeFilter.java
+++ b/src/proguard/classfile/visitor/ExceptionHandlerFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -27,11 +27,11 @@ import proguard.classfile.attribute.visitor.ExceptionInfoVisitor;
 /**
  * This <code>ExceptionInfoVisitor</code> delegates its visits to another given
  * <code>ExceptionInfoVisitor</code>, but only when the visited exception
- * overlaps with the given instruction range.
+ * targets an instruction in the given range of offsets.
  *
  * @author Eric Lafortune
  */
-public class ExceptionRangeFilter
+public class ExceptionHandlerFilter
 implements   ExceptionInfoVisitor
 {
     private final int                  startOffset;
@@ -40,15 +40,15 @@ implements   ExceptionInfoVisitor
 
 
     /**
-     * Creates a new ExceptionRangeFilter.
-     * @param startOffset          the start offset of the instruction range.
-     * @param endOffset            the end offset of the instruction range.
+     * Creates a new ExceptionHandlerFilter.
+     * @param startOffset          the start of the instruction offset range.
+     * @param endOffset            the end of the instruction offset range.
      * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits
      *                             will be delegated.
      */
-    public ExceptionRangeFilter(int                  startOffset,
-                                int                  endOffset,
-                                ExceptionInfoVisitor exceptionInfoVisitor)
+    public ExceptionHandlerFilter(int                  startOffset,
+                                  int                  endOffset,
+                                  ExceptionInfoVisitor exceptionInfoVisitor)
     {
         this.startOffset          = startOffset;
         this.endOffset            = endOffset;
@@ -60,9 +60,11 @@ implements   ExceptionInfoVisitor
 
     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
-        if (exceptionInfo.isApplicable(startOffset, endOffset))
+        int handlerPC = exceptionInfo.u2handlerPC;
+        if (handlerPC >= startOffset &&
+            handlerPC <  endOffset)
         {
             exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java
index 3114dbb..e2a4fc9 100644
--- a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java
+++ b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -47,7 +47,7 @@ implements   ExceptionInfoVisitor
     public ExceptionOffsetFilter(int                  instructionOffset,
                                  ExceptionInfoVisitor exceptionInfoVisitor)
     {
-        this.instructionOffset          = instructionOffset;
+        this.instructionOffset    = instructionOffset;
         this.exceptionInfoVisitor = exceptionInfoVisitor;
     }
 
diff --git a/src/proguard/classfile/visitor/ExceptionRangeFilter.java b/src/proguard/classfile/visitor/ExceptionRangeFilter.java
index a2e0f93..c541b1f 100644
--- a/src/proguard/classfile/visitor/ExceptionRangeFilter.java
+++ b/src/proguard/classfile/visitor/ExceptionRangeFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java
index 9c59944..6fe2e7d 100644
--- a/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java
+++ b/src/proguard/classfile/visitor/ImplementedClassConstantFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ImplementedClassFilter.java b/src/proguard/classfile/visitor/ImplementedClassFilter.java
index 7e78cd9..955a74e 100644
--- a/src/proguard/classfile/visitor/ImplementedClassFilter.java
+++ b/src/proguard/classfile/visitor/ImplementedClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java
index a247c64..9e9cea3 100644
--- a/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java
+++ b/src/proguard/classfile/visitor/ImplementingClassConstantFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/LibraryClassFilter.java b/src/proguard/classfile/visitor/LibraryClassFilter.java
index 2e23a51..0e40f2f 100644
--- a/src/proguard/classfile/visitor/LibraryClassFilter.java
+++ b/src/proguard/classfile/visitor/LibraryClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/LibraryMemberFilter.java b/src/proguard/classfile/visitor/LibraryMemberFilter.java
index aea455d..0ee80e5 100644
--- a/src/proguard/classfile/visitor/LibraryMemberFilter.java
+++ b/src/proguard/classfile/visitor/LibraryMemberFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberAccessFilter.java b/src/proguard/classfile/visitor/MemberAccessFilter.java
index 485f5df..6fd32e3 100644
--- a/src/proguard/classfile/visitor/MemberAccessFilter.java
+++ b/src/proguard/classfile/visitor/MemberAccessFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberClassAccessFilter.java b/src/proguard/classfile/visitor/MemberClassAccessFilter.java
index 39296cd..85272ff 100644
--- a/src/proguard/classfile/visitor/MemberClassAccessFilter.java
+++ b/src/proguard/classfile/visitor/MemberClassAccessFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberCollector.java b/src/proguard/classfile/visitor/MemberCollector.java
index 41e3403..ec68b2d 100644
--- a/src/proguard/classfile/visitor/MemberCollector.java
+++ b/src/proguard/classfile/visitor/MemberCollector.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberCounter.java b/src/proguard/classfile/visitor/MemberCounter.java
index 933c957..c2da72e 100644
--- a/src/proguard/classfile/visitor/MemberCounter.java
+++ b/src/proguard/classfile/visitor/MemberCounter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberDescriptorFilter.java b/src/proguard/classfile/visitor/MemberDescriptorFilter.java
index 4721c77..bd69304 100644
--- a/src/proguard/classfile/visitor/MemberDescriptorFilter.java
+++ b/src/proguard/classfile/visitor/MemberDescriptorFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberNameFilter.java b/src/proguard/classfile/visitor/MemberNameFilter.java
index 935f1ad..0fe450e 100644
--- a/src/proguard/classfile/visitor/MemberNameFilter.java
+++ b/src/proguard/classfile/visitor/MemberNameFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberToClassVisitor.java b/src/proguard/classfile/visitor/MemberToClassVisitor.java
index 2f75efe..a405cfc 100644
--- a/src/proguard/classfile/visitor/MemberToClassVisitor.java
+++ b/src/proguard/classfile/visitor/MemberToClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MemberVisitor.java b/src/proguard/classfile/visitor/MemberVisitor.java
index 96caaee..01fdf71 100644
--- a/src/proguard/classfile/visitor/MemberVisitor.java
+++ b/src/proguard/classfile/visitor/MemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MethodImplementationFilter.java b/src/proguard/classfile/visitor/MethodImplementationFilter.java
index 4b97050..57d923a 100644
--- a/src/proguard/classfile/visitor/MethodImplementationFilter.java
+++ b/src/proguard/classfile/visitor/MethodImplementationFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MethodImplementationTraveler.java b/src/proguard/classfile/visitor/MethodImplementationTraveler.java
index c9bd99e..dc0ea36 100644
--- a/src/proguard/classfile/visitor/MethodImplementationTraveler.java
+++ b/src/proguard/classfile/visitor/MethodImplementationTraveler.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
index 13d4b3e..044d55a 100644
--- a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
+++ b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MultiClassVisitor.java b/src/proguard/classfile/visitor/MultiClassVisitor.java
index 0259b73..d34d91e 100644
--- a/src/proguard/classfile/visitor/MultiClassVisitor.java
+++ b/src/proguard/classfile/visitor/MultiClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/MultiMemberVisitor.java b/src/proguard/classfile/visitor/MultiMemberVisitor.java
index 9edfeb7..cc4629c 100644
--- a/src/proguard/classfile/visitor/MultiMemberVisitor.java
+++ b/src/proguard/classfile/visitor/MultiMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/NamedClassVisitor.java b/src/proguard/classfile/visitor/NamedClassVisitor.java
index a7aabc2..a14d04a 100644
--- a/src/proguard/classfile/visitor/NamedClassVisitor.java
+++ b/src/proguard/classfile/visitor/NamedClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/src/proguard/classfile/visitor/NamedFieldVisitor.java
index bb47b27..76b66c6 100644
--- a/src/proguard/classfile/visitor/NamedFieldVisitor.java
+++ b/src/proguard/classfile/visitor/NamedFieldVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/src/proguard/classfile/visitor/NamedMethodVisitor.java
index 5b30fe3..d4611c1 100644
--- a/src/proguard/classfile/visitor/NamedMethodVisitor.java
+++ b/src/proguard/classfile/visitor/NamedMethodVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ProgramClassFilter.java b/src/proguard/classfile/visitor/ProgramClassFilter.java
index e4ddd56..fba3b21 100644
--- a/src/proguard/classfile/visitor/ProgramClassFilter.java
+++ b/src/proguard/classfile/visitor/ProgramClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ProgramMemberFilter.java b/src/proguard/classfile/visitor/ProgramMemberFilter.java
index 5463791..048a1e6 100644
--- a/src/proguard/classfile/visitor/ProgramMemberFilter.java
+++ b/src/proguard/classfile/visitor/ProgramMemberFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/ReferencedClassVisitor.java b/src/proguard/classfile/visitor/ReferencedClassVisitor.java
index aba4b7b..986c3f9 100644
--- a/src/proguard/classfile/visitor/ReferencedClassVisitor.java
+++ b/src/proguard/classfile/visitor/ReferencedClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -32,7 +32,8 @@ import proguard.classfile.util.SimplifiedVisitor;
 /**
  * This ClassVisitor, MemberVisitor, ConstantVisitor, AttributeVisitor, etc.
  * lets a given ClassVisitor visit all the referenced classes of the elements
- * that it visits.
+ * that it visits. Only downstream elements are considered (in order to avoid
+ * loops and repeated visits).
  *
  * @author Eric Lafortune
  */
diff --git a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java
index 93aba43..c4d34b8 100644
--- a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java
+++ b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/SimilarMemberVisitor.java b/src/proguard/classfile/visitor/SimilarMemberVisitor.java
index c62865c..6dc06af 100644
--- a/src/proguard/classfile/visitor/SimilarMemberVisitor.java
+++ b/src/proguard/classfile/visitor/SimilarMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/SimpleClassPrinter.java b/src/proguard/classfile/visitor/SimpleClassPrinter.java
index a6e438f..a661110 100644
--- a/src/proguard/classfile/visitor/SimpleClassPrinter.java
+++ b/src/proguard/classfile/visitor/SimpleClassPrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/SubclassFilter.java b/src/proguard/classfile/visitor/SubclassFilter.java
index ad3aa1f..69ea1a1 100644
--- a/src/proguard/classfile/visitor/SubclassFilter.java
+++ b/src/proguard/classfile/visitor/SubclassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/SubclassTraveler.java b/src/proguard/classfile/visitor/SubclassTraveler.java
index 97c263c..4170341 100644
--- a/src/proguard/classfile/visitor/SubclassTraveler.java
+++ b/src/proguard/classfile/visitor/SubclassTraveler.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/VariableClassVisitor.java b/src/proguard/classfile/visitor/VariableClassVisitor.java
index 673b328..2f575c4 100644
--- a/src/proguard/classfile/visitor/VariableClassVisitor.java
+++ b/src/proguard/classfile/visitor/VariableClassVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/classfile/visitor/VariableMemberVisitor.java b/src/proguard/classfile/visitor/VariableMemberVisitor.java
index b6b69ee..c58cff3 100644
--- a/src/proguard/classfile/visitor/VariableMemberVisitor.java
+++ b/src/proguard/classfile/visitor/VariableMemberVisitor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/BasicBranchUnit.java b/src/proguard/evaluation/BasicBranchUnit.java
index 1a5fad4..3a2db76 100644
--- a/src/proguard/evaluation/BasicBranchUnit.java
+++ b/src/proguard/evaluation/BasicBranchUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/BasicInvocationUnit.java b/src/proguard/evaluation/BasicInvocationUnit.java
index c05bd53..bccd866 100644
--- a/src/proguard/evaluation/BasicInvocationUnit.java
+++ b/src/proguard/evaluation/BasicInvocationUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -232,7 +232,7 @@ implements   InvocationUnit,
 
 
     /**
-     * Sets the class thru which the specified field is accessed.
+     * Sets the class through which the specified field is accessed.
      */
     protected void setFieldClassValue(Clazz          clazz,
                                       RefConstant    refConstant,
@@ -243,7 +243,7 @@ implements   InvocationUnit,
 
 
     /**
-     * Returns the class thru which the specified field is accessed.
+     * Returns the class though which the specified field is accessed.
      */
     protected Value getFieldClassValue(Clazz       clazz,
                                        RefConstant refConstant,
diff --git a/src/proguard/evaluation/BranchUnit.java b/src/proguard/evaluation/BranchUnit.java
index 9d8b1cc..b709807 100644
--- a/src/proguard/evaluation/BranchUnit.java
+++ b/src/proguard/evaluation/BranchUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/InvocationUnit.java b/src/proguard/evaluation/InvocationUnit.java
index f18581f..cb4d3c5 100644
--- a/src/proguard/evaluation/InvocationUnit.java
+++ b/src/proguard/evaluation/InvocationUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/Processor.java b/src/proguard/evaluation/Processor.java
index 9f30010..74afd0b 100644
--- a/src/proguard/evaluation/Processor.java
+++ b/src/proguard/evaluation/Processor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/Stack.java b/src/proguard/evaluation/Stack.java
index b2cb0ee..2808e62 100644
--- a/src/proguard/evaluation/Stack.java
+++ b/src/proguard/evaluation/Stack.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/TracedStack.java b/src/proguard/evaluation/TracedStack.java
index cf32883..c24f783 100644
--- a/src/proguard/evaluation/TracedStack.java
+++ b/src/proguard/evaluation/TracedStack.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/TracedVariables.java b/src/proguard/evaluation/TracedVariables.java
index 375150f..1ae6ba6 100644
--- a/src/proguard/evaluation/TracedVariables.java
+++ b/src/proguard/evaluation/TracedVariables.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/Variables.java b/src/proguard/evaluation/Variables.java
index 53d5db0..4496aaa 100644
--- a/src/proguard/evaluation/Variables.java
+++ b/src/proguard/evaluation/Variables.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/Category1Value.java b/src/proguard/evaluation/value/Category1Value.java
index 7db7eca..b8c5db2 100644
--- a/src/proguard/evaluation/value/Category1Value.java
+++ b/src/proguard/evaluation/value/Category1Value.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/Category2Value.java b/src/proguard/evaluation/value/Category2Value.java
index 95e22db..65916c7 100644
--- a/src/proguard/evaluation/value/Category2Value.java
+++ b/src/proguard/evaluation/value/Category2Value.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ComparisonValue.java b/src/proguard/evaluation/value/ComparisonValue.java
index 6fcc22b..e4e2e02 100644
--- a/src/proguard/evaluation/value/ComparisonValue.java
+++ b/src/proguard/evaluation/value/ComparisonValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,7 +21,7 @@
 package proguard.evaluation.value;
 
 /**
- * This IntegerValue represents the result of a comparions of two scalar
+ * This IntegerValue represents the result of a comparisons of two scalar
  * values.
  *
  * @author Eric Lafortune
diff --git a/src/proguard/evaluation/value/CompositeDoubleValue.java b/src/proguard/evaluation/value/CompositeDoubleValue.java
index c3b4c2b..a6d48ef 100644
--- a/src/proguard/evaluation/value/CompositeDoubleValue.java
+++ b/src/proguard/evaluation/value/CompositeDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/CompositeFloatValue.java b/src/proguard/evaluation/value/CompositeFloatValue.java
index 73698fb..4df890a 100644
--- a/src/proguard/evaluation/value/CompositeFloatValue.java
+++ b/src/proguard/evaluation/value/CompositeFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/CompositeIntegerValue.java b/src/proguard/evaluation/value/CompositeIntegerValue.java
index 72ffbc9..6d4f59c 100644
--- a/src/proguard/evaluation/value/CompositeIntegerValue.java
+++ b/src/proguard/evaluation/value/CompositeIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/CompositeLongValue.java b/src/proguard/evaluation/value/CompositeLongValue.java
index 7f2b5f8..7f63211 100644
--- a/src/proguard/evaluation/value/CompositeLongValue.java
+++ b/src/proguard/evaluation/value/CompositeLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedByteValue.java b/src/proguard/evaluation/value/ConvertedByteValue.java
index b32665a..7ab5d0a 100644
--- a/src/proguard/evaluation/value/ConvertedByteValue.java
+++ b/src/proguard/evaluation/value/ConvertedByteValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedCharacterValue.java b/src/proguard/evaluation/value/ConvertedCharacterValue.java
index a0ce754..76568d1 100644
--- a/src/proguard/evaluation/value/ConvertedCharacterValue.java
+++ b/src/proguard/evaluation/value/ConvertedCharacterValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedDoubleValue.java b/src/proguard/evaluation/value/ConvertedDoubleValue.java
index b39dce2..a6afe34 100644
--- a/src/proguard/evaluation/value/ConvertedDoubleValue.java
+++ b/src/proguard/evaluation/value/ConvertedDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedFloatValue.java b/src/proguard/evaluation/value/ConvertedFloatValue.java
index cabf6ef..33683b7 100644
--- a/src/proguard/evaluation/value/ConvertedFloatValue.java
+++ b/src/proguard/evaluation/value/ConvertedFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedIntegerValue.java b/src/proguard/evaluation/value/ConvertedIntegerValue.java
index 3bc6b2a..aecb533 100644
--- a/src/proguard/evaluation/value/ConvertedIntegerValue.java
+++ b/src/proguard/evaluation/value/ConvertedIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedLongValue.java b/src/proguard/evaluation/value/ConvertedLongValue.java
index 427a539..ec97008 100644
--- a/src/proguard/evaluation/value/ConvertedLongValue.java
+++ b/src/proguard/evaluation/value/ConvertedLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ConvertedShortValue.java b/src/proguard/evaluation/value/ConvertedShortValue.java
index 183c513..ab79b49 100644
--- a/src/proguard/evaluation/value/ConvertedShortValue.java
+++ b/src/proguard/evaluation/value/ConvertedShortValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/DoubleValue.java b/src/proguard/evaluation/value/DoubleValue.java
index f83a639..e39ee5c 100644
--- a/src/proguard/evaluation/value/DoubleValue.java
+++ b/src/proguard/evaluation/value/DoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/FloatValue.java b/src/proguard/evaluation/value/FloatValue.java
index b981155..b30e395 100644
--- a/src/proguard/evaluation/value/FloatValue.java
+++ b/src/proguard/evaluation/value/FloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedDoubleValue.java b/src/proguard/evaluation/value/IdentifiedDoubleValue.java
index 44d3769..4009c6e 100644
--- a/src/proguard/evaluation/value/IdentifiedDoubleValue.java
+++ b/src/proguard/evaluation/value/IdentifiedDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedFloatValue.java b/src/proguard/evaluation/value/IdentifiedFloatValue.java
index 1bf7b51..87bed64 100644
--- a/src/proguard/evaluation/value/IdentifiedFloatValue.java
+++ b/src/proguard/evaluation/value/IdentifiedFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedIntegerValue.java b/src/proguard/evaluation/value/IdentifiedIntegerValue.java
index 9c9e490..66e88ee 100644
--- a/src/proguard/evaluation/value/IdentifiedIntegerValue.java
+++ b/src/proguard/evaluation/value/IdentifiedIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedLongValue.java b/src/proguard/evaluation/value/IdentifiedLongValue.java
index 33bb78e..eea1816 100644
--- a/src/proguard/evaluation/value/IdentifiedLongValue.java
+++ b/src/proguard/evaluation/value/IdentifiedLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedReferenceValue.java b/src/proguard/evaluation/value/IdentifiedReferenceValue.java
index 323b93f..cb5efc1 100644
--- a/src/proguard/evaluation/value/IdentifiedReferenceValue.java
+++ b/src/proguard/evaluation/value/IdentifiedReferenceValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IdentifiedValueFactory.java b/src/proguard/evaluation/value/IdentifiedValueFactory.java
index 0b37ee7..2b14e72 100644
--- a/src/proguard/evaluation/value/IdentifiedValueFactory.java
+++ b/src/proguard/evaluation/value/IdentifiedValueFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/InstructionOffsetValue.java b/src/proguard/evaluation/value/InstructionOffsetValue.java
index a60008e..10b5a6f 100644
--- a/src/proguard/evaluation/value/InstructionOffsetValue.java
+++ b/src/proguard/evaluation/value/InstructionOffsetValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/IntegerValue.java b/src/proguard/evaluation/value/IntegerValue.java
index 3de3aae..508c5f5 100644
--- a/src/proguard/evaluation/value/IntegerValue.java
+++ b/src/proguard/evaluation/value/IntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/LongValue.java b/src/proguard/evaluation/value/LongValue.java
index be51d5d..f7ba162 100644
--- a/src/proguard/evaluation/value/LongValue.java
+++ b/src/proguard/evaluation/value/LongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/NegatedDoubleValue.java b/src/proguard/evaluation/value/NegatedDoubleValue.java
index 44a1978..2bc9423 100644
--- a/src/proguard/evaluation/value/NegatedDoubleValue.java
+++ b/src/proguard/evaluation/value/NegatedDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/NegatedFloatValue.java b/src/proguard/evaluation/value/NegatedFloatValue.java
index 31b338c..0ddf4ab 100644
--- a/src/proguard/evaluation/value/NegatedFloatValue.java
+++ b/src/proguard/evaluation/value/NegatedFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/NegatedIntegerValue.java b/src/proguard/evaluation/value/NegatedIntegerValue.java
index 22f9c5f..a89a2d9 100644
--- a/src/proguard/evaluation/value/NegatedIntegerValue.java
+++ b/src/proguard/evaluation/value/NegatedIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/NegatedLongValue.java b/src/proguard/evaluation/value/NegatedLongValue.java
index 52ee767..c3b22bb 100644
--- a/src/proguard/evaluation/value/NegatedLongValue.java
+++ b/src/proguard/evaluation/value/NegatedLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ParticularDoubleValue.java b/src/proguard/evaluation/value/ParticularDoubleValue.java
index e436b9c..05bc559 100644
--- a/src/proguard/evaluation/value/ParticularDoubleValue.java
+++ b/src/proguard/evaluation/value/ParticularDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ParticularFloatValue.java b/src/proguard/evaluation/value/ParticularFloatValue.java
index 0bf5306..59e4357 100644
--- a/src/proguard/evaluation/value/ParticularFloatValue.java
+++ b/src/proguard/evaluation/value/ParticularFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ParticularIntegerValue.java b/src/proguard/evaluation/value/ParticularIntegerValue.java
index b1a2b35..8a4ac1d 100644
--- a/src/proguard/evaluation/value/ParticularIntegerValue.java
+++ b/src/proguard/evaluation/value/ParticularIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ParticularLongValue.java b/src/proguard/evaluation/value/ParticularLongValue.java
index 762c5ba..61d2e04 100644
--- a/src/proguard/evaluation/value/ParticularLongValue.java
+++ b/src/proguard/evaluation/value/ParticularLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ReferenceValue.java b/src/proguard/evaluation/value/ReferenceValue.java
index bdc522b..418c6f8 100644
--- a/src/proguard/evaluation/value/ReferenceValue.java
+++ b/src/proguard/evaluation/value/ReferenceValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -496,7 +496,7 @@ public class ReferenceValue extends Category1Value
         {
             return true;
         }
-        
+
         if (object == null ||
             this.getClass() != object.getClass())
         {
diff --git a/src/proguard/evaluation/value/SpecificDoubleValue.java b/src/proguard/evaluation/value/SpecificDoubleValue.java
index 973b678..572d891 100644
--- a/src/proguard/evaluation/value/SpecificDoubleValue.java
+++ b/src/proguard/evaluation/value/SpecificDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/SpecificFloatValue.java b/src/proguard/evaluation/value/SpecificFloatValue.java
index e507e76..3bc3679 100644
--- a/src/proguard/evaluation/value/SpecificFloatValue.java
+++ b/src/proguard/evaluation/value/SpecificFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/SpecificIntegerValue.java b/src/proguard/evaluation/value/SpecificIntegerValue.java
index 5e1e594..57c48b1 100644
--- a/src/proguard/evaluation/value/SpecificIntegerValue.java
+++ b/src/proguard/evaluation/value/SpecificIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/SpecificLongValue.java b/src/proguard/evaluation/value/SpecificLongValue.java
index 0b07daf..5e12bde 100644
--- a/src/proguard/evaluation/value/SpecificLongValue.java
+++ b/src/proguard/evaluation/value/SpecificLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/SpecificValueFactory.java b/src/proguard/evaluation/value/SpecificValueFactory.java
index 5bc5b7e..72dd1d3 100644
--- a/src/proguard/evaluation/value/SpecificValueFactory.java
+++ b/src/proguard/evaluation/value/SpecificValueFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/TopValue.java b/src/proguard/evaluation/value/TopValue.java
index 0a53e7e..048a1ff 100644
--- a/src/proguard/evaluation/value/TopValue.java
+++ b/src/proguard/evaluation/value/TopValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/UnknownDoubleValue.java b/src/proguard/evaluation/value/UnknownDoubleValue.java
index dd2d519..79cc4de 100644
--- a/src/proguard/evaluation/value/UnknownDoubleValue.java
+++ b/src/proguard/evaluation/value/UnknownDoubleValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/UnknownFloatValue.java b/src/proguard/evaluation/value/UnknownFloatValue.java
index bd1d745..3f9622a 100644
--- a/src/proguard/evaluation/value/UnknownFloatValue.java
+++ b/src/proguard/evaluation/value/UnknownFloatValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/UnknownIntegerValue.java b/src/proguard/evaluation/value/UnknownIntegerValue.java
index f25b398..be5e79d 100644
--- a/src/proguard/evaluation/value/UnknownIntegerValue.java
+++ b/src/proguard/evaluation/value/UnknownIntegerValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/UnknownLongValue.java b/src/proguard/evaluation/value/UnknownLongValue.java
index f169208..83a75dc 100644
--- a/src/proguard/evaluation/value/UnknownLongValue.java
+++ b/src/proguard/evaluation/value/UnknownLongValue.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/Value.java b/src/proguard/evaluation/value/Value.java
index f3f6393..e24ece1 100644
--- a/src/proguard/evaluation/value/Value.java
+++ b/src/proguard/evaluation/value/Value.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/evaluation/value/ValueFactory.java b/src/proguard/evaluation/value/ValueFactory.java
index 1a84d58..8415381 100644
--- a/src/proguard/evaluation/value/ValueFactory.java
+++ b/src/proguard/evaluation/value/ValueFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/ClassPathPanel.java b/src/proguard/gui/ClassPathPanel.java
index 29532f3..95f3d1b 100644
--- a/src/proguard/gui/ClassPathPanel.java
+++ b/src/proguard/gui/ClassPathPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,13 +20,14 @@
  */
 package proguard.gui;
 
+import proguard.*;
+import proguard.util.ListUtil;
+
+import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
 import java.io.File;
-
-import javax.swing.*;
-
-import proguard.*;
+import java.util.List;
 
 /**
  * This <code>ListPanel</code> allows the user to add, edit, filter, move, and
@@ -417,7 +418,7 @@ class ClassPathPanel extends ListPanel
         }
 
 
-        private StringBuffer appendFilter(StringBuffer filter, String additionalFilter)
+        private StringBuffer appendFilter(StringBuffer filter, List additionalFilter)
         {
             if (filter != null)
             {
@@ -431,7 +432,7 @@ class ClassPathPanel extends ListPanel
                     filter = new StringBuffer().append('(');
                 }
 
-                filter.append(additionalFilter);
+                filter.append(ListUtil.commaSeparatedString(additionalFilter));
             }
 
             return filter;
diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java
index d2478b2..36d80d4 100644
--- a/src/proguard/gui/ClassSpecificationDialog.java
+++ b/src/proguard/gui/ClassSpecificationDialog.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -79,7 +79,7 @@ final class ClassSpecificationDialog extends JDialog
 
     public ClassSpecificationDialog(JFrame owner, boolean fullKeepOptions)
     {
-        super(owner, true);
+        super(owner, msg("specifyClasses"), true);
         setResizable(true);
 
         // Create some constraints that can be reused.
@@ -346,15 +346,15 @@ final class ClassSpecificationDialog extends JDialog
 
 
     /**
-     * Sets the KeepSpecification to be represented in this dialog.
+     * Sets the KeepClassSpecification to be represented in this dialog.
      */
-    public void setKeepSpecification(KeepSpecification keepSpecification)
+    public void setKeepSpecification(KeepClassSpecification keepClassSpecification)
     {
-        boolean markClasses       = keepSpecification.markClasses;
-        boolean markConditionally = keepSpecification.markConditionally;
-        boolean allowShrinking    = keepSpecification.allowShrinking;
-        boolean allowOptimization = keepSpecification.allowOptimization;
-        boolean allowObfuscation  = keepSpecification.allowObfuscation;
+        boolean markClasses       = keepClassSpecification.markClasses;
+        boolean markConditionally = keepClassSpecification.markConditionally;
+        boolean allowShrinking    = keepClassSpecification.allowShrinking;
+        boolean allowOptimization = keepClassSpecification.allowOptimization;
+        boolean allowObfuscation  = keepClassSpecification.allowObfuscation;
 
         // Figure out the proper keep radio button and set it.
         JRadioButton keepOptionRadioButton =
@@ -369,7 +369,7 @@ final class ClassSpecificationDialog extends JDialog
         allowOptimizationCheckBox.setSelected(allowOptimization);
         allowObfuscationCheckBox .setSelected(allowObfuscation);
 
-        setClassSpecification(keepSpecification);
+        setClassSpecification(keepClassSpecification);
     }
 
 
@@ -409,9 +409,9 @@ final class ClassSpecificationDialog extends JDialog
 
 
     /**
-     * Returns the KeepSpecification currently represented in this dialog.
+     * Returns the KeepClassSpecification currently represented in this dialog.
      */
-    public KeepSpecification getKeepSpecification()
+    public KeepClassSpecification getKeepSpecification()
     {
         boolean markClasses       = !keepClassMembersRadioButton     .isSelected();
         boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
@@ -419,7 +419,7 @@ final class ClassSpecificationDialog extends JDialog
         boolean allowOptimization = allowOptimizationCheckBox        .isSelected();
         boolean allowObfuscation  = allowObfuscationCheckBox         .isSelected();
 
-        return new KeepSpecification(markClasses,
+        return new KeepClassSpecification(markClasses,
                                      markConditionally,
                                      allowShrinking,
                                      allowOptimization,
@@ -490,8 +490,8 @@ final class ClassSpecificationDialog extends JDialog
      * flags of the given keep option.
      */
     private void setClassSpecificationRadioButtons(ClassSpecification classSpecification,
-                                                    int                 flag,
-                                                    JRadioButton[]      radioButtons)
+                                                   int                flag,
+                                                   JRadioButton[]     radioButtons)
     {
         int index = (classSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
                     (classSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
@@ -505,8 +505,8 @@ final class ClassSpecificationDialog extends JDialog
      * button triplet.
      */
     private void getClassSpecificationRadioButtons(ClassSpecification classSpecification,
-                                                    int                 flag,
-                                                    JRadioButton[]      radioButtons)
+                                                   int                flag,
+                                                   JRadioButton[]     radioButtons)
     {
         if      (radioButtons[0].isSelected())
         {
diff --git a/src/proguard/gui/ClassSpecificationsPanel.java b/src/proguard/gui/ClassSpecificationsPanel.java
index 03ee429..2cf0b1d 100644
--- a/src/proguard/gui/ClassSpecificationsPanel.java
+++ b/src/proguard/gui/ClassSpecificationsPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/ExtensionFileFilter.java b/src/proguard/gui/ExtensionFileFilter.java
index ef66a7a..d67be40 100644
--- a/src/proguard/gui/ExtensionFileFilter.java
+++ b/src/proguard/gui/ExtensionFileFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/FilterBuilder.java b/src/proguard/gui/FilterBuilder.java
new file mode 100644
index 0000000..e46193f
--- /dev/null
+++ b/src/proguard/gui/FilterBuilder.java
@@ -0,0 +1,208 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.gui;
+
+import javax.swing.*;
+
+/**
+ * This class builds filters corresponding to the selections and names of a
+ * given list of check boxes.
+ */
+public class FilterBuilder
+{
+    private JCheckBox[] checkBoxes;
+    private char        separator;
+
+
+    /**
+     * Creates a new FilterBuilder.
+     * @param checkBoxes the check boxes with names and selections that should
+     *                   be reflected in the output filter.
+     * @param separator  the separator for the names in the check boxes.
+     */
+    public FilterBuilder(JCheckBox[] checkBoxes, char separator)
+    {
+        this.checkBoxes = checkBoxes;
+        this.separator  = separator;
+    }
+
+
+    /**
+     * Builds a filter for the current names and selections of the check boxes.
+     */
+    public String buildFilter()
+    {
+        StringBuffer positive = new StringBuffer();
+        StringBuffer negative = new StringBuffer();
+
+        buildFilter("", positive, negative);
+
+        return positive.length() <= negative.length() ?
+            positive.toString() :
+            negative.toString();
+    }
+
+
+    /**
+     * Builds two versions of the filter for the given prefix.
+     * @param prefix   the prefix.
+     * @param positive the filter to be extended, assuming the matching
+     *                 strings are accepted.
+     * @param negative the filter to be extended, assuming the matching
+     *                 strings are rejected.
+     */
+    private void buildFilter(String       prefix,
+                             StringBuffer positive,
+                             StringBuffer negative)
+    {
+        int positiveCount = 0;
+        int negativeCount = 0;
+
+        // Count all selected and unselected check boxes with the prefix.
+        for (int index = 0; index < checkBoxes.length; index++)
+        {
+            JCheckBox checkBox = checkBoxes[index];
+            String    name     = checkBox.getText();
+
+            if (name.startsWith(prefix))
+            {
+                if (checkBox.isSelected())
+                {
+                    positiveCount++;
+                }
+                else
+                {
+                    negativeCount++;
+                }
+            }
+        }
+
+        // Are there only unselected check boxes?
+        if (positiveCount == 0)
+        {
+            // Extend the positive filter with exceptions and return.
+            if (positive.length() > 0)
+            {
+                positive.append(',');
+            }
+            positive.append('!').append(prefix);
+            if (prefix.length() == 0 ||
+                prefix.charAt(prefix.length()-1) == separator)
+            {
+                positive.append('*');
+            }
+
+            return;
+        }
+
+        // Are there only selected check boxes?
+        if (negativeCount == 0)
+        {
+            // Extend the negative filter with exceptions and return.
+            if (negative.length() > 0)
+            {
+                negative.append(',');
+            }
+            negative.append(prefix);
+            if (prefix.length() == 0 ||
+                prefix.charAt(prefix.length()-1) == separator)
+            {
+                negative.append('*');
+            }
+
+            return;
+        }
+
+        // Create new positive and negative filters for names starting with the
+        // prefix only.
+        StringBuffer positiveFilter = new StringBuffer();
+        StringBuffer negativeFilter = new StringBuffer();
+
+        String newPrefix = null;
+
+        for (int index = 0; index < checkBoxes.length; index++)
+        {
+            String name = checkBoxes[index].getText();
+
+            if (name.startsWith(prefix))
+            {
+                if (newPrefix == null ||
+                    !name.startsWith(newPrefix))
+                {
+                    int prefixIndex =
+                        name.indexOf(separator, prefix.length()+1);
+
+                    newPrefix = prefixIndex >= 0 ?
+                        name.substring(0, prefixIndex+1) :
+                        name;
+
+                    buildFilter(newPrefix,
+                                positiveFilter,
+                                negativeFilter);
+                }
+            }
+        }
+
+        // Extend the positive filter.
+        if (positiveFilter.length() <= negativeFilter.length() + prefix.length() + 3)
+        {
+            if (positive.length() > 0 &&
+                positiveFilter.length() > 0)
+            {
+                positive.append(',');
+            }
+
+            positive.append(positiveFilter);
+        }
+        else
+        {
+            if (positive.length() > 0 &&
+                negativeFilter.length() > 0)
+            {
+                positive.append(',');
+            }
+
+            positive.append(negativeFilter).append(",!").append(prefix).append('*');
+        }
+
+        // Extend the negative filter.
+        if (negativeFilter.length() <= positiveFilter.length() + prefix.length() + 4)
+        {
+            if (negative.length() > 0 &&
+                negativeFilter.length() > 0)
+            {
+                negative.append(',');
+            }
+
+            negative.append(negativeFilter);
+        }
+        else
+        {
+            if (negative.length() > 0 &&
+                positiveFilter.length() > 0)
+            {
+                negative.append(',');
+            }
+
+            negative.append(positiveFilter).append(',').append(prefix).append('*');
+        }
+    }
+}
diff --git a/src/proguard/gui/FilterDialog.java b/src/proguard/gui/FilterDialog.java
index 1d82ad9..1567a31 100644
--- a/src/proguard/gui/FilterDialog.java
+++ b/src/proguard/gui/FilterDialog.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,10 +20,13 @@
  */
 package proguard.gui;
 
+import proguard.util.ListUtil;
+
 import javax.swing.*;
 import javax.swing.border.*;
 import java.awt.*;
 import java.awt.event.*;
+import java.util.List;
 
 /**
  * This <code>JDialog</code> allows the user to enter a String.
@@ -177,100 +180,100 @@ public class FilterDialog extends JDialog
     /**
      * Sets the filter to be represented in this dialog.
      */
-    public void setFilter(String filter)
+    public void setFilter(List filter)
     {
-        filterTextField.setText(filter != null ? filter : DEFAULT_FILTER);
+        filterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_FILTER);
     }
 
 
     /**
      * Returns the filter currently represented in this dialog.
      */
-    public String getFilter()
+    public List getFilter()
     {
         String filter = filterTextField.getText();
 
-        return filter.equals(DEFAULT_FILTER) ? null : filter;
+        return filter.equals(DEFAULT_FILTER) ? null : ListUtil.commaSeparatedList(filter);
     }
 
 
     /**
      * Sets the jar filter to be represented in this dialog.
      */
-    public void setJarFilter(String filter)
+    public void setJarFilter(List filter)
     {
-        jarFilterTextField.setText(filter != null ? filter : DEFAULT_JAR_FILTER);
+        jarFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_JAR_FILTER);
     }
 
 
     /**
      * Returns the jar filter currently represented in this dialog.
      */
-    public String getJarFilter()
+    public List getJarFilter()
     {
         String filter = jarFilterTextField.getText();
 
-        return filter.equals(DEFAULT_JAR_FILTER) ? null : filter;
+        return filter.equals(DEFAULT_JAR_FILTER) ? null : ListUtil.commaSeparatedList(filter);
     }
 
 
     /**
      * Sets the war filter to be represented in this dialog.
      */
-    public void setWarFilter(String filter)
+    public void setWarFilter(List filter)
     {
-        warFilterTextField.setText(filter != null ? filter : DEFAULT_WAR_FILTER);
+        warFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_WAR_FILTER);
     }
 
 
     /**
      * Returns the war filter currently represented in this dialog.
      */
-    public String getWarFilter()
+    public List getWarFilter()
     {
         String filter = warFilterTextField.getText();
 
-        return filter.equals(DEFAULT_WAR_FILTER) ? null : filter;
+        return filter.equals(DEFAULT_WAR_FILTER) ? null : ListUtil.commaSeparatedList(filter);
     }
 
 
     /**
      * Sets the ear filter to be represented in this dialog.
      */
-    public void setEarFilter(String filter)
+    public void setEarFilter(List filter)
     {
-        earFilterTextField.setText(filter != null ? filter : DEFAULT_EAR_FILTER);
+        earFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_EAR_FILTER);
     }
 
 
     /**
      * Returns the ear filter currently represented in this dialog.
      */
-    public String getEarFilter()
+    public List getEarFilter()
     {
         String filter = earFilterTextField.getText();
 
-        return filter.equals(DEFAULT_EAR_FILTER) ? null : filter;
+        return filter.equals(DEFAULT_EAR_FILTER) ? null : ListUtil.commaSeparatedList(filter);
     }
 
 
     /**
      * Sets the zip filter to be represented in this dialog.
      */
-    public void setZipFilter(String filter)
+    public void setZipFilter(List filter)
     {
-        zipFilterTextField.setText(filter != null ? filter : DEFAULT_ZIP_FILTER);
+        zipFilterTextField.setText(filter != null ? ListUtil.commaSeparatedString(filter) : DEFAULT_ZIP_FILTER);
     }
 
 
     /**
      * Returns the zip filter currently represented in this dialog.
      */
-    public String getZipFilter()
+    public List getZipFilter()
     {
         String filter = zipFilterTextField.getText();
 
-        return filter.equals(DEFAULT_ZIP_FILTER) ? null : filter;
+        return filter.equals(DEFAULT_ZIP_FILTER) ? null : ListUtil.commaSeparatedList(filter);
     }
 
 
diff --git a/src/proguard/gui/GUIResources.java b/src/proguard/gui/GUIResources.java
index 868de88..85d582c 100644
--- a/src/proguard/gui/GUIResources.java
+++ b/src/proguard/gui/GUIResources.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties
index 28568eb..86ab7a1 100644
--- a/src/proguard/gui/GUIResources.properties
+++ b/src/proguard/gui/GUIResources.properties
@@ -1,5 +1,5 @@
 # ProGuard -- shrinking, optimization, and obfuscation of Java class files.
-# Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+# Copyright (c) 2002-2009 Eric Lafortune (eric at graphics.cornell.edu)
 
 #
 # Tab names.
@@ -25,7 +25,7 @@ preverification = Preverification
 #
 # Panel titles.
 #
-welcome                       = Welcome to ProGuard, version 4.3
+welcome                       = Welcome to ProGuard, version 4.4
 options                       = Options
 keepAdditional                = Keep additional classes and class members
 keepNamesAdditional           = Keep additional class names and class member names
@@ -64,7 +64,7 @@ proGuardInfo = \
   \n\n\
   Distributed under the GNU General Public License.\
   \n\
-  Copyright (c) 2002-2008.
+  Copyright (c) 2002-2009.
 
 processingInfo = \
   You can now start processing your code, \
@@ -89,6 +89,7 @@ printUsage                       = Print usage
 optimize                         = Optimize
 allowAccessModification          = Allow access modification
 mergeInterfacesAggressively      = Merge interfaces aggressively
+optimizations                    = Optimizations
 optimizationPasses               = Optimization passes
 
 obfuscate                        = Obfuscate
@@ -99,11 +100,13 @@ classObfuscationDictionary       = Class obfuscation dictionary
 packageObfuscationDictionary     = Package obfuscation dictionary
 overloadAggressively             = Overload aggressively
 useUniqueClassMemberNames        = Use unique class member names
+keepPackageNames                 = Keep package names
 flattenPackageHierarchy          = Flatten package hierarchy
 repackageClasses                 = Repackage classes
 useMixedCaseClassNames           = Use mixed-case class names
 keepAttributes                   = Keep attributes
 renameSourceFileAttribute        = Rename SourceFile attribute
+adaptClassStrings                = Adapt class strings
 adaptResourceFileNames           = Adapt resource file names
 adaptResourceFileContents        = Adapt resource file contents
 
@@ -116,6 +119,7 @@ warn                             = Warn about possibly erronous input
 ignoreWarnings                   = Ignore warnings about possibly erronous input
 skipNonPublicLibraryClasses      = Skip non-public library classes
 skipNonPublicLibraryClassMembers = Skip non-public library class members
+keepDirectories                  = Keep directories
 forceProcessing                  = Force processing
 target                           = Target
 targets                          = 1.0,1.1,1.2,1.3,1.4,1.5,1.6
@@ -145,8 +149,14 @@ allowAccessModificationTip = \
 mergeInterfacesAggressivelyTip = \
   <html>Allow interfaces to be merged, even if their implementations don't implement all<br>\
   interface methods.  This is not allowed in the Java language, but it is allowed in bytecode.</html>
+optimizationsTip = \
+  Specify the types of optimizations to be performed.
+optimizationsFilterTip = \
+  A filter for the names of the optimizations to be performed.
+optimizationsSelectTip = \
+  Select from the currently available optimizations...
 optimizationPassesTip = \
-  The number of optimization passes to be performed.
+  Specify the number of optimization passes to be performed.
 
 obfuscateTip = \
   Obfuscate the names of the processed classes, fields, and methods.
@@ -166,19 +176,31 @@ overloadAggressivelyTip = \
 useUniqueClassMemberNamesTip = \
   <html>Make sure fields and methods get the same obfuscation mapping across classes, even<br>\
   if they are unrelated. This is advisable if the output is to be obfuscated incrementally.</html>
+keepPackageNamesTip = \
+  Keep the specified package names from being obfuscated.
+packageNamesTip = \
+  <html>An optional comma-separated list of package names,<br>\
+  e.g. <code>myapplication,mylibrary.**</code><br>\
+  Possible wildcards:\
+  <ul>\
+  <li><code>?</code> for any single character, except the package separator.\
+  <li><code>*</code> for any number of any characters, except the package separator.\
+  <li><code>**</code> for any number of any characters.\
+  </ul>\
+  The negator <code>!</code> is also supported.</html>
 flattenPackageHierarchyTip = \
   Move all packages that are renamed into the given parent package.
 repackageClassesTip = \
   Move all classes that are renamed into the given package.
 packageTip = \
-  The package name (optionally nothing, for the root package).
+  The optional package name.
 useMixedCaseClassNamesTip = \
   <html>Generate mixed-case obfucated class names. This will complicate unpacking<br>\
   the resulting jars on case-insensitive file systems, should that be necessary.</html>
 keepAttributesTip = \
   Keep the specified optional class file attributes.
 attributesTip = \
-  <html>A comma-separated list of class file attributes.\
+  <html>An optional comma-separated list of class file attributes.\
   <ul>\
   <li>"Exceptions,Innerclasses, Signature" are necessary if the output is to be used as a library.\
   <li>"Deprecated" is optional if the output is to be used as a library.\
@@ -192,11 +214,14 @@ renameSourceFileAttributeTip = \
   It will appear as the file name of the classes in stack traces.</html>
 sourceFileAttributeTip = \
   The replacement "SourceFile" string.
+adaptClassStringsTip = \
+  <html>Adapt string constants in the specified classes, based<br>\
+  on the obfuscated names of corresponding classes.</html>
 adaptResourceFileNamesTip = \
   <html>Rename the specified resource files, based on the<br>\
   obfuscated names of the corresponding class files.</html>
 adaptResourceFileContentsTip = \
-  <html>Adapt the contents of the given resource files, based<br>\
+  <html>Adapt the contents of the specified resource files, based<br>\
   on the obfuscated names of the processed classes.</html>
 fileNameFilterTip = \
   <html>A filter on file names,<br>\
@@ -206,7 +231,8 @@ fileNameFilterTip = \
   <li><code>?</code> for any single character, except the directory separator.\
   <li><code>*</code> for any number of any characters, except the directory separator.\
   <li><code>**</code> for any number of any characters.\
-  </ul></html>
+  </ul>\
+  The negator <code>!</code> is also supported.</html>
 
 preverifyTip = \
   Preverify the processed classes, for Java Micro Edition or for Java 6.
@@ -217,9 +243,13 @@ verboseTip = \
   Print out verbose messages while processing.
 noteTip = \
   Print out notes about special or unusual input.
+noteFilterTip = \
+  A filter matching classes for which no notes should be printed.
 warnTip = \
   <html>Print out warnings about possibly erronous input.<br>\
   <i>Only unset this option if you know what you're doing!</i></html>
+warnFilterTip = \
+  A filter matching classes for which no warnings should be printed.
 ignoreWarningsTip = \
   <html>Ignore any warnings about possibly erronous input.<br>\
   <i>Only set this option if you know what you're doing!</i></html>
@@ -229,6 +259,18 @@ skipNonPublicLibraryClassesTip = \
 skipNonPublicLibraryClassMembersTip = \
   <html>Skip reading non-public library fields and methods, for efficiency.<br>\
   You may have to unset this option if ProGuard complains about missing class members.</html>
+keepDirectoriesTip = \
+  Keep the specified directories in the output jars, wars, ears, zips, or directories.
+directoriesTip = \
+  <html>A filter on directory names,<br>\
+  e.g. <code>mydirectory1,mydirectory2/**</code><br>\
+  Possible wildcards:\
+  <ul>\
+  <li><code>?</code> for any single character, except the directory separator.\
+  <li><code>*</code> for any number of any characters, except the directory separator.\
+  <li><code>**</code> for any number of any characters.\
+  </ul>\
+  The negator <code>!</code> is also supported.</html>
 forceProcessingTip = \
   Always process the input, even if the output seems up to date.
 targetTip = \
@@ -287,6 +329,10 @@ allowObfuscationTip = \
 #
 # Further keep titles and labels.
 #
+specifyClasses = Specify classes and class members...
+specifyFields  = Specify fields...
+specifyMethods = Specify methods...
+
 comments                    = Comments
 access                      = Access
 required                    = Required
@@ -345,57 +391,125 @@ classNameTip = \
   <html>The class name, e.g. <code>myPackage.MyClass</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>?<code> for any single character, except the package separator.\
-  <li><code>*<code> for any number of any characters, except the package separator.\
-  <li><code>**<code> for any number of any characters.\
+  <li><code>?</code> for any single character, except the package separator.\
+  <li><code>*</code> for any number of any characters, except the package separator.\
+  <li><code>**</code> for any number of any characters.\
   </ul></html>
 classNamesTip = \
   <html>A regular expression to further constrain the class names,<br>\
   e.g. <code>myPackage1.MyClass,myPackage2.**</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>?<code> for any single character, except the package separator.\
-  <li><code>*<code> for any number of any characters, except the package separator.\
-  <li><code>**<code> for any number of any characters.\
-  </ul></html>
+  <li><code>?</code> for any single character, except the package separator.\
+  <li><code>*</code> for any number of any characters, except the package separator.\
+  <li><code>**</code> for any number of any characters.\
+  </ul>\
+  The negator <code>!</code> is also supported.</html>
 typeTip = \
   <html>The type, e.g. <code>int</code>, or <code>java.lang.String[]</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>%<code> for any primitive type.\
-  <li><code>?<code> for any single character, except the package separator.\
-  <li><code>*<code> for any number of any characters, except the package separator.\
-  <li><code>**<code> for any number of any characters.\
-  <li><code>***<code> (or empty) for any type.\
+  <li><code>%</code> for any primitive type.\
+  <li><code>?</code> for any single character, except the package separator.\
+  <li><code>*</code> for any number of any characters, except the package separator.\
+  <li><code>**</code> for any number of any characters.\
+  <li><code>***</code> (or empty) for any type.\
   </ul></html>
 fieldNameTip = \
   <html>The field name, e.g. <code>myField</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>?<code> for any single character.\
-  <li><code>*<code> for any number of any characters.\
+  <li><code>?</code> for any single character.\
+  <li><code>*</code> for any number of any characters.\
   </ul></html>
 methodNameTip = \
   <html>The method name, e.g. <code>myMethod</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>?<code> for any single character.\
-  <li><code>*<code> for any number of any characters.\
+  <li><code>?</code> for any single character.\
+  <li><code>*</code> for any number of any characters.\
   </ul></html>
 argumentTypes2Tip = \
   <html>The comma-separated list of argument types,<br>\
   e.g. <code>java.lang.String[],int,boolean</code><br>\
   Possible wildcards:\
   <ul>\
-  <li><code>%<code> for any primitive type.\
-  <li><code>?<code> for any single character, except the package separator.\
-  <li><code>*<code> for any number of any characters, except the package separator.\
-  <li><code>**<code> for any number of any characters.\
-  <li><code>***<code> for any type.\
-  <li><code>...<code> for any number of any arguments.\
+  <li><code>%</code> for any primitive type.\
+  <li><code>?</code> for any single character, except the package separator.\
+  <li><code>*</code> for any number of any characters, except the package separator.\
+  <li><code>**</code> for any number of any characters.\
+  <li><code>***</code> for any type.\
+  <li><code>...</code> for any number of any arguments.\
   </ul></html>
 
 #
+# Titles and labels corresponding to optimization options.
+#
+selectOptimizations = Select optimizations...
+
+field  = Field
+method = Method
+code   = Code
+
+class_marking_finalTip = \
+  Mark classes as final, whenever possible.
+class_merging_verticalTip = \
+  Merge classes vertically in the class hierarchy, whenever possible.
+class_merging_horizontalTip = \
+  Merge classes horizontally in the class hierarchy, whenever possible.
+field_removal_writeonlyTip = \
+  Remove write-only fields.
+field_marking_privateTip = \
+  Mark fields as private, whenever possible.
+field_propagation_valueTip = \
+  Propagate the values of fields across methods.
+method_marking_privateTip = \
+  Mark methods as private, whenever possible (devirtualization).
+method_marking_staticTip = \
+  Mark methods as static, whenever possible (devirtualization).
+method_marking_finalTip = \
+  Mark methods as final, whenever possible.
+method_removal_parameterTip = \
+  Remove unused method parameters.
+method_propagation_parameterTip = \
+  Propagate the values of method parameters from method invocations to \
+  the invoked methods.
+method_propagation_returnvalueTip = \
+  Propagate the values of method return values from methods to their \
+  invocations.
+method_inlining_shortTip = \
+  Inline short methods.
+method_inlining_uniqueTip = \
+  Inline methods that are only called once.
+method_inlining_tailrecursionTip = \
+  Simplify tail recursion calls, whenever possible.
+code_mergingTip = \
+  Merge identical blocks of code by modifying branch targets.
+code_simplification_variableTip = \
+  Perform peephole optimizations for variable loading and storing.
+code_simplification_arithmeticTip = \
+  Perform peephole optimizations for arithmetic instructions.
+code_simplification_castTip = \
+  Perform peephole optimizations for casting operations.
+code_simplification_fieldTip = \
+  Perform peephole optimizations for field loading and storing.
+code_simplification_branchTip = \
+  Perform peephole optimizations for branch instructions.
+code_simplification_advancedTip = \
+  Simplify code based on control flow analysis and data flow analysis.
+code_removal_advancedTip = \
+  Remove dead code based on control flow analysis and data flow analysis.
+code_removal_simpleTip = \
+  Remove dead code based on a simple control flow analysis.
+code_removal_variableTip = \
+  Remove unused variables from the local variable frame.
+code_removal_exceptionTip = \
+  Remove exceptions with empty catch blocks.
+code_allocation_variableTip = \
+  Optimize variable allocation on the local variable frame.
+
+
+#
 # File selection titles.
 #
 selectConfigurationFile         = Select a configuration file...
@@ -439,22 +553,24 @@ zipNameFilterTip = A filter on zip file names.
 #
 # Simple button texts.
 #
-previous = Previous
-next     = Next
-browse   = Browse...
-advanced = Advanced options
-basic    = Basic options
-ok       = Ok
-cancel   = Cancel
-
-add       = Add...
-addInput  = Add input...
-addOutput = Add output...
-edit      = Edit...
-filter    = Filter...
-remove    = Remove
-moveUp    = Move up
-moveDown  = Move down
+previous   = Previous
+next       = Next
+browse     = Browse...
+advanced   = Advanced options
+basic      = Basic options
+selectAll  = Select all
+selectNone = Select none
+ok         = Ok
+cancel     = Cancel
+
+add        = Add...
+addInput   = Add input...
+addOutput  = Add output...
+edit       = Edit...
+filter     = Filter...
+remove     = Remove
+moveUp     = Move up
+moveDown   = Move down
 
 moveToLibraries = Move to libraries
 moveToProgram   = Move to program
@@ -462,6 +578,8 @@ moveToProgram   = Move to program
 addField  = Add field...
 addMethod = Add method...
 
+select = Select...
+
 loadConfiguration = Load configuration...
 viewConfiguration = View configuration
 saveConfiguration = Save configuration...
diff --git a/src/proguard/gui/KeepSpecificationsPanel.java b/src/proguard/gui/KeepSpecificationsPanel.java
index 3a3d4b8..4c3c953 100644
--- a/src/proguard/gui/KeepSpecificationsPanel.java
+++ b/src/proguard/gui/KeepSpecificationsPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -26,7 +26,7 @@ import javax.swing.*;
 
 /**
  * This <code>ListPanel</code> allows the user to add, edit, move, and remove
- * KeepSpecification entries in a list.
+ * KeepClassSpecification entries in a list.
  *
  * @author Eric Lafortune
  */
@@ -60,7 +60,7 @@ final class KeepSpecificationsPanel extends ClassSpecificationsPanel
 
     protected ClassSpecification createClassSpecification()
     {
-        return new KeepSpecification(markClasses,
+        return new KeepClassSpecification(markClasses,
                                      markConditionally,
                                      allowShrinking,
                                      allowOptimization,
@@ -70,7 +70,7 @@ final class KeepSpecificationsPanel extends ClassSpecificationsPanel
 
     protected void setClassSpecification(ClassSpecification classSpecification)
     {
-        classSpecificationDialog.setKeepSpecification((KeepSpecification)classSpecification);
+        classSpecificationDialog.setKeepSpecification((KeepClassSpecification)classSpecification);
     }
 
 
diff --git a/src/proguard/gui/ListPanel.java b/src/proguard/gui/ListPanel.java
index e0ab998..0132340 100644
--- a/src/proguard/gui/ListPanel.java
+++ b/src/proguard/gui/ListPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/MemberSpecificationDialog.java b/src/proguard/gui/MemberSpecificationDialog.java
index 22f5b88..46a3f6f 100644
--- a/src/proguard/gui/MemberSpecificationDialog.java
+++ b/src/proguard/gui/MemberSpecificationDialog.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -75,7 +75,7 @@ final class MemberSpecificationDialog extends JDialog
 
     public MemberSpecificationDialog(JDialog owner, boolean isField)
     {
-        super(owner, true);
+        super(owner, msg(isField ? "specifyFields" : "specifyMethods"), true);
         setResizable(true);
 
         // Create some constraints that can be reused.
@@ -439,8 +439,8 @@ final class MemberSpecificationDialog extends JDialog
      * flags of the given keep option.
      */
     private void setMemberSpecificationRadioButtons(MemberSpecification memberSpecification,
-                                                         int            flag,
-                                                         JRadioButton[] radioButtons)
+                                                    int                 flag,
+                                                    JRadioButton[]      radioButtons)
     {
         if (radioButtons != null)
         {
diff --git a/src/proguard/gui/MemberSpecificationsPanel.java b/src/proguard/gui/MemberSpecificationsPanel.java
index 9fe5b9f..20b2f17 100644
--- a/src/proguard/gui/MemberSpecificationsPanel.java
+++ b/src/proguard/gui/MemberSpecificationsPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/MessageDialogRunnable.java b/src/proguard/gui/MessageDialogRunnable.java
index 00a02b3..e58f1c6 100644
--- a/src/proguard/gui/MessageDialogRunnable.java
+++ b/src/proguard/gui/MessageDialogRunnable.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -22,6 +22,7 @@ package proguard.gui;
 
 import javax.swing.*;
 import java.awt.*;
+import java.lang.reflect.InvocationTargetException;
 
 
 /**
@@ -46,10 +47,17 @@ final class MessageDialogRunnable implements Runnable
                                          String    title,
                                          int       messageType)
     {
-        SwingUtil.invokeAndWait(new MessageDialogRunnable(parentComponent,
-                                                          message,
-                                                          title,
-                                                          messageType));
+        try
+        {
+            SwingUtil.invokeAndWait(new MessageDialogRunnable(parentComponent,
+                                                              message,
+                                                              title,
+                                                              messageType));
+        }
+        catch (Exception e)
+        {
+            // Nothing.
+        }
     }
 
 
diff --git a/src/proguard/gui/OptimizationsDialog.java b/src/proguard/gui/OptimizationsDialog.java
new file mode 100644
index 0000000..044c338
--- /dev/null
+++ b/src/proguard/gui/OptimizationsDialog.java
@@ -0,0 +1,251 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.gui;
+
+import proguard.optimize.Optimizer;
+import proguard.util.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * This <code>JDialog</code> allows the user to enter a String.
+ *
+ * @author Eric Lafortune
+ */
+final class OptimizationsDialog extends JDialog
+{
+    /**
+     * Return value if the dialog is canceled (with the Cancel button or by
+     * closing the dialog window).
+     */
+    public static final int CANCEL_OPTION = 1;
+
+    /**
+     * Return value if the dialog is approved (with the Ok button).
+     */
+    public static final int APPROVE_OPTION = 0;
+
+
+    private final JCheckBox[] optimizationCheckBoxes = new JCheckBox[Optimizer.OPTIMIZATION_NAMES.length];
+
+    private int returnValue;
+
+
+    public OptimizationsDialog(JFrame owner)
+    {
+        super(owner, msg("selectOptimizations"), true);
+        setResizable(true);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints constraintsLast = new GridBagConstraints();
+        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLast.anchor    = GridBagConstraints.WEST;
+        constraintsLast.insets    = new Insets(1, 2, 1, 2);
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraintsLast.insets;
+
+        GridBagConstraints panelConstraints = new GridBagConstraints();
+        panelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        panelConstraints.fill      = GridBagConstraints.HORIZONTAL;
+        panelConstraints.weightx   = 1.0;
+        panelConstraints.weighty   = 0.0;
+        panelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        panelConstraints.insets    = constraintsLast.insets;
+
+        GridBagConstraints selectButtonConstraints = new GridBagConstraints();
+        selectButtonConstraints.weighty = 1.0;
+        selectButtonConstraints.anchor  = GridBagConstraints.SOUTHWEST;
+        selectButtonConstraints.insets  = new Insets(4, 4, 8, 4);
+
+        GridBagConstraints okButtonConstraints = new GridBagConstraints();
+        okButtonConstraints.weightx = 1.0;
+        okButtonConstraints.weighty = 1.0;
+        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
+        okButtonConstraints.insets  = selectButtonConstraints.insets;
+
+        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
+        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        cancelButtonConstraints.weighty   = 1.0;
+        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        cancelButtonConstraints.insets    = selectButtonConstraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+        // Create the optimizations panel.
+        JPanel optimizationsPanel     = new JPanel(layout);
+        JPanel optimizationSubpanel   = null;
+        String lastOptimizationPrefix = null;
+
+        for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++)
+        {
+            String optimizationName = Optimizer.OPTIMIZATION_NAMES[index];
+
+            String optimizationPrefix = optimizationName.substring(0, optimizationName.indexOf('/'));
+
+            if (optimizationSubpanel == null || !optimizationPrefix.equals(lastOptimizationPrefix))
+            {
+                // Create a new keep subpanel and add it.
+                optimizationSubpanel = new JPanel(layout);
+                optimizationSubpanel.setBorder(BorderFactory.createTitledBorder(etchedBorder, msg(optimizationPrefix)));
+                optimizationsPanel.add(optimizationSubpanel, panelConstraints);
+
+                lastOptimizationPrefix = optimizationPrefix;
+            }
+
+            JCheckBox optimizationCheckBox = new JCheckBox(optimizationName);
+            optimizationCheckBoxes[index] = optimizationCheckBox;
+
+            optimizationSubpanel.add(tip(optimizationCheckBox, optimizationName.replace('/', '_')+"Tip"), constraintsLastStretch);
+        }
+
+        // Create the Select All button.
+        JButton selectAllButton = new JButton(msg("selectAll"));
+        selectAllButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                for (int index = 0; index < optimizationCheckBoxes.length; index++)
+                {
+                    optimizationCheckBoxes[index].setSelected(true);
+                }
+            }
+        });
+
+        // Create the Select All button.
+        JButton selectNoneButton = new JButton(msg("selectNone"));
+        selectNoneButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                for (int index = 0; index < optimizationCheckBoxes.length; index++)
+                {
+                    optimizationCheckBoxes[index].setSelected(false);
+                }
+            }
+        });
+
+        // Create the Ok button.
+        JButton okButton = new JButton(msg("ok"));
+        okButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                returnValue = APPROVE_OPTION;
+                hide();
+            }
+        });
+
+        // Create the Cancel button.
+        JButton cancelButton = new JButton(msg("cancel"));
+        cancelButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                hide();
+            }
+        });
+
+        // Add all panels to the main panel.
+        optimizationsPanel.add(selectAllButton,  selectButtonConstraints);
+        optimizationsPanel.add(selectNoneButton, selectButtonConstraints);
+        optimizationsPanel.add(okButton,         okButtonConstraints);
+        optimizationsPanel.add(cancelButton,     cancelButtonConstraints);
+
+        getContentPane().add(new JScrollPane(optimizationsPanel));
+    }
+
+
+    /**
+     * Sets the initial optimization filter to be used by the dialog.
+     */
+    public void setFilter(String optimizations)
+    {
+        StringMatcher filter = optimizations != null && optimizations.length() > 0 ?
+            new ListParser(new NameParser()).parse(optimizations) :
+            new FixedStringMatcher("");
+
+        for (int index = 0; index < Optimizer.OPTIMIZATION_NAMES.length; index++)
+        {
+            optimizationCheckBoxes[index].setSelected(filter.matches(Optimizer.OPTIMIZATION_NAMES[index]));
+        }
+    }
+
+
+    /**
+     * Returns the optimization filter composed from the settings in the dialog.
+     */
+    public String getFilter()
+    {
+        return new FilterBuilder(optimizationCheckBoxes, '/').buildFilter();
+    }
+
+
+    /**
+     * Shows this dialog. This method only returns when the dialog is closed.
+     *
+     * @return <code>CANCEL_OPTION</code> or <code>APPROVE_OPTION</code>,
+     *         depending on the choice of the user.
+     */
+    public int showDialog()
+    {
+        returnValue = CANCEL_OPTION;
+
+        // Open the dialog in the right place, then wait for it to be closed,
+        // one way or another.
+        pack();
+        setLocationRelativeTo(getOwner());
+        show();
+
+        return returnValue;
+    }
+
+
+    /**
+     * Attaches the tool tip from the GUI resources that corresponds to the
+     * given key, to the given component.
+     */
+    private static JComponent tip(JComponent component, String messageKey)
+    {
+        component.setToolTipText(msg(messageKey));
+
+        return component;
+    }
+
+
+    /**
+     * Returns the message from the GUI resources that corresponds to the given
+     * key.
+     */
+    private static String msg(String messageKey)
+    {
+         return GUIResources.getMessage(messageKey);
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java
index da9e74f..f27d698 100644
--- a/src/proguard/gui/ProGuardGUI.java
+++ b/src/proguard/gui/ProGuardGUI.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -33,6 +33,7 @@ import java.io.*;
 import java.net.URL;
 import java.util.*;
 import java.util.List;
+import java.lang.reflect.InvocationTargetException;
 
 
 /**
@@ -44,10 +45,11 @@ public class ProGuardGUI extends JFrame
 {
     private static final String NO_SPLASH_OPTION = "-nosplash";
 
-    private static final String TITLE_IMAGE_FILE          = "vtitle.gif";
+    private static final String TITLE_IMAGE_FILE          = "vtitle.png";
     private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro";
     private static final String DEFAULT_CONFIGURATION     = "default.pro";
 
+    private static final String OPTIMIZATIONS_DEFAULT                = "*";
     private static final String KEEP_ATTRIBUTE_DEFAULT               = "Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod";
     private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT        = "SourceFile";
     private static final String ADAPT_RESOURCE_FILE_NAMES_DEFAULT    = "**.properties";
@@ -65,15 +67,15 @@ public class ProGuardGUI extends JFrame
     private final ClassPathPanel programPanel = new ClassPathPanel(this, true);
     private final ClassPathPanel libraryPanel = new ClassPathPanel(this, false);
 
-    private       KeepSpecification[] boilerplateKeep;
-    private final JCheckBox[]         boilerplateKeepCheckBoxes;
-    private final JTextField[]        boilerplateKeepTextFields;
+    private       KeepClassSpecification[] boilerplateKeep;
+    private final JCheckBox[]              boilerplateKeepCheckBoxes;
+    private final JTextField[]             boilerplateKeepTextFields;
 
     private final KeepSpecificationsPanel additionalKeepPanel = new KeepSpecificationsPanel(this, true, false, false, false, false);
 
-    private       KeepSpecification[] boilerplateKeepNames;
-    private final JCheckBox[]         boilerplateKeepNamesCheckBoxes;
-    private final JTextField[]        boilerplateKeepNamesTextFields;
+    private       KeepClassSpecification[] boilerplateKeepNames;
+    private final JCheckBox[]              boilerplateKeepNamesCheckBoxes;
+    private final JTextField[]             boilerplateKeepNamesTextFields;
 
     private final KeepSpecificationsPanel additionalKeepNamesPanel = new KeepSpecificationsPanel(this, true, false, true, false, false);
 
@@ -90,8 +92,10 @@ public class ProGuardGUI extends JFrame
     private final JCheckBox optimizeCheckBox                    = new JCheckBox(msg("optimize"));
     private final JCheckBox allowAccessModificationCheckBox     = new JCheckBox(msg("allowAccessModification"));
     private final JCheckBox mergeInterfacesAggressivelyCheckBox = new JCheckBox(msg("mergeInterfacesAggressively"));
+    private final JLabel    optimizationsLabel                  = new JLabel(msg("optimizations"));
     private final JLabel    optimizationPassesLabel             = new JLabel(msg("optimizationPasses"));
-    private final JSpinner  optimizationPassesSpinner           = new JSpinner(new SpinnerNumberModel(1, 1, 9, 1));
+
+    private final JSpinner optimizationPassesSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 9, 1));
 
     private final JCheckBox obfuscateCheckBox                    = new JCheckBox(msg("obfuscate"));
     private final JCheckBox printMappingCheckBox                 = new JCheckBox(msg("printMapping"));
@@ -102,10 +106,12 @@ public class ProGuardGUI extends JFrame
     private final JCheckBox overloadAggressivelyCheckBox         = new JCheckBox(msg("overloadAggressively"));
     private final JCheckBox useUniqueClassMemberNamesCheckBox    = new JCheckBox(msg("useUniqueClassMemberNames"));
     private final JCheckBox useMixedCaseClassNamesCheckBox       = new JCheckBox(msg("useMixedCaseClassNames"));
+    private final JCheckBox keepPackageNamesCheckBox             = new JCheckBox(msg("keepPackageNames"));
     private final JCheckBox flattenPackageHierarchyCheckBox      = new JCheckBox(msg("flattenPackageHierarchy"));
     private final JCheckBox repackageClassesCheckBox             = new JCheckBox(msg("repackageClasses"));
     private final JCheckBox keepAttributesCheckBox               = new JCheckBox(msg("keepAttributes"));
     private final JCheckBox newSourceFileAttributeCheckBox       = new JCheckBox(msg("renameSourceFileAttribute"));
+    private final JCheckBox adaptClassStringsCheckBox            = new JCheckBox(msg("adaptClassStrings"));
     private final JCheckBox adaptResourceFileNamesCheckBox       = new JCheckBox(msg("adaptResourceFileNames"));
     private final JCheckBox adaptResourceFileContentsCheckBox    = new JCheckBox(msg("adaptResourceFileContents"));
 
@@ -113,31 +119,38 @@ public class ProGuardGUI extends JFrame
     private final JCheckBox microEditionCheckBox = new JCheckBox(msg("microEdition"));
     private final JCheckBox targetCheckBox       = new JCheckBox(msg("target"));
 
-    private final JComboBox  targetComboBox = new JComboBox(ListUtil.commaSeparatedList(msg("targets")).toArray());
+    private final JComboBox targetComboBox = new JComboBox(ListUtil.commaSeparatedList(msg("targets")).toArray());
 
     private final JCheckBox verboseCheckBox                          = new JCheckBox(msg("verbose"));
-    private final JCheckBox ignoreWarningsCheckBox                   = new JCheckBox(msg("ignoreWarnings"));
-    private final JCheckBox warnCheckBox                             = new JCheckBox(msg("warn"));
     private final JCheckBox noteCheckBox                             = new JCheckBox(msg("note"));
+    private final JCheckBox warnCheckBox                             = new JCheckBox(msg("warn"));
+    private final JCheckBox ignoreWarningsCheckBox                   = new JCheckBox(msg("ignoreWarnings"));
     private final JCheckBox skipNonPublicLibraryClassesCheckBox      = new JCheckBox(msg("skipNonPublicLibraryClasses"));
     private final JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers"));
+    private final JCheckBox keepDirectoriesCheckBox                  = new JCheckBox(msg("keepDirectories"));
     private final JCheckBox forceProcessingCheckBox                  = new JCheckBox(msg("forceProcessing"));
     private final JCheckBox printSeedsCheckBox                       = new JCheckBox(msg("printSeeds"));
     private final JCheckBox printConfigurationCheckBox               = new JCheckBox(msg("printConfiguration"));
     private final JCheckBox dumpCheckBox                             = new JCheckBox(msg("dump"));
 
     private final JTextField printUsageTextField                   = new JTextField(40);
+    private final JTextField optimizationsTextField                = new JTextField(40);
     private final JTextField printMappingTextField                 = new JTextField(40);
     private final JTextField applyMappingTextField                 = new JTextField(40);
     private final JTextField obfuscationDictionaryTextField        = new JTextField(40);
     private final JTextField classObfuscationDictionaryTextField   = new JTextField(40);
     private final JTextField packageObfuscationDictionaryTextField = new JTextField(40);
+    private final JTextField keepPackageNamesTextField             = new JTextField(40);
     private final JTextField flattenPackageHierarchyTextField      = new JTextField(40);
     private final JTextField repackageClassesTextField             = new JTextField(40);
     private final JTextField keepAttributesTextField               = new JTextField(40);
     private final JTextField newSourceFileAttributeTextField       = new JTextField(40);
+    private final JTextField adaptClassStringsTextField            = new JTextField(40);
     private final JTextField adaptResourceFileNamesTextField       = new JTextField(40);
     private final JTextField adaptResourceFileContentsTextField    = new JTextField(40);
+    private final JTextField noteTextField                         = new JTextField(40);
+    private final JTextField warnTextField                         = new JTextField(40);
+    private final JTextField keepDirectoriesTextField              = new JTextField(40);
     private final JTextField printSeedsTextField                   = new JTextField(40);
     private final JTextField printConfigurationTextField           = new JTextField(40);
     private final JTextField dumpTextField                         = new JTextField(40);
@@ -382,6 +395,8 @@ public class ProGuardGUI extends JFrame
         obfuscationOptionsPanel.add(tip(overloadAggressivelyCheckBox,            "overloadAggressivelyTip"),         constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(useUniqueClassMemberNamesCheckBox,       "useUniqueClassMemberNamesTip"),    constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(useMixedCaseClassNamesCheckBox,          "useMixedCaseClassNamesTip"),       constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(keepPackageNamesCheckBox,                "keepPackageNamesTip"),             constraints);
+        obfuscationOptionsPanel.add(tip(keepPackageNamesTextField,               "packageNamesTip"),                 constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(flattenPackageHierarchyCheckBox,         "flattenPackageHierarchyTip"),      constraints);
         obfuscationOptionsPanel.add(tip(flattenPackageHierarchyTextField,        "packageTip"),                      constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(repackageClassesCheckBox,                "repackageClassesTip"),             constraints);
@@ -390,6 +405,8 @@ public class ProGuardGUI extends JFrame
         obfuscationOptionsPanel.add(tip(keepAttributesTextField,                 "attributesTip"),                   constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(newSourceFileAttributeCheckBox,          "renameSourceFileAttributeTip"),    constraints);
         obfuscationOptionsPanel.add(tip(newSourceFileAttributeTextField,         "sourceFileAttributeTip"),          constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(adaptClassStringsCheckBox,               "adaptClassStringsTip"),            constraints);
+        obfuscationOptionsPanel.add(tip(adaptClassStringsTextField,              "classNamesTip"),                   constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(adaptResourceFileNamesCheckBox,          "adaptResourceFileNamesTip"),       constraints);
         obfuscationOptionsPanel.add(tip(adaptResourceFileNamesTextField,         "fileNameFilterTip"),               constraintsLastStretch);
         obfuscationOptionsPanel.add(tip(adaptResourceFileContentsCheckBox,       "adaptResourceFileContentsTip"),    constraints);
@@ -412,9 +429,15 @@ public class ProGuardGUI extends JFrame
         JPanel optimizationOptionsPanel = new JPanel(layout);
         addBorder(optimizationOptionsPanel, "options");
 
+        JButton optimizationsButton =
+            createOptimizationsButton(optimizationsTextField);
+
         optimizationOptionsPanel.add(tip(optimizeCheckBox,                    "optimizeTip"),                    constraintsLastStretch);
         optimizationOptionsPanel.add(tip(allowAccessModificationCheckBox,     "allowAccessModificationTip"),     constraintsLastStretch);
         optimizationOptionsPanel.add(tip(mergeInterfacesAggressivelyCheckBox, "mergeInterfacesAggressivelyTip"), constraintsLastStretch);
+        optimizationOptionsPanel.add(tip(optimizationsLabel,                  "optimizationsTip"),               constraints);
+        optimizationOptionsPanel.add(tip(optimizationsTextField,              "optimizationsFilterTip"),         constraintsStretch);
+        optimizationOptionsPanel.add(tip(optimizationsButton,                 "optimizationsSelectTip"),         constraintsLast);
         optimizationOptionsPanel.add(tip(optimizationPassesLabel,             "optimizationPassesTip"),          constraints);
         optimizationOptionsPanel.add(tip(optimizationPassesSpinner,           "optimizationPassesTip"),          constraintsLast);
 
@@ -454,11 +477,15 @@ public class ProGuardGUI extends JFrame
         addBorder(consistencyPanel, "consistencyAndCorrectness");
 
         consistencyPanel.add(tip(verboseCheckBox,                          "verboseTip"),                          constraintsLastStretch);
-        consistencyPanel.add(tip(noteCheckBox,                             "noteTip"),                             constraintsLastStretch);
-        consistencyPanel.add(tip(warnCheckBox,                             "warnTip"),                             constraintsLastStretch);
+        consistencyPanel.add(tip(noteCheckBox,                             "noteTip"),                             constraints);
+        consistencyPanel.add(tip(noteTextField,                            "noteFilterTip"),                             constraintsLastStretch);
+        consistencyPanel.add(tip(warnCheckBox,                             "warnTip"),                             constraints);
+        consistencyPanel.add(tip(warnTextField,                            "warnFilterTip"),                             constraintsLastStretch);
         consistencyPanel.add(tip(ignoreWarningsCheckBox,                   "ignoreWarningsTip"),                   constraintsLastStretch);
         consistencyPanel.add(tip(skipNonPublicLibraryClassesCheckBox,      "skipNonPublicLibraryClassesTip"),      constraintsLastStretch);
         consistencyPanel.add(tip(skipNonPublicLibraryClassMembersCheckBox, "skipNonPublicLibraryClassMembersTip"), constraintsLastStretch);
+        consistencyPanel.add(tip(keepDirectoriesCheckBox,                  "keepDirectoriesTip"),                  constraints);
+        consistencyPanel.add(tip(keepDirectoriesTextField,                 "directoriesTip"),                      constraintsLastStretch);
         consistencyPanel.add(tip(forceProcessingCheckBox,                  "forceProcessingTip"),                  constraintsLastStretch);
         consistencyPanel.add(tip(printSeedsCheckBox,                       "printSeedsTip"),                       constraints);
         consistencyPanel.add(tip(printSeedsTextField,                      "outputFileTip"),                       constraintsStretch);
@@ -669,40 +696,40 @@ public class ProGuardGUI extends JFrame
      * Returns an array containing the ClassSpecifications instances with
      * matching flags.
      */
-    private KeepSpecification[] extractKeepSpecifications(List    keepSpecifications,
-                                                          boolean allowShrinking,
-                                                          boolean allowObfuscation)
+    private KeepClassSpecification[] extractKeepSpecifications(List    keepSpecifications,
+                                                               boolean allowShrinking,
+                                                               boolean allowObfuscation)
     {
         List matches = new ArrayList();
 
         for (int index = 0; index < keepSpecifications.size(); index++)
         {
-            KeepSpecification keepSpecification = (KeepSpecification)keepSpecifications.get(index);
-            if (keepSpecification.allowShrinking   == allowShrinking &&
-                keepSpecification.allowObfuscation == allowObfuscation)
+            KeepClassSpecification keepClassSpecification = (KeepClassSpecification)keepSpecifications.get(index);
+            if (keepClassSpecification.allowShrinking   == allowShrinking &&
+                keepClassSpecification.allowObfuscation == allowObfuscation)
             {
-                 matches.add(keepSpecification);
+                 matches.add(keepClassSpecification);
             }
         }
 
-        KeepSpecification[] matchingKeepSpecifications = new KeepSpecification[matches.size()];
-        matches.toArray(matchingKeepSpecifications);
+        KeepClassSpecification[] matchingKeepClassSpecifications = new KeepClassSpecification[matches.size()];
+        matches.toArray(matchingKeepClassSpecifications);
 
-        return matchingKeepSpecifications;
+        return matchingKeepClassSpecifications;
     }
 
 
     /**
      * Returns an array containing the ClassSpecification instances of the
-     * given array of KeepSpecification instances.
+     * given array of KeepClassSpecification instances.
      */
-    private ClassSpecification[] extractClassSpecifications(KeepSpecification[] keepSpecifications)
+    private ClassSpecification[] extractClassSpecifications(KeepClassSpecification[] keepClassSpecifications)
     {
-        ClassSpecification[] classSpecifications = new ClassSpecification[keepSpecifications.length];
+        ClassSpecification[] classSpecifications = new ClassSpecification[keepClassSpecifications.length];
 
         for (int index = 0; index < classSpecifications.length; index++)
         {
-            classSpecifications[index] = keepSpecifications[index];
+            classSpecifications[index] = keepClassSpecifications[index];
         }
 
         return classSpecifications;
@@ -840,12 +867,14 @@ public class ProGuardGUI extends JFrame
         {
             public void actionPerformed(ActionEvent e)
             {
+                // Update the file chooser.
                 fileChooser.setDialogTitle(title);
                 fileChooser.setSelectedFile(new File(textField.getText()));
 
                 int returnVal = fileChooser.showDialog(ProGuardGUI.this, msg("ok"));
                 if (returnVal == JFileChooser.APPROVE_OPTION)
                 {
+                    // Update the text field.
                     textField.setText(fileChooser.getSelectedFile().getPath());
                 }
             }
@@ -855,6 +884,31 @@ public class ProGuardGUI extends JFrame
     }
 
 
+    protected JButton createOptimizationsButton(final JTextField textField)
+    {
+        final OptimizationsDialog optimizationsDialog = new OptimizationsDialog(ProGuardGUI.this);
+
+        JButton optimizationsButton = new JButton(msg("select"));
+        optimizationsButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                // Update the dialog.
+                optimizationsDialog.setFilter(textField.getText());
+
+                int returnValue = optimizationsDialog.showDialog();
+                if (returnValue == OptimizationsDialog.APPROVE_OPTION)
+                {
+                    // Update the text field.
+                    textField.setText(optimizationsDialog.getFilter());
+                }
+            }
+        });
+
+        return optimizationsButton;
+    }
+
+
     /**
      * Sets the preferred sizes of the given components to the maximum of their
      * current preferred sizes.
@@ -945,12 +999,12 @@ public class ProGuardGUI extends JFrame
 
         // Set up the other options.
         shrinkCheckBox                          .setSelected(configuration.shrink);
-        printUsageCheckBox                      .setSelected(configuration.printUsage              != null);
+        printUsageCheckBox                      .setSelected(configuration.printUsage != null);
 
         optimizeCheckBox                        .setSelected(configuration.optimize);
-        optimizationPassesSpinner.getModel()    .setValue(new Integer(configuration.optimizationPasses));
         allowAccessModificationCheckBox         .setSelected(configuration.allowAccessModification);
         mergeInterfacesAggressivelyCheckBox     .setSelected(configuration.mergeInterfacesAggressively);
+        optimizationPassesSpinner.getModel()    .setValue(new Integer(configuration.optimizationPasses));
 
         obfuscateCheckBox                       .setSelected(configuration.obfuscate);
         printMappingCheckBox                    .setSelected(configuration.printMapping                 != null);
@@ -961,10 +1015,12 @@ public class ProGuardGUI extends JFrame
         overloadAggressivelyCheckBox            .setSelected(configuration.overloadAggressively);
         useUniqueClassMemberNamesCheckBox       .setSelected(configuration.useUniqueClassMemberNames);
         useMixedCaseClassNamesCheckBox          .setSelected(configuration.useMixedCaseClassNames);
+        keepPackageNamesCheckBox                .setSelected(configuration.keepPackageNames             != null);
         flattenPackageHierarchyCheckBox         .setSelected(configuration.flattenPackageHierarchy      != null);
         repackageClassesCheckBox                .setSelected(configuration.repackageClasses             != null);
         keepAttributesCheckBox                  .setSelected(configuration.keepAttributes               != null);
         newSourceFileAttributeCheckBox          .setSelected(configuration.newSourceFileAttribute       != null);
+        adaptClassStringsCheckBox               .setSelected(configuration.adaptClassStrings            != null);
         adaptResourceFileNamesCheckBox          .setSelected(configuration.adaptResourceFileNames       != null);
         adaptResourceFileContentsCheckBox       .setSelected(configuration.adaptResourceFileContents    != null);
 
@@ -973,26 +1029,33 @@ public class ProGuardGUI extends JFrame
         targetCheckBox                          .setSelected(configuration.targetClassVersion != 0);
 
         verboseCheckBox                         .setSelected(configuration.verbose);
-        noteCheckBox                            .setSelected(configuration.note);
-        warnCheckBox                            .setSelected(configuration.warn);
+        noteCheckBox                            .setSelected(configuration.note == null || !configuration.note.isEmpty());
+        warnCheckBox                            .setSelected(configuration.warn == null || !configuration.warn.isEmpty());
         ignoreWarningsCheckBox                  .setSelected(configuration.ignoreWarnings);
         skipNonPublicLibraryClassesCheckBox     .setSelected(configuration.skipNonPublicLibraryClasses);
         skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers);
+        keepDirectoriesCheckBox                 .setSelected(configuration.keepDirectories    != null);
         forceProcessingCheckBox                 .setSelected(configuration.lastModified == Long.MAX_VALUE);
-        printSeedsCheckBox                      .setSelected(configuration.printSeeds              != null);
-        printConfigurationCheckBox              .setSelected(configuration.printConfiguration      != null);
-        dumpCheckBox                            .setSelected(configuration.dump                    != null);
+        printSeedsCheckBox                      .setSelected(configuration.printSeeds         != null);
+        printConfigurationCheckBox              .setSelected(configuration.printConfiguration != null);
+        dumpCheckBox                            .setSelected(configuration.dump               != null);
 
         printUsageTextField                     .setText(fileName(configuration.printUsage));
+        optimizationsTextField                  .setText(configuration.optimizations             == null ? OPTIMIZATIONS_DEFAULT                : ListUtil.commaSeparatedString(configuration.optimizations));
         printMappingTextField                   .setText(fileName(configuration.printMapping));
         applyMappingTextField                   .setText(fileName(configuration.applyMapping));
         obfuscationDictionaryTextField          .setText(fileName(configuration.obfuscationDictionary));
+        keepPackageNamesTextField               .setText(configuration.keepPackageNames          == null ? ""                                   : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.keepPackageNames)));
         flattenPackageHierarchyTextField        .setText(configuration.flattenPackageHierarchy);
         repackageClassesTextField               .setText(configuration.repackageClasses);
         keepAttributesTextField                 .setText(configuration.keepAttributes            == null ? KEEP_ATTRIBUTE_DEFAULT               : ListUtil.commaSeparatedString(configuration.keepAttributes));
         newSourceFileAttributeTextField         .setText(configuration.newSourceFileAttribute    == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT        : configuration.newSourceFileAttribute);
+        adaptClassStringsTextField              .setText(configuration.adaptClassStrings         == null ? ""                                   : ClassUtil.externalClassName(ListUtil.commaSeparatedString(configuration.adaptClassStrings)));
         adaptResourceFileNamesTextField         .setText(configuration.adaptResourceFileNames    == null ? ADAPT_RESOURCE_FILE_NAMES_DEFAULT    : ListUtil.commaSeparatedString(configuration.adaptResourceFileNames));
         adaptResourceFileContentsTextField      .setText(configuration.adaptResourceFileContents == null ? ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT : ListUtil.commaSeparatedString(configuration.adaptResourceFileContents));
+        noteTextField                           .setText(ListUtil.commaSeparatedString(configuration.note));
+        warnTextField                           .setText(ListUtil.commaSeparatedString(configuration.warn));
+        keepDirectoriesTextField                .setText(ListUtil.commaSeparatedString(configuration.keepDirectories));
         printSeedsTextField                     .setText(fileName(configuration.printSeeds));
         printConfigurationTextField             .setText(fileName(configuration.printConfiguration));
         dumpTextField                           .setText(fileName(configuration.dump));
@@ -1098,43 +1161,47 @@ public class ProGuardGUI extends JFrame
 
         // Get the other options.
         configuration.shrink                           = shrinkCheckBox                          .isSelected();
-        configuration.printUsage                       = printUsageCheckBox                      .isSelected() ? new File(printUsageTextField                       .getText()) : null;
+        configuration.printUsage                       = printUsageCheckBox                      .isSelected() ? new File(printUsageTextField                                         .getText()) : null;
 
         configuration.optimize                         = optimizeCheckBox                        .isSelected();
-        configuration.optimizationPasses               = ((SpinnerNumberModel)optimizationPassesSpinner.getModel()).getNumber().intValue();
         configuration.allowAccessModification          = allowAccessModificationCheckBox         .isSelected();
         configuration.mergeInterfacesAggressively      = mergeInterfacesAggressivelyCheckBox     .isSelected();
+        configuration.optimizations                    = optimizationsTextField.getText().length() > 1 ?         ListUtil.commaSeparatedList(optimizationsTextField                   .getText()) : null;
+        configuration.optimizationPasses               = ((SpinnerNumberModel)optimizationPassesSpinner.getModel()).getNumber().intValue();
 
         configuration.obfuscate                        = obfuscateCheckBox                       .isSelected();
-        configuration.printMapping                     = printMappingCheckBox                    .isSelected() ? new File(printMappingTextField                                .getText()) : null;
-        configuration.applyMapping                     = applyMappingCheckBox                    .isSelected() ? new File(applyMappingTextField                                .getText()) : null;
-        configuration.obfuscationDictionary            = obfuscationDictionaryCheckBox           .isSelected() ? new File(obfuscationDictionaryTextField                       .getText()) : null;
-        configuration.classObfuscationDictionary       = classObfuscationDictionaryCheckBox      .isSelected() ? new File(classObfuscationDictionaryTextField                  .getText()) : null;
-        configuration.packageObfuscationDictionary     = packageObfuscationDictionaryCheckBox    .isSelected() ? new File(packageObfuscationDictionaryTextField                .getText()) : null;
+        configuration.printMapping                     = printMappingCheckBox                    .isSelected() ? new File(printMappingTextField                                       .getText()) : null;
+        configuration.applyMapping                     = applyMappingCheckBox                    .isSelected() ? new File(applyMappingTextField                                       .getText()) : null;
+        configuration.obfuscationDictionary            = obfuscationDictionaryCheckBox           .isSelected() ? new File(obfuscationDictionaryTextField                              .getText()) : null;
+        configuration.classObfuscationDictionary       = classObfuscationDictionaryCheckBox      .isSelected() ? new File(classObfuscationDictionaryTextField                         .getText()) : null;
+        configuration.packageObfuscationDictionary     = packageObfuscationDictionaryCheckBox    .isSelected() ? new File(packageObfuscationDictionaryTextField                       .getText()) : null;
         configuration.overloadAggressively             = overloadAggressivelyCheckBox            .isSelected();
         configuration.useUniqueClassMemberNames        = useUniqueClassMemberNamesCheckBox       .isSelected();
         configuration.useMixedCaseClassNames           = useMixedCaseClassNamesCheckBox          .isSelected();
-        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;
-        configuration.adaptResourceFileContents        = adaptResourceFileContentsCheckBox       .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileContentsTextField.getText()) : null;
+        configuration.keepPackageNames                 = keepPackageNamesCheckBox                .isSelected() ? keepPackageNamesTextField.getText().length()  > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepPackageNamesTextField.getText()))  : new ArrayList() : 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.adaptClassStrings                = adaptClassStringsCheckBox               .isSelected() ? adaptClassStringsTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(adaptClassStringsTextField.getText())) : new ArrayList() : null;
+        configuration.adaptResourceFileNames           = adaptResourceFileNamesCheckBox          .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileNamesTextField          .getText()) : null;
+        configuration.adaptResourceFileContents        = adaptResourceFileContentsCheckBox       .isSelected() ? ListUtil.commaSeparatedList(adaptResourceFileContentsTextField       .getText()) : null;
 
         configuration.preverify                        = preverifyCheckBox                       .isSelected();
         configuration.microEdition                     = microEditionCheckBox                    .isSelected();
         configuration.targetClassVersion               = targetCheckBox                          .isSelected() ? ClassUtil.internalClassVersion(targetComboBox.getSelectedItem().toString()) : 0;
 
         configuration.verbose                          = verboseCheckBox                         .isSelected();
-        configuration.note                             = noteCheckBox                            .isSelected();
-        configuration.warn                             = warnCheckBox                            .isSelected();
+        configuration.note                             = noteCheckBox                            .isSelected() ? noteTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(noteTextField.getText())) : null : new ArrayList();
+        configuration.warn                             = warnCheckBox                            .isSelected() ? warnTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(warnTextField.getText())) : null : new ArrayList();
         configuration.ignoreWarnings                   = ignoreWarningsCheckBox                  .isSelected();
         configuration.skipNonPublicLibraryClasses      = skipNonPublicLibraryClassesCheckBox     .isSelected();
         configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected();
+        configuration.keepDirectories                  = keepDirectoriesCheckBox                 .isSelected() ? keepDirectoriesTextField.getText().length() > 0 ? ListUtil.commaSeparatedList(ClassUtil.internalClassName(keepDirectoriesTextField.getText())) : new ArrayList() : null;
         configuration.lastModified                     = forceProcessingCheckBox                 .isSelected() ? Long.MAX_VALUE : System.currentTimeMillis();
-        configuration.printSeeds                       = printSeedsCheckBox                      .isSelected() ? new File(printSeedsTextField                                .getText()) : null;
-        configuration.printConfiguration               = printConfigurationCheckBox              .isSelected() ? new File(printConfigurationTextField                        .getText()) : null;
-        configuration.dump                             = dumpCheckBox                            .isSelected() ? new File(dumpTextField                                      .getText()) : null;
+        configuration.printSeeds                       = printSeedsCheckBox                      .isSelected() ? new File(printSeedsTextField                                         .getText()) : null;
+        configuration.printConfiguration               = printConfigurationCheckBox              .isSelected() ? new File(printConfigurationTextField                                 .getText()) : null;
+        configuration.dump                             = dumpCheckBox                            .isSelected() ? new File(dumpTextField                                               .getText()) : null;
 
         return configuration;
     }
@@ -1179,12 +1246,12 @@ public class ProGuardGUI extends JFrame
 
         for (int index = 0; index < keepSpecifications.size(); index++)
         {
-            KeepSpecification keepSpecification =
-                (KeepSpecification)keepSpecifications.get(index);
+            KeepClassSpecification keepClassSpecification =
+                (KeepClassSpecification)keepSpecifications.get(index);
 
-            if (keepSpecification.allowShrinking == allowShrinking)
+            if (keepClassSpecification.allowShrinking == allowShrinking)
             {
-                filteredKeepSpecifications.add(keepSpecification);
+                filteredKeepSpecifications.add(keepClassSpecification);
             }
         }
 
@@ -1198,7 +1265,7 @@ public class ProGuardGUI extends JFrame
      * matching keep specifications, and removes the matching keep
      * specifications as a side effect.
      */
-    private String findMatchingKeepSpecifications(KeepSpecification keepSpecificationTemplate,
+    private String findMatchingKeepSpecifications(KeepClassSpecification keepClassSpecificationTemplate,
                                                   List              keepSpecifications)
     {
         if (keepSpecifications == null)
@@ -1210,11 +1277,11 @@ public class ProGuardGUI extends JFrame
 
         for (int index = 0; index < keepSpecifications.size(); index++)
         {
-            KeepSpecification listedKeepSpecification =
-                (KeepSpecification)keepSpecifications.get(index);
-            String className = listedKeepSpecification.className;
-            keepSpecificationTemplate.className = className;
-            if (keepSpecificationTemplate.equals(listedKeepSpecification))
+            KeepClassSpecification listedKeepClassSpecification =
+                (KeepClassSpecification)keepSpecifications.get(index);
+            String className = listedKeepClassSpecification.className;
+            keepClassSpecificationTemplate.className = className;
+            if (keepClassSpecificationTemplate.equals(listedKeepClassSpecification))
             {
                 if (buffer == null)
                 {
@@ -1620,45 +1687,52 @@ public class ProGuardGUI extends JFrame
      */
     public static void main(final String[] args)
     {
-//        SwingUtil.invokeAndWait(new Runnable()
-//        {
-//            public void run()
+        try
+        {
+            SwingUtil.invokeAndWait(new Runnable()
             {
-                ProGuardGUI gui = new ProGuardGUI();
-                gui.pack();
-
-                Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-                Dimension guiSize    = gui.getSize();
-                gui.setLocation((screenSize.width - guiSize.width)   / 2,
-                                (screenSize.height - guiSize.height) / 2);
-                gui.show();
-
-                // Start the splash animation, unless specified otherwise.
-                int argIndex = 0;
-                if (argIndex < args.length &&
-                    NO_SPLASH_OPTION.startsWith(args[argIndex]))
-                {
-                    gui.skipSplash();
-                    argIndex++;
-                }
-                else
+                public void run()
                 {
-                    gui.startSplash();
-                }
+                    ProGuardGUI gui = new ProGuardGUI();
+                    gui.pack();
+
+                    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+                    Dimension guiSize    = gui.getSize();
+                    gui.setLocation((screenSize.width - guiSize.width)   / 2,
+                                    (screenSize.height - guiSize.height) / 2);
+                    gui.show();
+
+                    // Start the splash animation, unless specified otherwise.
+                    int argIndex = 0;
+                    if (argIndex < args.length &&
+                        NO_SPLASH_OPTION.startsWith(args[argIndex]))
+                    {
+                        gui.skipSplash();
+                        argIndex++;
+                    }
+                    else
+                    {
+                        gui.startSplash();
+                    }
 
-                // Load an initial configuration, if specified.
-                if (argIndex < args.length)
-                {
-                    gui.loadConfiguration(new File(args[argIndex]));
-                    argIndex++;
-                }
+                    // Load an initial configuration, if specified.
+                    if (argIndex < args.length)
+                    {
+                        gui.loadConfiguration(new File(args[argIndex]));
+                        argIndex++;
+                    }
 
-                if (argIndex < args.length)
-                {
-                    System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]");
-                }
+                    if (argIndex < args.length)
+                    {
+                        System.out.println(gui.getClass().getName() + ": ignoring extra arguments [" + args[argIndex] + "...]");
+                    }
 
-            }
-//        });
+                }
+            });
+        }
+        catch (Exception e)
+        {
+            // Nothing.
+        }
     }
 }
diff --git a/src/proguard/gui/ProGuardRunnable.java b/src/proguard/gui/ProGuardRunnable.java
index 56ccb36..c5c5937 100644
--- a/src/proguard/gui/ProGuardRunnable.java
+++ b/src/proguard/gui/ProGuardRunnable.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/ReTraceRunnable.java b/src/proguard/gui/ReTraceRunnable.java
index e9355a6..1ca19ca 100644
--- a/src/proguard/gui/ReTraceRunnable.java
+++ b/src/proguard/gui/ReTraceRunnable.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -87,7 +87,8 @@ final class ReTraceRunnable implements Runnable
         try
         {
             // Create a new ProGuard object with the GUI's configuration.
-            ReTrace reTrace = new ReTrace(verbose,
+            ReTrace reTrace = new ReTrace(ReTrace.STACK_TRACE_EXPRESSION,
+                                          verbose,
                                           mappingFile);
 
             // Run it.
diff --git a/src/proguard/gui/SwingUtil.java b/src/proguard/gui/SwingUtil.java
index bceccfe..75d2f02 100644
--- a/src/proguard/gui/SwingUtil.java
+++ b/src/proguard/gui/SwingUtil.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,6 +21,7 @@
 package proguard.gui;
 
 import javax.swing.*;
+import java.lang.reflect.InvocationTargetException;
 
 
 /**
@@ -30,7 +31,7 @@ import javax.swing.*;
  * @see SwingUtilities
  * @author Eric Lafortune
  */
-class SwingUtil
+public class SwingUtil
 {
     /**
      * Invokes the given Runnable in the AWT event dispatching thread,
@@ -40,6 +41,7 @@ class SwingUtil
      * @param runnable the Runnable to be executed.
      */
     public static void invokeAndWait(Runnable runnable)
+    throws InterruptedException, InvocationTargetException
     {
         try
         {
diff --git a/src/proguard/gui/TabbedPane.java b/src/proguard/gui/TabbedPane.java
index 35dad8f..a6460f5 100644
--- a/src/proguard/gui/TabbedPane.java
+++ b/src/proguard/gui/TabbedPane.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -155,14 +155,15 @@ public class TabbedPane
         imageConstraints.fill    = GridBagConstraints.BOTH;
         imageConstraints.anchor  = GridBagConstraints.SOUTHWEST;
 
-        JPanel component = new JPanel()
-        {
-            public void paintComponent(Graphics graphics)
-            {
-                graphics.drawImage(image, 0, getHeight() - image.getHeight(null), this);
-            }
-        };
-        component.setBorder(BorderFactory.createEtchedBorder());
+        JButton component = new JButton(new ImageIcon(image));
+        component.setFocusPainted(false);
+        component.setFocusable(false);
+        component.setRequestFocusEnabled(false);
+        component.setRolloverEnabled(false);
+        component.setMargin(new Insets(0, 0, 0, 0));
+        component.setHorizontalAlignment(JButton.LEFT);
+        component.setVerticalAlignment(JButton.BOTTOM);
+        component.setPreferredSize(new Dimension(0, 0));
 
         add(component, imageConstraints);
 
diff --git a/src/proguard/gui/TextAreaOutputStream.java b/src/proguard/gui/TextAreaOutputStream.java
index d46b3b1..57f983d 100644
--- a/src/proguard/gui/TextAreaOutputStream.java
+++ b/src/proguard/gui/TextAreaOutputStream.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -22,6 +22,7 @@ package proguard.gui;
 
 import javax.swing.*;
 import java.io.*;
+import java.lang.reflect.InvocationTargetException;
 
 
 /**
@@ -48,8 +49,15 @@ final class TextAreaOutputStream extends FilterOutputStream implements Runnable
     {
         super.flush();
 
-        // Append the accumulated buffer contents to the text area.
-        SwingUtil.invokeAndWait(this);
+        try
+        {
+            // Append the accumulated buffer contents to the text area.
+            SwingUtil.invokeAndWait(this);
+        }
+        catch (Exception e)
+        {
+            // Nothing.
+        }
     }
 
 
diff --git a/src/proguard/gui/splash/BufferedSprite.java b/src/proguard/gui/splash/BufferedSprite.java
index dfa52ec..8427832 100644
--- a/src/proguard/gui/splash/BufferedSprite.java
+++ b/src/proguard/gui/splash/BufferedSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/CircleSprite.java b/src/proguard/gui/splash/CircleSprite.java
index 26f6782..5dc65eb 100644
--- a/src/proguard/gui/splash/CircleSprite.java
+++ b/src/proguard/gui/splash/CircleSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ClipSprite.java b/src/proguard/gui/splash/ClipSprite.java
index 70cd627..55f9eac 100644
--- a/src/proguard/gui/splash/ClipSprite.java
+++ b/src/proguard/gui/splash/ClipSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ColorSprite.java b/src/proguard/gui/splash/ColorSprite.java
index c3b2372..3f9bc3b 100644
--- a/src/proguard/gui/splash/ColorSprite.java
+++ b/src/proguard/gui/splash/ColorSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/CompositeSprite.java
index b8b524a..2480ead 100644
--- a/src/proguard/gui/splash/CompositeSprite.java
+++ b/src/proguard/gui/splash/CompositeSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantColor.java b/src/proguard/gui/splash/ConstantColor.java
index e520fac..94c78df 100644
--- a/src/proguard/gui/splash/ConstantColor.java
+++ b/src/proguard/gui/splash/ConstantColor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantDouble.java b/src/proguard/gui/splash/ConstantDouble.java
index 1ae8386..0874d6d 100644
--- a/src/proguard/gui/splash/ConstantDouble.java
+++ b/src/proguard/gui/splash/ConstantDouble.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantFont.java b/src/proguard/gui/splash/ConstantFont.java
index 4a01c6e..3f1ac03 100644
--- a/src/proguard/gui/splash/ConstantFont.java
+++ b/src/proguard/gui/splash/ConstantFont.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantInt.java b/src/proguard/gui/splash/ConstantInt.java
index da39e66..537196d 100644
--- a/src/proguard/gui/splash/ConstantInt.java
+++ b/src/proguard/gui/splash/ConstantInt.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantString.java b/src/proguard/gui/splash/ConstantString.java
index 6ab1548..7617c3f 100644
--- a/src/proguard/gui/splash/ConstantString.java
+++ b/src/proguard/gui/splash/ConstantString.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ConstantTiming.java b/src/proguard/gui/splash/ConstantTiming.java
index 19ba182..dfde644 100644
--- a/src/proguard/gui/splash/ConstantTiming.java
+++ b/src/proguard/gui/splash/ConstantTiming.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/FontSprite.java b/src/proguard/gui/splash/FontSprite.java
index 11c77cb..9a554ba 100644
--- a/src/proguard/gui/splash/FontSprite.java
+++ b/src/proguard/gui/splash/FontSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ImageSprite.java b/src/proguard/gui/splash/ImageSprite.java
index afdea51..6e7c189 100644
--- a/src/proguard/gui/splash/ImageSprite.java
+++ b/src/proguard/gui/splash/ImageSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/LinearColor.java b/src/proguard/gui/splash/LinearColor.java
index 83c22fd..3a7674d 100644
--- a/src/proguard/gui/splash/LinearColor.java
+++ b/src/proguard/gui/splash/LinearColor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/LinearDouble.java b/src/proguard/gui/splash/LinearDouble.java
index cb3b7f9..046ae84 100644
--- a/src/proguard/gui/splash/LinearDouble.java
+++ b/src/proguard/gui/splash/LinearDouble.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/LinearInt.java b/src/proguard/gui/splash/LinearInt.java
index 80d5b2b..8d299bc 100644
--- a/src/proguard/gui/splash/LinearInt.java
+++ b/src/proguard/gui/splash/LinearInt.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/LinearTiming.java b/src/proguard/gui/splash/LinearTiming.java
index dfd9171..9b26644 100644
--- a/src/proguard/gui/splash/LinearTiming.java
+++ b/src/proguard/gui/splash/LinearTiming.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/OverrideGraphics2D.java b/src/proguard/gui/splash/OverrideGraphics2D.java
index 45277b6..4333459 100644
--- a/src/proguard/gui/splash/OverrideGraphics2D.java
+++ b/src/proguard/gui/splash/OverrideGraphics2D.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/RectangleSprite.java b/src/proguard/gui/splash/RectangleSprite.java
index 5f81330..d204831 100644
--- a/src/proguard/gui/splash/RectangleSprite.java
+++ b/src/proguard/gui/splash/RectangleSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/SawToothTiming.java b/src/proguard/gui/splash/SawToothTiming.java
index e470d50..076d5e2 100644
--- a/src/proguard/gui/splash/SawToothTiming.java
+++ b/src/proguard/gui/splash/SawToothTiming.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/ShadowedSprite.java b/src/proguard/gui/splash/ShadowedSprite.java
index a273e46..c3504f3 100644
--- a/src/proguard/gui/splash/ShadowedSprite.java
+++ b/src/proguard/gui/splash/ShadowedSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/SineTiming.java b/src/proguard/gui/splash/SineTiming.java
index 90ea8b8..eb0a7cc 100644
--- a/src/proguard/gui/splash/SineTiming.java
+++ b/src/proguard/gui/splash/SineTiming.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/SmoothTiming.java b/src/proguard/gui/splash/SmoothTiming.java
index 69f9aba..a985712 100644
--- a/src/proguard/gui/splash/SmoothTiming.java
+++ b/src/proguard/gui/splash/SmoothTiming.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/SplashPanel.java b/src/proguard/gui/splash/SplashPanel.java
index 2d49646..23a9ce4 100644
--- a/src/proguard/gui/splash/SplashPanel.java
+++ b/src/proguard/gui/splash/SplashPanel.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,6 +20,8 @@
  */
 package proguard.gui.splash;
 
+import proguard.gui.SwingUtil;
+
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
@@ -114,7 +116,7 @@ public class SplashPanel extends JPanel
         // Repaint the SplashPanel one last time.
         try
         {
-            SwingUtilities.invokeAndWait(repainter);
+            SwingUtil.invokeAndWait(repainter);
         }
         catch (InterruptedException ex)
         {
@@ -157,7 +159,7 @@ public class SplashPanel extends JPanel
                     }
 
                     // Do a repaint and time it.
-                    SwingUtilities.invokeAndWait(repainter);
+                    SwingUtil.invokeAndWait(repainter);
 
                     // Sleep for a proportional while.
                     long repaintTime = System.currentTimeMillis() - time;
diff --git a/src/proguard/gui/splash/Sprite.java b/src/proguard/gui/splash/Sprite.java
index 990c53a..ada7a81 100644
--- a/src/proguard/gui/splash/Sprite.java
+++ b/src/proguard/gui/splash/Sprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/TextSprite.java b/src/proguard/gui/splash/TextSprite.java
index d287914..bbf37d4 100644
--- a/src/proguard/gui/splash/TextSprite.java
+++ b/src/proguard/gui/splash/TextSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/TimeSwitchSprite.java b/src/proguard/gui/splash/TimeSwitchSprite.java
index 49c3b27..921bef2 100644
--- a/src/proguard/gui/splash/TimeSwitchSprite.java
+++ b/src/proguard/gui/splash/TimeSwitchSprite.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/Timing.java b/src/proguard/gui/splash/Timing.java
index 84b09e9..887d737 100644
--- a/src/proguard/gui/splash/Timing.java
+++ b/src/proguard/gui/splash/Timing.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/TypeWriterString.java b/src/proguard/gui/splash/TypeWriterString.java
index d576e28..9f1441e 100644
--- a/src/proguard/gui/splash/TypeWriterString.java
+++ b/src/proguard/gui/splash/TypeWriterString.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableColor.java b/src/proguard/gui/splash/VariableColor.java
index 908e057..6a30062 100644
--- a/src/proguard/gui/splash/VariableColor.java
+++ b/src/proguard/gui/splash/VariableColor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableDouble.java b/src/proguard/gui/splash/VariableDouble.java
index b72362b..39302dd 100644
--- a/src/proguard/gui/splash/VariableDouble.java
+++ b/src/proguard/gui/splash/VariableDouble.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/gui/splash/VariableFont.java
index 1676b63..a7de8d7 100644
--- a/src/proguard/gui/splash/VariableFont.java
+++ b/src/proguard/gui/splash/VariableFont.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableInt.java b/src/proguard/gui/splash/VariableInt.java
index a58d138..68a33af 100644
--- a/src/proguard/gui/splash/VariableInt.java
+++ b/src/proguard/gui/splash/VariableInt.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableSizeFont.java b/src/proguard/gui/splash/VariableSizeFont.java
index 949eeb0..e36d28c 100644
--- a/src/proguard/gui/splash/VariableSizeFont.java
+++ b/src/proguard/gui/splash/VariableSizeFont.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/splash/VariableString.java b/src/proguard/gui/splash/VariableString.java
index e98b93e..1dec23b 100644
--- a/src/proguard/gui/splash/VariableString.java
+++ b/src/proguard/gui/splash/VariableString.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/gui/vtitle.gif b/src/proguard/gui/vtitle.gif
deleted file mode 100644
index 646c69b..0000000
Binary files a/src/proguard/gui/vtitle.gif and /dev/null differ
diff --git a/src/proguard/gui/vtitle.png b/src/proguard/gui/vtitle.png
new file mode 100644
index 0000000..218f716
Binary files /dev/null and b/src/proguard/gui/vtitle.png differ
diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/src/proguard/io/CascadingDataEntryWriter.java
index 6347bf0..15719e6 100644
--- a/src/proguard/io/CascadingDataEntryWriter.java
+++ b/src/proguard/io/CascadingDataEntryWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -52,6 +52,16 @@ public class CascadingDataEntryWriter implements DataEntryWriter
 
     // Implementations for DataEntryWriter.
 
+
+    public boolean createDirectory(DataEntry dataEntry) throws IOException
+    {
+        // Try to create a directory with the first data entry writer, or
+        // otherwise with the second data entry writer.
+        return dataEntryWriter1.createDirectory(dataEntry) ||
+               dataEntryWriter2.createDirectory(dataEntry);
+    }
+
+
     public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
     {
         return getOutputStream(dataEntry,  null);
diff --git a/src/proguard/io/ClassFilter.java b/src/proguard/io/ClassFilter.java
index 01fbaf3..bf591ab 100644
--- a/src/proguard/io/ClassFilter.java
+++ b/src/proguard/io/ClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -32,11 +32,8 @@ import java.io.IOException;
  *
  * @author Eric Lafortune
  */
-public class ClassFilter implements DataEntryReader
+public class ClassFilter extends FilteredDataEntryReader
 {
-    private final FilteredDataEntryReader filteredDataEntryReader;
-
-
     /**
      * Creates a new ClassFilter that delegates reading classes to the
      * given reader.
@@ -54,19 +51,9 @@ public class ClassFilter implements DataEntryReader
     public ClassFilter(DataEntryReader classReader,
                        DataEntryReader dataEntryReader)
     {
-        filteredDataEntryReader =
-            new FilteredDataEntryReader(
-            new DataEntryNameFilter(
-            new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
-            classReader,
-            dataEntryReader);
-    }
-
-
-    // Implementations for DataEntryReader.
-
-    public void read(DataEntry dataEntry) throws IOException
-    {
-        filteredDataEntryReader.read(dataEntry);
+        super(new DataEntryNameFilter(
+              new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
+              classReader,
+              dataEntryReader);
     }
 }
diff --git a/src/proguard/io/ClassReader.java b/src/proguard/io/ClassReader.java
index d6c28d3..e21968c 100644
--- a/src/proguard/io/ClassReader.java
+++ b/src/proguard/io/ClassReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -98,7 +98,8 @@ public class ClassReader implements DataEntryReader
                 if (!dataEntry.getName().replace(File.pathSeparatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR).equals(className+ClassConstants.CLASS_FILE_EXTENSION) &&
                     warningPrinter != null)
                 {
-                    warningPrinter.print("Warning: class [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(className) + "]");
+                    warningPrinter.print(className,
+                                         "Warning: class [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(className) + "]");
                 }
 
                 clazz.accept(classVisitor);
diff --git a/src/proguard/io/ClassRewriter.java b/src/proguard/io/ClassRewriter.java
index 98cafdd..bd19e1d 100644
--- a/src/proguard/io/ClassRewriter.java
+++ b/src/proguard/io/ClassRewriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntry.java b/src/proguard/io/DataEntry.java
index d5d9018..af0e373 100644
--- a/src/proguard/io/DataEntry.java
+++ b/src/proguard/io/DataEntry.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,7 +23,8 @@ package proguard.io;
 import java.io.*;
 
 /**
- * This interface describes a data entry, e.g. a ZIP entry or a file.
+ * This interface describes a data entry, e.g. a ZIP entry, a file, or a
+ * directory.
  *
  * @author Eric Lafortune
  */
@@ -34,9 +35,15 @@ public interface DataEntry
      */
     public String getName();
 
+    /**
+     * Returns whether the data entry represents a directory.
+     */
+    public boolean isDirectory();
+
 
     /**
      * Returns an input stream for reading the content of this data entry.
+     * The data entry may not represent a directory.
      */
     public InputStream getInputStream() throws IOException;
 
diff --git a/src/proguard/io/DataEntryCopier.java b/src/proguard/io/DataEntryCopier.java
index 2f6e37e..faaa555 100644
--- a/src/proguard/io/DataEntryCopier.java
+++ b/src/proguard/io/DataEntryCopier.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -52,17 +52,24 @@ public class DataEntryCopier implements DataEntryReader
     {
         try
         {
-            // Get the output entry corresponding to this input entry.
-            OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
-            if (outputStream != null)
+            if (dataEntry.isDirectory())
             {
-                InputStream inputStream = dataEntry.getInputStream();
+                dataEntryWriter.createDirectory(dataEntry);
+            }
+            else
+            {
+                // Get the output entry corresponding to this input entry.
+                OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
+                if (outputStream != null)
+                {
+                    InputStream inputStream = dataEntry.getInputStream();
 
-                // Copy the data from the input entry to the output entry.
-                copyData(inputStream, outputStream);
+                    // Copy the data from the input entry to the output entry.
+                    copyData(inputStream, outputStream);
 
-                // Close the data entries.
-                dataEntry.closeInputStream();
+                    // Close the data entries.
+                    dataEntry.closeInputStream();
+                }
             }
         }
         catch (IOException ex)
@@ -72,14 +79,12 @@ public class DataEntryCopier implements DataEntryReader
     }
 
 
-    // Small utility methods.
-
     /**
      * Copies all data that it can read from the given input stream to the
      * given output stream.
      */
-    private void copyData(InputStream  inputStream,
-                          OutputStream outputStream)
+    protected void copyData(InputStream  inputStream,
+                            OutputStream outputStream)
     throws IOException
     {
         while (true)
diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryDirectoryFilter.java
similarity index 67%
copy from src/proguard/io/DataEntryReader.java
copy to src/proguard/io/DataEntryDirectoryFilter.java
index 4759310..bb36f3e 100644
--- a/src/proguard/io/DataEntryReader.java
+++ b/src/proguard/io/DataEntryDirectoryFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,19 +20,21 @@
  */
 package proguard.io;
 
-import java.io.IOException;
-
+import proguard.util.StringMatcher;
 
 /**
- * This interface provides methods for reading data entries. The implementation
- * determines what to do with the read data, if anything.
+ * This DataEntryFilter filters data entries based on whether they represent
+ * directories.
  *
  * @author Eric Lafortune
  */
-public interface DataEntryReader
+public class DataEntryDirectoryFilter
+implements   DataEntryFilter
 {
-    /**
-     * Reads the given data entry.
-     */
-    public void read(DataEntry dataEntry) throws IOException;
-}
+    // Implementations for DataEntryFilter.
+
+    public boolean accepts(DataEntry dataEntry)
+    {
+        return dataEntry != null && dataEntry.isDirectory();
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/io/DataEntryFilter.java b/src/proguard/io/DataEntryFilter.java
index e84c4c6..ddcd0be 100644
--- a/src/proguard/io/DataEntryFilter.java
+++ b/src/proguard/io/DataEntryFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntryNameFilter.java b/src/proguard/io/DataEntryNameFilter.java
index 21947e1..d6afd2e 100644
--- a/src/proguard/io/DataEntryNameFilter.java
+++ b/src/proguard/io/DataEntryNameFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryObfuscator.java
similarity index 64%
copy from src/proguard/io/DataEntryRenamer.java
copy to src/proguard/io/DataEntryObfuscator.java
index 34f5a21..aa63af1 100644
--- a/src/proguard/io/DataEntryRenamer.java
+++ b/src/proguard/io/DataEntryObfuscator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,8 +21,10 @@
 package proguard.io;
 
 import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
 
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * This DataEntryReader delegates to another DataEntryReader, renaming the
@@ -30,17 +32,29 @@ import java.io.IOException;
  *
  * @author Eric Lafortune
  */
-public class DataEntryRenamer implements DataEntryReader
+public class DataEntryObfuscator implements DataEntryReader
 {
     private final ClassPool       classPool;
+    private final Map             packagePrefixMap;
     private final DataEntryReader dataEntryReader;
 
 
-    public DataEntryRenamer(ClassPool       classPool,
-                            DataEntryReader dataEntryReader)
+    /**
+     * Creates a new DataEntryObfuscator.
+     * @param classPool        the class pool that maps from old names to new
+     *                         names.
+     * @param packagePrefixMap the map from old package prefixes to new package
+     *                         prefixes.
+     * @param dataEntryReader  the DataEntryReader to which calls will be
+     *                         delegated.
+     */
+    public DataEntryObfuscator(ClassPool       classPool,
+                               Map             packagePrefixMap,
+                               DataEntryReader dataEntryReader)
     {
-        this.classPool       = classPool;
-        this.dataEntryReader = dataEntryReader;
+        this.classPool        = classPool;
+        this.packagePrefixMap = packagePrefixMap;
+        this.dataEntryReader  = dataEntryReader;
     }
 
 
@@ -69,15 +83,15 @@ public class DataEntryRenamer implements DataEntryReader
             char c = dataEntryName.charAt(suffixIndex);
             if (!Character.isLetterOrDigit(c))
             {
-                // Stop looking at the first package separator.
+                // Chop off the suffix.
+                String className = dataEntryName.substring(0, suffixIndex);
+
+                // Did we get to the package separator?
                 if (c == ClassConstants.INTERNAL_PACKAGE_SEPARATOR)
                 {
                     break;
                 }
 
-                // 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)
@@ -87,9 +101,8 @@ public class DataEntryRenamer implements DataEntryReader
                     if (!className.equals(newClassName))
                     {
                         // Return a renamed data entry.
-                        String newDataEntryName =  suffixIndex > 0 ?
-                            newClassName + dataEntryName.substring(suffixIndex) :
-                            newClassName;
+                        String newDataEntryName =
+                            newClassName + dataEntryName.substring(suffixIndex);
 
                         return new RenamedDataEntry(dataEntry, newDataEntryName);
                     }
@@ -100,6 +113,19 @@ public class DataEntryRenamer implements DataEntryReader
             }
         }
 
+        // Did the package get a new name?
+        String packagePrefix    = ClassUtil.internalPackagePrefix(dataEntryName);
+        String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
+        if (newPackagePrefix != null &&
+            !packagePrefix.equals(newPackagePrefix))
+        {
+            // Return a renamed data entry.
+            String newDataEntryName =
+                newPackagePrefix + dataEntryName.substring(packagePrefix.length());
+
+            return new RenamedDataEntry(dataEntry, newDataEntryName);
+        }
+
         return dataEntry;
     }
 }
diff --git a/src/proguard/io/DataEntryParentFilter.java b/src/proguard/io/DataEntryParentFilter.java
index 838ea54..fbeac4f 100644
--- a/src/proguard/io/DataEntryParentFilter.java
+++ b/src/proguard/io/DataEntryParentFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntryPump.java b/src/proguard/io/DataEntryPump.java
index d897685..bfe22a3 100644
--- a/src/proguard/io/DataEntryPump.java
+++ b/src/proguard/io/DataEntryPump.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryReader.java
index 4759310..e77f7bf 100644
--- a/src/proguard/io/DataEntryReader.java
+++ b/src/proguard/io/DataEntryReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryRenamer.java
index 34f5a21..45c61ee 100644
--- a/src/proguard/io/DataEntryRenamer.java
+++ b/src/proguard/io/DataEntryRenamer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,86 +20,85 @@
  */
 package proguard.io;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 import java.io.IOException;
+import java.util.Map;
 
 /**
  * This DataEntryReader delegates to another DataEntryReader, renaming the
- * data entries based on the renamed classes in the given ClassPool.
+ * data entries based on the given map. Entries whose name does not appear
+ * in the map may be passed to an alternative DataEntryReader.
  *
  * @author Eric Lafortune
  */
 public class DataEntryRenamer implements DataEntryReader
 {
-    private final ClassPool       classPool;
-    private final DataEntryReader dataEntryReader;
+    private final Map             nameMap;
+    private final DataEntryReader renamedDataEntryReader;
+    private final DataEntryReader missingDataEntryReader;
 
 
-    public DataEntryRenamer(ClassPool       classPool,
-                            DataEntryReader dataEntryReader)
-    {
-        this.classPool       = classPool;
-        this.dataEntryReader = dataEntryReader;
-    }
-
-
-    // Implementations for DataEntryReader.
-
-    public void read(DataEntry dataEntry) throws IOException
+    /**
+     * Creates a new DataEntryRenamer.
+     * @param nameMap                the map from old names to new names.
+     * @param renamedDataEntryReader the DataEntryReader to which renamed data
+     *                               entries will be passed.
+     */
+    public DataEntryRenamer(Map             nameMap,
+                            DataEntryReader renamedDataEntryReader)
     {
-        // Delegate to the actual data entry reader.
-        dataEntryReader.read(renamedDataEntry(dataEntry));
+        this(nameMap, renamedDataEntryReader, null);
     }
 
 
     /**
-     * Create a renamed data entry, if possible.
+     * Creates a new DataEntryRenamer.
+     * @param nameMap                the map from old names to new names.
+     * @param renamedDataEntryReader the DataEntryReader to which renamed data
+     *                               entries will be passed.
+     * @param missingDataEntryReader the optional DataEntryReader to which data
+     *                               entries that can't be renamed will be
+     *                               passed.
      */
-    private DataEntry renamedDataEntry(DataEntry dataEntry)
+    public DataEntryRenamer(Map             nameMap,
+                            DataEntryReader renamedDataEntryReader,
+                            DataEntryReader missingDataEntryReader)
     {
-        String dataEntryName = dataEntry.getName();
+        this.nameMap                = nameMap;
+        this.renamedDataEntryReader = renamedDataEntryReader;
+        this.missingDataEntryReader = missingDataEntryReader;
+    }
 
-        // Try to find a corresponding class name by removing increasingly
-        // long suffixes,
-        for (int suffixIndex = dataEntryName.length() - 1;
-             suffixIndex > 0;
-             suffixIndex--)
-        {
-            char c = dataEntryName.charAt(suffixIndex);
-            if (!Character.isLetterOrDigit(c))
-            {
-                // Stop looking at the first package separator.
-                if (c == ClassConstants.INTERNAL_PACKAGE_SEPARATOR)
-                {
-                    break;
-                }
 
-                // Chop off the suffix.
-                String className = dataEntryName.substring(0, suffixIndex);
+    // Implementations for DataEntryReader.
 
-                // 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;
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        String name = dataEntry.getName();
 
-                        return new RenamedDataEntry(dataEntry, newDataEntryName);
-                    }
+        // Add a directory separator if necessary.
+        if (dataEntry.isDirectory() &&
+            name.length() > 0)
+        {
+            name += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+        }
 
-                    // Otherwise stop looking.
-                    break;
-                }
+        String newName = (String)nameMap.get(name);
+        if (newName != null)
+        {
+            // Add remove the directory separator if necessary.
+            if (dataEntry.isDirectory() &&
+                newName.length() > 0)
+            {
+                newName = newName.substring(0, newName.length() -  1);
             }
-        }
 
-        return dataEntry;
+            renamedDataEntryReader.read(new RenamedDataEntry(dataEntry, newName));
+        }
+        else if (missingDataEntryReader != null)
+        {
+            missingDataEntryReader.read(dataEntry);
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/io/DataEntryRewriter.java b/src/proguard/io/DataEntryRewriter.java
index 7c28381..eefced4 100644
--- a/src/proguard/io/DataEntryRewriter.java
+++ b/src/proguard/io/DataEntryRewriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -31,10 +31,9 @@ import java.io.*;
  *
  * @author Eric Lafortune
  */
-public class DataEntryRewriter implements DataEntryReader
+public class DataEntryRewriter extends DataEntryCopier
 {
-    private final ClassPool       classPool;
-    private final DataEntryWriter dataEntryWriter;
+    private final ClassPool classPool;
 
 
     /**
@@ -43,50 +42,36 @@ public class DataEntryRewriter implements DataEntryReader
     public DataEntryRewriter(ClassPool       classPool,
                              DataEntryWriter dataEntryWriter)
     {
-        this.classPool       = classPool;
-        this.dataEntryWriter = dataEntryWriter;
+        super(dataEntryWriter);
+
+        this.classPool = classPool;
     }
 
 
-    // Implementations for DataEntryReader.
+    // Implementations for DataEntryCopier.
 
-    public void read(DataEntry dataEntry) throws IOException
+    protected void copyData(InputStream  inputStream,
+                            OutputStream outputStream)
+    throws IOException
     {
-        try
-        {
-            // Get the output entry corresponding to this input entry.
-            OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
-            if (outputStream != null)
-            {
-                InputStream inputStream = dataEntry.getInputStream();
+        Reader reader = new BufferedReader(new InputStreamReader(inputStream));
+        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
 
-                // Copy the data from the input entry to the output entry.
-                copyData(inputStream, outputStream);
+        copyData(reader, writer);
 
-                // Close the data entries.
-                dataEntry.closeInputStream();
-            }
-        }
-        catch (IOException ex)
-        {
-            System.err.println("Warning: can't write resource [" + dataEntry.getName() + "] (" + ex.getMessage() + ")");
-        }
+        writer.flush();
+        outputStream.flush();
     }
 
 
-    // Small utility methods.
-
     /**
-     * Copies all data that it can read from the given input stream to the
-     * given output stream.
+     * Copies all data that it can read from the given reader to the given
+     * writer.
      */
-    private void copyData(InputStream  inputStream,
-                          OutputStream outputStream)
+    protected void copyData(Reader reader,
+                            Writer writer)
     throws IOException
     {
-        Reader reader = new BufferedReader(new InputStreamReader(inputStream));
-        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
-
         StringBuffer word = new StringBuffer();
 
         while (true)
@@ -119,12 +104,11 @@ public class DataEntryRewriter implements DataEntryReader
 
         // Write out the final word.
         writeUpdatedWord(writer, word.toString());
-
-        writer.flush();
-        outputStream.flush();
     }
 
 
+    // Small utility methods.
+
     /**
      * Writes the given word to the given writer, after having adapted it,
      * based on the renamed class names.
diff --git a/src/proguard/io/DataEntryWriter.java b/src/proguard/io/DataEntryWriter.java
index b30bb62..9ecf79b 100644
--- a/src/proguard/io/DataEntryWriter.java
+++ b/src/proguard/io/DataEntryWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -33,6 +33,14 @@ import java.io.*;
 public interface DataEntryWriter
 {
     /**
+     * Creates a directory.
+     * @param dataEntry the data entry for which the directory is to be created.
+     * @return whether the directory has been created.
+     */
+    public boolean createDirectory(DataEntry dataEntry) throws IOException;
+
+
+    /**
      * Returns an output stream for writing data. The caller must not close
      * the output stream; closing the output stream is the responsibility of
      * the implementation of this interface.
diff --git a/src/proguard/io/ClassFilter.java b/src/proguard/io/DirectoryFilter.java
similarity index 58%
copy from src/proguard/io/ClassFilter.java
copy to src/proguard/io/DirectoryFilter.java
index 01fbaf3..a45d1aa 100644
--- a/src/proguard/io/ClassFilter.java
+++ b/src/proguard/io/DirectoryFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -28,22 +28,19 @@ import java.io.IOException;
 
 /**
  * This DataEntryReader delegates to one of two other DataEntryReader instances,
- * depending on the extension of the data entry.
+ * depending on whether the data entry represents a directory or not.
  *
  * @author Eric Lafortune
  */
-public class ClassFilter implements DataEntryReader
+public class DirectoryFilter extends FilteredDataEntryReader
 {
-    private final FilteredDataEntryReader filteredDataEntryReader;
-
-
     /**
-     * Creates a new ClassFilter that delegates reading classes to the
+     * Creates a new ClassFilter that delegates reading directories to the
      * given reader.
      */
-    public ClassFilter(DataEntryReader classReader)
+    public DirectoryFilter(DataEntryReader directoryReader)
     {
-        this(classReader, null);
+        this (directoryReader, null);
     }
 
 
@@ -51,22 +48,11 @@ public class ClassFilter implements DataEntryReader
      * Creates a new ClassFilter that delegates to either of the two given
      * readers.
      */
-    public ClassFilter(DataEntryReader classReader,
-                       DataEntryReader dataEntryReader)
-    {
-        filteredDataEntryReader =
-            new FilteredDataEntryReader(
-            new DataEntryNameFilter(
-            new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
-            classReader,
-            dataEntryReader);
-    }
-
-
-    // Implementations for DataEntryReader.
-
-    public void read(DataEntry dataEntry) throws IOException
+    public DirectoryFilter(DataEntryReader directoryReader,
+                           DataEntryReader otherReader)
     {
-        filteredDataEntryReader.read(dataEntry);
+        super(new DataEntryDirectoryFilter(),
+              directoryReader,
+              otherReader);
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java
index 04eee76..cab2ff3 100644
--- a/src/proguard/io/DirectoryPump.java
+++ b/src/proguard/io/DirectoryPump.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -61,6 +61,9 @@ public class DirectoryPump implements DataEntryPump
     private void readFiles(File file, DataEntryReader dataEntryReader)
     throws IOException
     {
+        // Pass the file data entry to the reader.
+        dataEntryReader.read(new FileDataEntry(directory, file));
+
         if (file.isDirectory())
         {
             // Recurse into the subdirectory.
@@ -71,9 +74,5 @@ public class DirectoryPump implements DataEntryPump
                 readFiles(files[index], dataEntryReader);
             }
         }
-        else
-        {
-            dataEntryReader.read(new FileDataEntry(directory, file));
-        }
     }
 }
diff --git a/src/proguard/io/DirectoryWriter.java b/src/proguard/io/DirectoryWriter.java
index 172966d..c6605df 100644
--- a/src/proguard/io/DirectoryWriter.java
+++ b/src/proguard/io/DirectoryWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -55,6 +55,26 @@ public class DirectoryWriter implements DataEntryWriter
 
     // Implementations for DataEntryWriter.
 
+    public boolean createDirectory(DataEntry dataEntry) throws IOException
+    {
+        // Should we close the current file?
+        if (!isFile &&
+            currentFile != null)
+        {
+            closeEntry();
+        }
+
+        File directory = getFile(dataEntry);
+        if (!directory.exists() &&
+            !directory.mkdirs())
+        {
+            throw new IOException("Can't create directory [" + directory.getPath() + "]");
+        }
+
+        return true;
+    }
+
+
     public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
     {
         return getOutputStream(dataEntry,  null);
@@ -64,10 +84,12 @@ public class DirectoryWriter implements DataEntryWriter
     public OutputStream getOutputStream(DataEntry dataEntry,
                                         Finisher  finisher) throws IOException
     {
+        File file = getFile(dataEntry);
+
         // Should we close the current file?
         if (!isFile             &&
             currentFile != null &&
-            !currentFile.equals(getFile(dataEntry)))
+            !currentFile.equals(file))
         {
             closeEntry();
         }
@@ -75,8 +97,6 @@ public class DirectoryWriter implements DataEntryWriter
         // Do we need a new stream?
         if (currentOutputStream == null)
         {
-            File file = getFile(dataEntry);
-
             // Make sure the parent directories exist.
             File parentDirectory = file.getParentFile();
             if (parentDirectory != null   &&
diff --git a/src/proguard/io/FileDataEntry.java b/src/proguard/io/FileDataEntry.java
index 0c484ca..d0449ee 100644
--- a/src/proguard/io/FileDataEntry.java
+++ b/src/proguard/io/FileDataEntry.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -57,7 +57,11 @@ public class FileDataEntry implements DataEntry
     }
 
 
-    // Implementations for InputEntry.
+    public boolean isDirectory()
+    {
+        return file.isDirectory();
+    }
+
 
     public InputStream getInputStream() throws IOException
     {
diff --git a/src/proguard/io/FilteredDataEntryReader.java b/src/proguard/io/FilteredDataEntryReader.java
index 40e733d..11da0d4 100644
--- a/src/proguard/io/FilteredDataEntryReader.java
+++ b/src/proguard/io/FilteredDataEntryReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/src/proguard/io/FilteredDataEntryWriter.java
index 6c50bea..40a8c64 100644
--- a/src/proguard/io/FilteredDataEntryWriter.java
+++ b/src/proguard/io/FilteredDataEntryWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -74,6 +74,19 @@ public class FilteredDataEntryWriter implements DataEntryWriter
 
     // Implementations for DataEntryWriter.
 
+    public boolean createDirectory(DataEntry dataEntry) throws IOException
+    {
+        // Get the right data entry writer.
+        DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ?
+            acceptedDataEntryWriter :
+            rejectedDataEntryWriter;
+
+        // Delegate to it, if it's not null.
+        return dataEntryWriter != null &&
+               dataEntryWriter.createDirectory(dataEntry);
+    }
+
+
     public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
     {
         return getOutputStream(dataEntry,  null);
diff --git a/src/proguard/io/Finisher.java b/src/proguard/io/Finisher.java
index 792e032..8c4cc1e 100644
--- a/src/proguard/io/Finisher.java
+++ b/src/proguard/io/Finisher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/io/JarReader.java b/src/proguard/io/JarReader.java
index 7e366f9..f96b4aa 100644
--- a/src/proguard/io/JarReader.java
+++ b/src/proguard/io/JarReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -61,13 +61,10 @@ public class JarReader implements DataEntryReader
                     break;
                 }
 
-                if (!zipEntry.isDirectory())
-                {
-                    // Delegate the actual reading to the data entry reader.
-                    dataEntryReader.read(new ZipDataEntry(dataEntry,
-                                                          zipEntry,
-                                                          zipInputStream));
-                }
+                // Delegate the actual reading to the data entry reader.
+                dataEntryReader.read(new ZipDataEntry(dataEntry,
+                                                      zipEntry,
+                                                      zipInputStream));
             }
         }
         finally
diff --git a/src/proguard/io/JarWriter.java b/src/proguard/io/JarWriter.java
index cb0f0a8..3e40cdf 100644
--- a/src/proguard/io/JarWriter.java
+++ b/src/proguard/io/JarWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,6 +20,8 @@
  */
 package proguard.io;
 
+import proguard.classfile.ClassConstants;
+
 import java.io.*;
 import java.util.*;
 import java.util.jar.*;
@@ -71,6 +73,39 @@ public class JarWriter implements DataEntryWriter, Finisher
 
     // Implementations for DataEntryWriter.
 
+    public boolean createDirectory(DataEntry dataEntry) throws IOException
+    {
+        //Make sure we can start with a new entry.
+        if (!prepareEntry(dataEntry))
+        {
+            return false;
+        }
+
+        // Close the previous ZIP entry, if any.
+        closeEntry();
+
+        // Get the directory entry name.
+        String name = dataEntry.getName() + ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+
+        // We have to check if the name is already used, because
+        // ZipOutputStream doesn't handle this case properly (it throws
+        // an exception which can be caught, but the ZipDataEntry is
+        // remembered anyway).
+        if (jarEntryNames.add(name))
+        {
+            // Create a new directory entry.
+            currentJarOutputStream.putNextEntry(new ZipEntry(name));
+            currentJarOutputStream.closeEntry();
+        }
+
+        // Clear the finisher.
+        currentFinisher  = null;
+        currentDataEntry = null;
+
+        return true;
+    }
+
+
     public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
     {
         return getOutputStream(dataEntry,  null);
@@ -80,34 +115,12 @@ public class JarWriter implements DataEntryWriter, Finisher
     public OutputStream getOutputStream(DataEntry dataEntry,
                                         Finisher  finisher) throws IOException
     {
-        // Get the parent stream, new or exisiting.
-        // This may finish our own jar output stream.
-        OutputStream parentOutputStream =
-            dataEntryWriter.getOutputStream(dataEntry.getParent(), this);
-
-        // Did we get a stream?
-        if (parentOutputStream == null)
+        //Make sure we can start with a new entry.
+        if (!prepareEntry(dataEntry))
         {
             return null;
         }
 
-        // Do we need a new stream?
-        if (currentParentOutputStream == null)
-        {
-            currentParentOutputStream = parentOutputStream;
-
-            // Create a new jar stream, with a manifest, if set.
-            currentJarOutputStream = manifest != null ?
-                new JarOutputStream(parentOutputStream, manifest) :
-                new ZipOutputStream(parentOutputStream);
-
-            // Add a comment, if set.
-            if (comment != null)
-            {
-                currentJarOutputStream.setComment(comment);
-            }
-        }
-
         // Do we need a new entry?
         if (!dataEntry.equals(currentDataEntry))
         {
@@ -117,9 +130,10 @@ public class JarWriter implements DataEntryWriter, Finisher
             // Get the entry name.
             String name = dataEntry.getName();
 
-            // We have to check if the name is already used, because ZipOutputStream
-            // doesn't handle this case properly (it throws an exception which can
-            // be caught, but the ZipDataEntry is remembered anyway).
+            // We have to check if the name is already used, because
+            // ZipOutputStream doesn't handle this case properly (it throws
+            // an exception which can be caught, but the ZipDataEntry is
+            // remembered anyway).
             if (!jarEntryNames.add(name))
             {
                 throw new IOException("Duplicate zip entry ["+dataEntry+"]");
@@ -128,6 +142,7 @@ public class JarWriter implements DataEntryWriter, Finisher
             // Create a new entry.
             currentJarOutputStream.putNextEntry(new ZipEntry(name));
 
+            // Set up the finisher for the entry.
             currentFinisher  = finisher;
             currentDataEntry = dataEntry;
         }
@@ -163,6 +178,43 @@ public class JarWriter implements DataEntryWriter, Finisher
     // Small utility methods.
 
     /**
+     * Makes sure the current output stream is set up for the given entry.
+     */
+    private boolean prepareEntry(DataEntry dataEntry) throws IOException
+    {
+        // Get the parent stream, new or exisiting.
+        // This may finish our own jar output stream.
+        OutputStream parentOutputStream =
+            dataEntryWriter.getOutputStream(dataEntry.getParent(), this);
+
+        // Did we get a stream?
+        if (parentOutputStream == null)
+        {
+            return false;
+        }
+
+        // Do we need a new stream?
+        if (currentParentOutputStream == null)
+        {
+            currentParentOutputStream = parentOutputStream;
+
+            // Create a new jar stream, with a manifest, if set.
+            currentJarOutputStream = manifest != null ?
+                new JarOutputStream(parentOutputStream, manifest) :
+                new ZipOutputStream(parentOutputStream);
+
+            // Add a comment, if set.
+            if (comment != null)
+            {
+                currentJarOutputStream.setComment(comment);
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
      * Closes the previous ZIP entry, if any.
      */
     private void closeEntry() throws IOException
diff --git a/src/proguard/io/ManifestRewriter.java b/src/proguard/io/ManifestRewriter.java
new file mode 100644
index 0000000..f10307e
--- /dev/null
+++ b/src/proguard/io/ManifestRewriter.java
@@ -0,0 +1,216 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader writes the manifest data entries that it reads to a
+ * given DataEntryWriter, updating their contents based on the renamed classes
+ * in the given ClassPool.
+ *
+ * @author Eric Lafortune
+ */
+public class ManifestRewriter extends DataEntryRewriter
+{
+    /**
+     * Creates a new ManifestRewriter.
+     */
+    public ManifestRewriter(ClassPool       classPool,
+                            DataEntryWriter dataEntryWriter)
+    {
+        super(classPool, dataEntryWriter);
+    }
+
+
+    // Implementations for DataEntryRewriter.
+
+    protected void copyData(Reader reader,
+                            Writer writer)
+    throws IOException
+    {
+        super.copyData(new SplitLineReader(reader),
+                       new SplitLineWriter(writer));
+    }
+
+
+    /**
+     * This Reader reads manifest files, joining any split lines.
+     */
+    private static class SplitLineReader extends FilterReader
+    {
+        private char[] buffer      = new char[2];
+        private int    bufferIndex = 0;
+        private int    bufferSize  = 0;
+
+
+        public SplitLineReader(Reader reader)
+        {
+            super(reader);
+        }
+
+
+        // Implementations for Reader.
+
+        public int read() throws IOException
+        {
+            while (true)
+            {
+                if (bufferIndex < bufferSize)
+                {
+                    return buffer[bufferIndex++];
+                }
+
+                // Read the first character.
+                int c1 = super.read();
+                if (c1 != '\n' && c1 != '\r')
+                {
+                    return c1;
+                }
+
+                bufferIndex = 0;
+                bufferSize  = 0;
+                buffer[bufferSize++] = '\n';
+
+                // Read the second character.
+                int c2 = super.read();
+                if (c2 == ' ')
+                {
+                    bufferSize = 0;
+                    continue;
+                }
+
+                if (c1 != '\r' || c2 != '\n')
+                {
+                    buffer[bufferSize++] = (char)c2;
+                    continue;
+                }
+
+                // Read the third character.
+                int c3 = super.read();
+                if (c3 == ' ')
+                {
+                    bufferSize = 0;
+                    continue;
+                }
+
+                buffer[bufferSize++] = (char)c3;
+            }
+        }
+
+
+        public int read(char[] cbuf, int off, int len) throws IOException
+        {
+            // Delegate to reading a single character at a time.
+            int count = 0;
+            while (count < len)
+            {
+                int c = read();
+                if (c == -1)
+                {
+                    break;
+                }
+
+                cbuf[off + count++] = (char)c;
+            }
+
+            return count;
+        }
+
+
+        public long skip(long n) throws IOException
+        {
+            // Delegate to reading a single character at a time.
+            int count = 0;
+            while (count < n)
+            {
+                int c = read();
+                if (c == -1)
+                {
+                    break;
+                }
+
+                count++;
+            }
+
+            return count;
+        }
+    }
+
+
+    /**
+     * This Writer writes manifest files, splitting any long lines.
+     */
+    private static class SplitLineWriter extends FilterWriter
+    {
+        private int counter = 0;
+
+
+        public SplitLineWriter(Writer writer)
+        {
+            super(writer);
+        }
+
+
+        // Implementations for Reader.
+
+        public void write(int c) throws IOException
+        {
+            // TODO: We should actually count the Utf-8 bytes, not the characters.
+            if (c == '\n')
+            {
+                // Reset the character count.
+                counter = 0;
+            }
+            else if (counter == 70)
+            {
+                // Insert are newline and space.
+                super.write('\n');
+                super.write(' ');
+
+                counter = 2;
+            }
+            else
+            {
+                counter++;
+            }
+
+            super.write(c);
+        }
+
+
+        public void write(char[] cbuf, int off, int len) throws IOException
+        {
+            for (int count = 0; count < len; count++)
+            {
+                write(cbuf[off + count]);
+            }
+        }
+
+
+        public void write(String str, int off, int len) throws IOException
+        {
+            write(str.toCharArray(), off, len);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/io/NameFilter.java b/src/proguard/io/NameFilter.java
new file mode 100644
index 0000000..2a9fbc3
--- /dev/null
+++ b/src/proguard/io/NameFilter.java
@@ -0,0 +1,83 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.io;
+
+import proguard.util.*;
+
+import java.util.List;
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on the name of the data entry.
+ *
+ * @author Eric Lafortune
+ */
+public class NameFilter extends FilteredDataEntryReader
+{
+    /**
+     * Creates a new NameFilter that delegates to the given reader, depending
+     * on the given list of filters.
+     */
+    public NameFilter(String          regularExpression,
+                      DataEntryReader acceptedDataEntryReader)
+    {
+        this(regularExpression, acceptedDataEntryReader, null);
+    }
+
+
+    /**
+     * Creates a new NameFilter that delegates to either of the two given
+     * readers, depending on the given list of filters.
+     */
+    public NameFilter(String          regularExpression,
+                      DataEntryReader acceptedDataEntryReader,
+                      DataEntryReader rejectedDataEntryReader)
+    {
+        super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpression)),
+              acceptedDataEntryReader,
+              rejectedDataEntryReader);
+    }
+
+
+    /**
+     * Creates a new NameFilter that delegates to the given reader, depending
+     * on the given list of filters.
+     */
+    public NameFilter(List            regularExpressions,
+                      DataEntryReader acceptedDataEntryReader)
+    {
+        this(regularExpressions, acceptedDataEntryReader, null);
+    }
+
+
+    /**
+     * Creates a new NameFilter that delegates to either of the two given
+     * readers, depending on the given list of filters.
+     */
+    public NameFilter(List            regularExpressions,
+                      DataEntryReader acceptedDataEntryReader,
+                      DataEntryReader rejectedDataEntryReader)
+    {
+        super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpressions)),
+              acceptedDataEntryReader,
+              rejectedDataEntryReader);
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/io/ParentDataEntryWriter.java b/src/proguard/io/ParentDataEntryWriter.java
index 5def90c..4f16d12 100644
--- a/src/proguard/io/ParentDataEntryWriter.java
+++ b/src/proguard/io/ParentDataEntryWriter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -46,6 +46,13 @@ public class ParentDataEntryWriter implements DataEntryWriter
 
     // Implementations for DataEntryWriter.
 
+
+    public boolean createDirectory(DataEntry dataEntry) throws IOException
+    {
+        return getOutputStream(dataEntry) != null;
+    }
+
+
     public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
     {
         return getOutputStream(dataEntry, null);
diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/io/RenamedDataEntry.java
index 410523d..ae2d267 100644
--- a/src/proguard/io/RenamedDataEntry.java
+++ b/src/proguard/io/RenamedDataEntry.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -50,6 +50,12 @@ public class RenamedDataEntry implements DataEntry
     }
 
 
+    public boolean isDirectory()
+    {
+        return dataEntry.isDirectory();
+    }
+
+
     public InputStream getInputStream() throws IOException
     {
         return dataEntry.getInputStream();
diff --git a/src/proguard/io/ZipDataEntry.java b/src/proguard/io/ZipDataEntry.java
index a760709..5779fd8 100644
--- a/src/proguard/io/ZipDataEntry.java
+++ b/src/proguard/io/ZipDataEntry.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -34,7 +34,7 @@ public class ZipDataEntry implements DataEntry
 {
     private final DataEntry      parent;
     private final ZipEntry       zipEntry;
-    private ZipInputStream zipInputStream;
+    private       ZipInputStream zipInputStream;
 
 
     public ZipDataEntry(DataEntry      parent,
@@ -51,9 +51,22 @@ public class ZipDataEntry implements DataEntry
 
     public String getName()
     {
-        // Chop the directory name from the file name and get the right separators.
-        return zipEntry.getName()
+        // Get the right separators.
+        String name = zipEntry.getName()
             .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+
+        // Chop the trailing directory slash, if any.
+        int length = name.length();
+        return length > 0 &&
+               name.charAt(length-1) == ClassConstants.INTERNAL_PACKAGE_SEPARATOR ?
+                   name.substring(0, length -1) :
+                   name;
+    }
+
+
+    public boolean isDirectory()
+    {
+        return zipEntry.isDirectory();
     }
 
 
diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/src/proguard/obfuscate/AttributeShrinker.java
index e5a99e6..a8bc36b 100644
--- a/src/proguard/obfuscate/AttributeShrinker.java
+++ b/src/proguard/obfuscate/AttributeShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/src/proguard/obfuscate/AttributeUsageMarker.java
index d195386..e772324 100644
--- a/src/proguard/obfuscate/AttributeUsageMarker.java
+++ b/src/proguard/obfuscate/AttributeUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/src/proguard/obfuscate/ClassObfuscator.java
index 9e6ec83..9af0c82 100644
--- a/src/proguard/obfuscate/ClassObfuscator.java
+++ b/src/proguard/obfuscate/ClassObfuscator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -27,6 +27,7 @@ import proguard.classfile.constant.ClassConstant;
 import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.ClassVisitor;
+import proguard.util.*;
 
 import java.util.*;
 
@@ -49,24 +50,29 @@ implements   ClassVisitor,
     private final DictionaryNameFactory classNameFactory;
     private final DictionaryNameFactory packageNameFactory;
     private final boolean               useMixedCaseClassNames;
+    private final StringMatcher         keepPackageNamesMatcher;
     private final String                flattenPackageHierarchy;
     private final String                repackageClasses;
     private final boolean               allowAccessModification;
 
-    private final Set classNamesToAvoid                  = new HashSet();
+    private final Set classNamesToAvoid                       = new HashSet();
 
     // Map: [package prefix - new package prefix]
-    private final Map packagePrefixMap                   = new HashMap();
+    private final Map packagePrefixMap                        = new HashMap();
 
     // Map: [package prefix - package name factory]
-    private final Map packagePrefixPackageNameFactoryMap = new HashMap();
+    private final Map packagePrefixPackageNameFactoryMap      = new HashMap();
 
-    // Map: [package prefix - class name factory]
-    private final Map packagePrefixClassNameFactoryMap   = new HashMap();
+    // Map: [package prefix - numeric class name factory]
+    private final Map packagePrefixClassNameFactoryMap        = new HashMap();
 
-    // A field acting as a temporary variable and as a return value for names
-    // of outer classes.
-    private String newClassName;
+    // Map: [package prefix - numeric class name factory]
+    private final Map packagePrefixNumericClassNameFactoryMap = new HashMap();
+
+    // Field acting as temporary variables and as return values for names
+    // of outer classes and types of inner classes.
+    private String  newClassName;
+    private boolean numericClassName;
 
 
     /**
@@ -78,6 +84,8 @@ implements   ClassVisitor,
      *                                dictionary.
      * @param useMixedCaseClassNames  specifies whether obfuscated packages and
      *                                classes can get mixed-case names.
+     * @param keepPackageNames        the optional filter for which matching
+     *                                package names are kept.
      * @param flattenPackageHierarchy the base package if the obfuscated package
      *                                hierarchy is to be flattened.
      * @param repackageClasses        the base package if the obfuscated classes
@@ -89,6 +97,7 @@ implements   ClassVisitor,
                            DictionaryNameFactory classNameFactory,
                            DictionaryNameFactory packageNameFactory,
                            boolean               useMixedCaseClassNames,
+                           List                  keepPackageNames,
                            String                flattenPackageHierarchy,
                            String                repackageClasses,
                            boolean               allowAccessModification)
@@ -111,6 +120,8 @@ implements   ClassVisitor,
         }
 
         this.useMixedCaseClassNames  = useMixedCaseClassNames;
+        this.keepPackageNamesMatcher = keepPackageNames == null ? null :
+            new ListParser(new FileNameParser()).parse(keepPackageNames);
         this.flattenPackageHierarchy = flattenPackageHierarchy;
         this.repackageClasses        = repackageClasses;
         this.allowAccessModification = allowAccessModification;
@@ -137,14 +148,16 @@ implements   ClassVisitor,
             programClass.attributesAccept(this);
 
             // Figure out a package prefix. The package prefix may actually be
-            // the outer class prefix, if any, or it may be the fixed base
+            // the an 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.INTERNAL_INNER_CLASS_SEPARATOR :
                 newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName()));
 
-            // Come up with a new class name.
-            newClassName = generateUniqueClassName(newPackagePrefix);
+            // Come up with a new class name, numeric or ordinary.
+            newClassName = newClassName != null && numericClassName ?
+                generateUniqueNumericClassName(newPackagePrefix) :
+                generateUniqueClassName(newPackagePrefix);
 
             setNewClassName(programClass, newClassName);
         }
@@ -167,6 +180,12 @@ implements   ClassVisitor,
     {
         // Make sure the enclosing class has a name.
         enclosingMethodAttribute.referencedClassAccept(this);
+
+        String innerClassName = clazz.getName();
+        String outerClassName = clazz.getClassName(enclosingMethodAttribute.u2classIndex);
+
+        numericClassName = isNumericClassName(innerClassName,
+                                              outerClassName);
     }
 
 
@@ -178,11 +197,45 @@ implements   ClassVisitor,
         int innerClassIndex = innerClassesInfo.u2innerClassIndex;
         int outerClassIndex = innerClassesInfo.u2outerClassIndex;
         if (innerClassIndex != 0 &&
-            outerClassIndex != 0 &&
-            clazz.getClassName(innerClassIndex).equals(clazz.getName()))
+            outerClassIndex != 0)
+        {
+            String innerClassName = clazz.getClassName(innerClassIndex);
+            if (innerClassName.equals(clazz.getName()))
+            {
+                clazz.constantPoolEntryAccept(outerClassIndex, this);
+
+                String outerClassName = clazz.getClassName(outerClassIndex);
+
+                numericClassName = isNumericClassName(innerClassName,
+                                                      outerClassName);
+            }
+        }
+    }
+
+
+    /**
+     * Returns whether the given inner class name is a numeric name.
+     */
+    private boolean isNumericClassName(String innerClassName,
+                                       String outerClassName)
+    {
+        int innerClassNameStart  = outerClassName.length() + 1;
+        int innerClassNameLength = innerClassName.length();
+
+        if (innerClassNameStart >= innerClassNameLength)
         {
-            clazz.constantPoolEntryAccept(outerClassIndex, this);
+            return false;
         }
+
+        for (int index = innerClassNameStart; index < innerClassNameLength; index++)
+        {
+            if (!Character.isDigit(innerClassName.charAt(index)))
+            {
+                return false;
+            }
+        }
+
+        return true;
     }
 
 
@@ -217,7 +270,7 @@ implements   ClassVisitor,
                     String className = programClass.getName();
 
                     // Keep the package name for all other classes in the same
-                    // package. Do this resursively if we're not doing any
+                    // package. Do this recursively if we're not doing any
                     // repackaging.
                     mapPackageName(className,
                                    newClassName,
@@ -275,6 +328,15 @@ implements   ClassVisitor,
         String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
         if (newPackagePrefix == null)
         {
+            // Are we keeping the package name?
+            if (keepPackageNamesMatcher != null &&
+                keepPackageNamesMatcher.matches(packagePrefix.length() > 0 ?
+                    packagePrefix.substring(0, packagePrefix.length()-1) :
+                    packagePrefix))
+            {
+                return packagePrefix;
+            }
+
             // Are we forcing a new package prefix?
             if (repackageClasses != null)
             {
@@ -282,7 +344,7 @@ implements   ClassVisitor,
             }
 
             // Are we forcing a new superpackage prefix?
-            // Othewrise figure out the new superpackage prefix, recursively.
+            // Otherwise figure out the new superpackage prefix, recursively.
             String newSuperPackagePrefix = flattenPackageHierarchy != null ?
                 flattenPackageHierarchy :
                 newPackagePrefix(ClassUtil.internalPackagePrefix(packagePrefix));
@@ -322,6 +384,17 @@ implements   ClassVisitor,
                                                    packageNameFactory);
         }
 
+        return generateUniquePackagePrefix(newSuperPackagePrefix, packageNameFactory);
+    }
+
+
+    /**
+     * Creates a new package prefix in the given new superpackage, with the
+     * given package name factory.
+     */
+    private String generateUniquePackagePrefix(String      newSuperPackagePrefix,
+                                               NameFactory packageNameFactory)
+    {
         // Come up with package names until we get an original one.
         String newPackagePrefix;
         do
@@ -347,8 +420,8 @@ implements   ClassVisitor,
             (NameFactory)packagePrefixClassNameFactoryMap.get(newPackagePrefix);
         if (classNameFactory == null)
         {
-            // We haven't seen classes in this package before. Create a new name
-            // factory for them.
+            // We haven't seen classes in this package before.
+            // Create a new name factory for them.
             classNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
             if (this.classNameFactory != null)
             {
@@ -361,6 +434,39 @@ implements   ClassVisitor,
                                                  classNameFactory);
         }
 
+        return generateUniqueClassName(newPackagePrefix, classNameFactory);
+    }
+
+
+    /**
+     * Creates a new class name in the given new package.
+     */
+    private String generateUniqueNumericClassName(String newPackagePrefix)
+    {
+        // Find the right name factory for this package.
+        NameFactory classNameFactory =
+            (NameFactory)packagePrefixNumericClassNameFactoryMap.get(newPackagePrefix);
+        if (classNameFactory == null)
+        {
+            // We haven't seen classes in this package before.
+            // Create a new name factory for them.
+            classNameFactory = new NumericNameFactory();
+
+            packagePrefixNumericClassNameFactoryMap.put(newPackagePrefix,
+                                                        classNameFactory);
+        }
+
+        return generateUniqueClassName(newPackagePrefix, classNameFactory);
+    }
+
+
+    /**
+     * Creates a new class name in the given new package, with the given
+     * class name factory.
+     */
+    private String generateUniqueClassName(String      newPackagePrefix,
+                                           NameFactory classNameFactory)
+    {
         // Come up with class names until we get an original one.
         String newClassName;
         do
@@ -374,6 +480,7 @@ implements   ClassVisitor,
         return newClassName;
     }
 
+
     /**
      * Returns the given class name, unchanged if mixed-case class names are
      * allowed, or the lower-case version otherwise.
diff --git a/src/proguard/obfuscate/ClassRenamer.java b/src/proguard/obfuscate/ClassRenamer.java
index bde6982..143e3fb 100644
--- a/src/proguard/obfuscate/ClassRenamer.java
+++ b/src/proguard/obfuscate/ClassRenamer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/DictionaryNameFactory.java b/src/proguard/obfuscate/DictionaryNameFactory.java
index b0ca387..f262664 100644
--- a/src/proguard/obfuscate/DictionaryNameFactory.java
+++ b/src/proguard/obfuscate/DictionaryNameFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MapCleaner.java b/src/proguard/obfuscate/MapCleaner.java
index f1fd597..fdefec5 100644
--- a/src/proguard/obfuscate/MapCleaner.java
+++ b/src/proguard/obfuscate/MapCleaner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MappingKeeper.java b/src/proguard/obfuscate/MappingKeeper.java
index 2213b91..c9d6aa6 100644
--- a/src/proguard/obfuscate/MappingKeeper.java
+++ b/src/proguard/obfuscate/MappingKeeper.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -22,6 +22,7 @@ package proguard.obfuscate;
 
 import proguard.classfile.*;
 import proguard.classfile.util.*;
+import proguard.util.ListUtil;
 
 
 /**
@@ -75,7 +76,9 @@ public class MappingKeeper implements MappingProcessor
                 if (currentNewName != null &&
                     !currentNewName.equals(newName))
                 {
-                    warningPrinter.print("Warning: " +
+                    warningPrinter.print(name,
+                                         currentNewName,
+                                         "Warning: " +
                                          className +
                                          " is not being kept as '" +
                                          ClassUtil.externalClassName(currentNewName) +
@@ -116,7 +119,8 @@ public class MappingKeeper implements MappingProcessor
                     if (currentNewName != null &&
                         !currentNewName.equals(newFieldName))
                     {
-                        warningPrinter.print("Warning: " +
+                        warningPrinter.print(ClassUtil.internalClassName(className),
+                                             "Warning: " +
                                              className +
                                              ": field '" + fieldType + " " + fieldName +
                                              "' is not being kept as '" + currentNewName +
@@ -135,17 +139,17 @@ public class MappingKeeper implements MappingProcessor
                                      int    firstLineNumber,
                                      int    lastLineNumber,
                                      String methodReturnType,
-                                     String methodNameAndArguments,
+                                     String methodName,
+                                     String methodArguments,
                                      String newMethodName)
     {
         if (clazz != null)
         {
             // Find the method.
-            String name       = ClassUtil.externalMethodName(methodNameAndArguments);
             String descriptor = ClassUtil.internalMethodDescriptor(methodReturnType,
-                                                                   methodNameAndArguments);
+                                                                   ListUtil.commaSeparatedList(methodArguments));
 
-            Method method = clazz.findMethod(name, descriptor);
+            Method method = clazz.findMethod(methodName, descriptor);
             if (method != null)
             {
                 // Print out a warning if the mapping conflicts with a name that
@@ -156,9 +160,10 @@ public class MappingKeeper implements MappingProcessor
                     if (currentNewName != null &&
                         !currentNewName.equals(newMethodName))
                     {
-                        warningPrinter.print("Warning: " +
+                        warningPrinter.print(ClassUtil.internalClassName(className),
+                                             "Warning: " +
                                              className +
-                                             ": method '" + methodReturnType + " " + methodNameAndArguments +
+                                             ": method '" + methodReturnType + " " + methodName + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + methodArguments + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE +
                                              "' is not being kept as '" + currentNewName +
                                              "', but remapped to '" + newMethodName + "'");
                     }
diff --git a/src/proguard/obfuscate/MappingPrinter.java b/src/proguard/obfuscate/MappingPrinter.java
index 0327687..aa8b13e 100644
--- a/src/proguard/obfuscate/MappingPrinter.java
+++ b/src/proguard/obfuscate/MappingPrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MappingProcessor.java b/src/proguard/obfuscate/MappingProcessor.java
index 972d190..01c1809 100644
--- a/src/proguard/obfuscate/MappingProcessor.java
+++ b/src/proguard/obfuscate/MappingProcessor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -59,20 +59,21 @@ public interface MappingProcessor
     /**
      * Processes the given method name mapping.
      *
-     * @param className              the original class name.
-     * @param firstLineNumber        the first line number of the method, or
-     *                               0 if it is not known.
-     * @param lastLineNumber         the last line number of the method, or
-     *                               0 if it is not known.
-     * @param methodReturnType       the original external method return type.
-     * @param methodNameAndArguments the original external method name and
-     *                               arguments.
-     * @param newMethodName          the new method name.
+     * @param className        the original class name.
+     * @param firstLineNumber  the first line number of the method, or 0 if it
+     *                         is not known.
+     * @param lastLineNumber   the last line number of the method, or 0 if it
+     *                         is not known.
+     * @param methodReturnType the original external method return type.
+     * @param methodName       the original external method name.
+     * @param methodArguments  the original external method arguments.
+     * @param newMethodName    the new method name.
      */
     public void processMethodMapping(String className,
                                      int    firstLineNumber,
                                      int    lastLineNumber,
                                      String methodReturnType,
-                                     String methodNameAndArguments,
+                                     String methodName,
+                                     String methodArguments,
                                      String newMethodName);
 }
diff --git a/src/proguard/obfuscate/MappingReader.java b/src/proguard/obfuscate/MappingReader.java
index 1bbace3..24fd26c 100644
--- a/src/proguard/obfuscate/MappingReader.java
+++ b/src/proguard/obfuscate/MappingReader.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -63,9 +63,11 @@ public class MappingReader
                     break;
                 }
 
+                line = line.trim();
+
                 // The distinction between a class mapping and a class
                 // member mapping is the initial whitespace.
-                if (!line.startsWith("    "))
+                if (line.endsWith(":"))
                 {
                     // Process the class mapping and remember the class's
                     // old name.
@@ -108,8 +110,6 @@ public class MappingReader
         // See if we can parse "___ -> ___:", containing the original
         // class name and the new class name.
 
-        line = line.trim();
-
         int arrowIndex = line.indexOf("->");
         if (arrowIndex < 0)
         {
@@ -141,49 +141,58 @@ public class MappingReader
                                            String           line,
                                            MappingProcessor mappingProcessor)
     {
-        // See if we can parse "    ___:___:___ ___ -> ___",
+        // See if we can parse "___:___:___ ___(___) -> ___",
         // containing the optional line numbers, the return type, the original
-        // field/method name (including arguments), and the new method name.
-
-        line = line.trim();
-
-        int colonIndex1 = line.indexOf(':');
-        int colonIndex2 = line.indexOf(':', colonIndex1 + 1);
+        // field/method name, optional arguments, and the new field/method name.
 
-        int spaceIndex = line.indexOf(' ', colonIndex2 + 1);
-        if (spaceIndex < 0)
-        {
-            return;
-        }
+        int colonIndex1    =                           line.indexOf(':');
+        int colonIndex2    = colonIndex1    < 0 ? -1 : line.indexOf(':', colonIndex1    + 1);
+        int spaceIndex     =                           line.indexOf(' ', colonIndex2    + 2);
+        int argumentIndex1 =                           line.indexOf('(', spaceIndex     + 1);
+        int argumentIndex2 = argumentIndex1 < 0 ? -1 : line.indexOf(')', argumentIndex1 + 1);
+        int arrowIndex     =                           line.indexOf("->", Math.max(spaceIndex, argumentIndex2) + 1);
 
-        int arrowIndex = line.indexOf("->", spaceIndex + 1);
-        if (arrowIndex < 1)
+        if (spaceIndex < 0 ||
+            arrowIndex < 0)
         {
             return;
         }
 
-        int firstLineNumber = colonIndex1 < 0 ? 0 :
-            Integer.parseInt(line.substring(0, colonIndex1).trim());
-
-        int lastLineNumber = colonIndex1 < 0 ||
-                             colonIndex2 < 0 ? 0 :
-            Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
-
         // Extract the elements.
         String type    = line.substring(colonIndex2 + 1, spaceIndex).trim();
-        String name    = line.substring(spaceIndex + 1, arrowIndex).trim();
+        String name    = line.substring(spaceIndex + 1, argumentIndex1 >= 0 ? argumentIndex1 : arrowIndex).trim();
         String newName = line.substring(arrowIndex + 2).trim();
 
         // Process this class member mapping.
-        if (type.length() > 0 && name.length() > 0)
+        if (type.length()    > 0 &&
+            name.length()    > 0 &&
+            newName.length() > 0)
         {
-            if (name.charAt(name.length() - 1) != ')')
+            // Is it a field or a method?
+            if (argumentIndex2 < 0)
             {
                 mappingProcessor.processFieldMapping(className, type, name, newName);
             }
             else
             {
-                mappingProcessor.processMethodMapping(className, firstLineNumber, lastLineNumber, type, name, newName);
+                int firstLineNumber = 0;
+                int lastLineNumber  = 0;
+
+                if (colonIndex2 > 0)
+                {
+                    firstLineNumber = Integer.parseInt(line.substring(0, colonIndex1).trim());
+                    lastLineNumber  = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
+                }
+
+                String arguments = line.substring(argumentIndex1 + 1, argumentIndex2).trim();
+
+                mappingProcessor.processMethodMapping(className,
+                                                      firstLineNumber,
+                                                      lastLineNumber,
+                                                      type,
+                                                      name,
+                                                      arguments,
+                                                      newName);
             }
         }
     }
diff --git a/src/proguard/obfuscate/MemberNameCleaner.java b/src/proguard/obfuscate/MemberNameCleaner.java
index 646d1e5..c41c59d 100644
--- a/src/proguard/obfuscate/MemberNameCleaner.java
+++ b/src/proguard/obfuscate/MemberNameCleaner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MemberNameCollector.java b/src/proguard/obfuscate/MemberNameCollector.java
index a168647..c248820 100644
--- a/src/proguard/obfuscate/MemberNameCollector.java
+++ b/src/proguard/obfuscate/MemberNameCollector.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MemberNameConflictFixer.java b/src/proguard/obfuscate/MemberNameConflictFixer.java
index d3bb02c..b9093a6 100644
--- a/src/proguard/obfuscate/MemberNameConflictFixer.java
+++ b/src/proguard/obfuscate/MemberNameConflictFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -136,7 +136,8 @@ public class MemberNameConflictFixer implements MemberVisitor
                 warningPrinter != null)
             {
                 descriptor = member.getDescriptor(clazz);
-                warningPrinter.print("Warning: " + ClassUtil.externalClassName(clazz.getName()) +
+                warningPrinter.print(clazz.getName(),
+                                     "Warning: " + ClassUtil.externalClassName(clazz.getName()) +
                                                    (isField ?
                                                        ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) :
                                                        ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) +
diff --git a/src/proguard/obfuscate/MemberObfuscator.java b/src/proguard/obfuscate/MemberObfuscator.java
index bdd11f7..332b849 100644
--- a/src/proguard/obfuscate/MemberObfuscator.java
+++ b/src/proguard/obfuscate/MemberObfuscator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MemberSpecialNameFilter.java b/src/proguard/obfuscate/MemberSpecialNameFilter.java
index 4f252d4..f83374b 100644
--- a/src/proguard/obfuscate/MemberSpecialNameFilter.java
+++ b/src/proguard/obfuscate/MemberSpecialNameFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/src/proguard/obfuscate/MultiMappingProcessor.java
index 777e3f8..4074ff8 100644
--- a/src/proguard/obfuscate/MultiMappingProcessor.java
+++ b/src/proguard/obfuscate/MultiMappingProcessor.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -78,7 +78,8 @@ public class MultiMappingProcessor implements MappingProcessor
                                      int    firstLineNumber,
                                      int    lastLineNumber,
                                      String methodReturnType,
-                                     String methodNameAndArguments,
+                                     String methodName,
+                                     String methodArguments,
                                      String newMethodName)
     {
         for (int index = 0; index < mappingProcessors.length; index++)
@@ -87,7 +88,8 @@ public class MultiMappingProcessor implements MappingProcessor
                                                           firstLineNumber,
                                                           lastLineNumber,
                                                           methodReturnType,
-                                                          methodNameAndArguments,
+                                                          methodName,
+                                                          methodArguments,
                                                           newMethodName);
         }
     }
diff --git a/src/proguard/obfuscate/NameAndTypeShrinker.java b/src/proguard/obfuscate/NameAndTypeShrinker.java
index 812768b..1284c82 100644
--- a/src/proguard/obfuscate/NameAndTypeShrinker.java
+++ b/src/proguard/obfuscate/NameAndTypeShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/NameAndTypeUsageMarker.java b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
index a179781..cc779f0 100644
--- a/src/proguard/obfuscate/NameAndTypeUsageMarker.java
+++ b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NameFactory.java
index 095b64d..c64d1ad 100644
--- a/src/proguard/obfuscate/NameFactory.java
+++ b/src/proguard/obfuscate/NameFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/NameFactoryResetter.java b/src/proguard/obfuscate/NameFactoryResetter.java
index ae51e7f..b6ba6ad 100644
--- a/src/proguard/obfuscate/NameFactoryResetter.java
+++ b/src/proguard/obfuscate/NameFactoryResetter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java
index 302893e..2ce0ee9 100644
--- a/src/proguard/obfuscate/NameMarker.java
+++ b/src/proguard/obfuscate/NameMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NumericNameFactory.java
similarity index 67%
copy from src/proguard/obfuscate/NameFactory.java
copy to src/proguard/obfuscate/NumericNameFactory.java
index 095b64d..cc21c4b 100644
--- a/src/proguard/obfuscate/NameFactory.java
+++ b/src/proguard/obfuscate/NumericNameFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,15 +20,30 @@
  */
 package proguard.obfuscate;
 
+import java.util.*;
+
+
 /**
- * This interfaces provides methods to generate unique sequences of names.
- * The names must be valid Java identifiers.
+ * This <code>NameFactory</code> generates unique numeric names, starting at
+ * "1".
  *
  * @author Eric Lafortune
  */
-public interface NameFactory
+public class NumericNameFactory implements NameFactory
 {
-    public void reset();
+    private int index;
+
+
+    // Implementations for NameFactory.
+
+    public void reset()
+    {
+        index = 0;
+    }
+
 
-    public String nextName();
-}
+    public String nextName()
+    {
+        return Integer.toString(++index);
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/obfuscate/Obfuscator.java b/src/proguard/obfuscate/Obfuscator.java
index 04a80b5..dce563a 100644
--- a/src/proguard/obfuscate/Obfuscator.java
+++ b/src/proguard/obfuscate/Obfuscator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -70,25 +70,6 @@ public class Obfuscator
         programClassPool.classesAccept(new ClassCleaner());
         libraryClassPool.classesAccept(new ClassCleaner());
 
-        // Mark attributes that have to be kept.
-        AttributeUsageMarker requiredAttributeUsageMarker =
-            new AttributeUsageMarker();
-
-        AttributeVisitor optionalAttributeUsageMarker =
-            configuration.keepAttributes == null     ? null :
-            configuration.keepAttributes.size() == 0 ?
-                (AttributeVisitor)requiredAttributeUsageMarker :
-                (AttributeVisitor)new AttributeNameFilter(new ListParser(new NameParser()).parse(configuration.keepAttributes),
-                                                          requiredAttributeUsageMarker);
-
-        programClassPool.classesAccept(
-            new AllAttributeVisitor(true,
-            new RequiredAttributeFilter(requiredAttributeUsageMarker,
-                                        optionalAttributeUsageMarker)));
-
-        // Remove the attributes that can be discarded.
-        programClassPool.classesAccept(new AttributeShrinker());
-
         // If the class member names have to correspond globally,
         // link all class members in all classes, otherwise
         // link all non-private methods in all class hierarchies.
@@ -117,13 +98,30 @@ public class Obfuscator
         libraryClassPool.classesAccept(nameMarker);
         libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker));
 
+        // Mark attributes that have to be kept.
+        AttributeUsageMarker requiredAttributeUsageMarker =
+            new AttributeUsageMarker();
+
+        AttributeVisitor optionalAttributeUsageMarker =
+            configuration.keepAttributes == null ? null :
+                new AttributeNameFilter(new ListParser(new NameParser()).parse(configuration.keepAttributes),
+                                        requiredAttributeUsageMarker);
+
+        programClassPool.classesAccept(
+            new AllAttributeVisitor(true,
+            new RequiredAttributeFilter(requiredAttributeUsageMarker,
+                                        optionalAttributeUsageMarker)));
+
+        // Remove the attributes that can be discarded. Note that the attributes
+        // may only be discarded after the seeds have been marked, since the
+        // configuration may rely on annotations.
+        programClassPool.classesAccept(new AttributeShrinker());
+
         // Apply the mapping, if one has been specified. The mapping can
         // override the names of library classes and of library class members.
         if (configuration.applyMapping != null)
         {
-            WarningPrinter warningPrinter = configuration.warn ?
-                new WarningPrinter(System.err) :
-                null;
+            WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
 
             MappingReader reader = new MappingReader(configuration.applyMapping);
 
@@ -136,22 +134,19 @@ public class Obfuscator
 
             reader.pump(keeper);
 
-            if (warningPrinter != null)
+            // Print out a summary of the warnings if necessary.
+            int mappingWarningCount = warningPrinter.getWarningCount();
+            if (mappingWarningCount > 0)
             {
-                // Print out a summary of the warnings if necessary.
-                int mappingWarningCount = warningPrinter.getWarningCount();
-                if (mappingWarningCount > 0)
+                System.err.println("Warning: there were " + mappingWarningCount +
+                                                            " kept classes and class members that were remapped anyway.");
+                System.err.println("         You should adapt your configuration or edit the mapping file.");
+
+                if (!configuration.ignoreWarnings)
                 {
-                    System.err.println("Warning: there were " + mappingWarningCount +
-                                       " kept classes and class members that were remapped anyway.");
-                    System.err.println("         You should adapt your configuration or edit the mapping file.");
-
-                    if (!configuration.ignoreWarnings)
-                    {
-                        System.err.println("         If you are sure this remapping won't hurt,");
-                        System.err.println("         you could try your luck using the '-ignorewarnings' option.");
-                        throw new IOException("Please correct the above warnings first.");
-                    }
+                    System.err.println("         If you are sure this remapping won't hurt,");
+                    System.err.println("         you could try your luck using the '-ignorewarnings' option.");
+                    throw new IOException("Please correct the above warnings first.");
                 }
             }
         }
@@ -170,6 +165,7 @@ public class Obfuscator
                                 classNameFactory,
                                 packageNameFactory,
                                 configuration.useMixedCaseClassNames,
+                                configuration.keepPackageNames,
                                 configuration.flattenPackageHierarchy,
                                 configuration.repackageClasses,
                                 configuration.allowAccessModification));
@@ -183,9 +179,7 @@ public class Obfuscator
                                                     nameFactory);
         }
 
-        WarningPrinter warningPrinter = configuration.warn ?
-            new WarningPrinter(System.err) :
-            null;
+        WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
 
         // Maintain a map of names to avoid [descriptor - new name - old name].
         Map descriptorMap = new HashMap();
@@ -356,21 +350,18 @@ public class Obfuscator
             }));
 
         // Print out any warnings about member name conflicts.
-        if (warningPrinter != null)
+        int warningCount = warningPrinter.getWarningCount();
+        if (warningCount > 0)
         {
-            int warningCount = warningPrinter.getWarningCount();
-            if (warningCount > 0)
-            {
-                System.err.println("Warning: there were " + warningCount +
-                                   " conflicting class member name mappings.");
-                System.err.println("         Your configuration may be inconsistent.");
+            System.err.println("Warning: there were " + warningCount +
+                               " conflicting class member name mappings.");
+            System.err.println("         Your configuration may be inconsistent.");
 
-                if (!configuration.ignoreWarnings)
-                {
-                    System.err.println("         If you are sure the conflicts are harmless,");
-                    System.err.println("         you could try your luck using the '-ignorewarnings' option.");
-                    throw new IOException("Please correct the above warnings first.");
-                }
+            if (!configuration.ignoreWarnings)
+            {
+                System.err.println("         If you are sure the conflicts are harmless,");
+                System.err.println("         you could try your luck using the '-ignorewarnings' option.");
+                throw new IOException("Please correct the above warnings first.");
             }
         }
 
diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/src/proguard/obfuscate/SimpleNameFactory.java
index 211dbc3..bce22de 100644
--- a/src/proguard/obfuscate/SimpleNameFactory.java
+++ b/src/proguard/obfuscate/SimpleNameFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/SourceFileRenamer.java b/src/proguard/obfuscate/SourceFileRenamer.java
index daf4df6..cbf1b63 100644
--- a/src/proguard/obfuscate/SourceFileRenamer.java
+++ b/src/proguard/obfuscate/SourceFileRenamer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -62,12 +62,6 @@ implements   ClassVisitor,
     }
 
 
-    public void visitLibraryClass(LibraryClass libraryClass)
-    {
-        // Library classes don't have attributes.
-    }
-
-
     // Implementations for AttributeVisitor.
 
     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
diff --git a/src/proguard/obfuscate/SpecialNameFactory.java b/src/proguard/obfuscate/SpecialNameFactory.java
index 64607aa..a5431ca 100644
--- a/src/proguard/obfuscate/SpecialNameFactory.java
+++ b/src/proguard/obfuscate/SpecialNameFactory.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/Utf8Shrinker.java b/src/proguard/obfuscate/Utf8Shrinker.java
index 9ceb02c..87ada80 100644
--- a/src/proguard/obfuscate/Utf8Shrinker.java
+++ b/src/proguard/obfuscate/Utf8Shrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/obfuscate/Utf8UsageMarker.java b/src/proguard/obfuscate/Utf8UsageMarker.java
index b521193..c59ebb8 100644
--- a/src/proguard/obfuscate/Utf8UsageMarker.java
+++ b/src/proguard/obfuscate/Utf8UsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/src/proguard/optimize/ChangedCodePrinter.java
index 29d21ee..67a79ab 100644
--- a/src/proguard/optimize/ChangedCodePrinter.java
+++ b/src/proguard/optimize/ChangedCodePrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/ConstantMemberFilter.java b/src/proguard/optimize/ConstantMemberFilter.java
index 4058eb1..56437c3 100644
--- a/src/proguard/optimize/ConstantMemberFilter.java
+++ b/src/proguard/optimize/ConstantMemberFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -29,7 +29,7 @@ import proguard.optimize.evaluation.StoringInvocationUnit;
 /**
  * This <code>MemberVisitor</code> delegates its visits to program class members
  * to another given <code>MemberVisitor</code>, but only when the visited
- * field has been marked as a constant.
+ * class member has been marked as a constant.
  *
  * @see StoringInvocationUnit
  * @author Eric Lafortune
diff --git a/src/proguard/optimize/ConstantParameterFilter.java b/src/proguard/optimize/ConstantParameterFilter.java
new file mode 100644
index 0000000..24a7040
--- /dev/null
+++ b/src/proguard/optimize/ConstantParameterFilter.java
@@ -0,0 +1,79 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.evaluation.value.Value;
+import proguard.optimize.evaluation.StoringInvocationUnit;
+import proguard.optimize.info.ParameterUsageMarker;
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to program methods
+ * to another given <code>MemberVisitor</code>, for each method parameter
+ * that has been marked as constant.
+ *
+ * @see StoringInvocationUnit
+ * @author Eric Lafortune
+ */
+public class ConstantParameterFilter
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private final MemberVisitor constantParameterVisitor;
+
+
+    /**
+     * Creates a new ConstantParameterFilter.
+     * @param constantParameterVisitor the <code>MemberVisitor</code> to which
+     *                                 visits will be delegated.
+     */
+    public ConstantParameterFilter(MemberVisitor constantParameterVisitor)
+    {
+        this.constantParameterVisitor = constantParameterVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // All parameters of non-static methods are shifted by one in the local
+        // variable frame.
+        int firstParameterIndex =
+            (programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
+                0 : 1;
+
+        int parameterCount =
+            ClassUtil.internalMethodParameterCount(programMethod.getDescriptor(programClass));
+
+        for (int index = firstParameterIndex; index < parameterCount; index++)
+        {
+            Value value = StoringInvocationUnit.getMethodParameterValue(programMethod, index);
+            if (value != null &&
+                value.isParticular())
+            {
+                constantParameterVisitor.visitProgramMethod(programClass, programMethod);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/DuplicateInitializerFixer.java b/src/proguard/optimize/DuplicateInitializerFixer.java
index 27c655b..746d182 100644
--- a/src/proguard/optimize/DuplicateInitializerFixer.java
+++ b/src/proguard/optimize/DuplicateInitializerFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java
index 23973c9..ca24481 100644
--- a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java
+++ b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java
index 704bde5..4297996 100644
--- a/src/proguard/optimize/KeepMarker.java
+++ b/src/proguard/optimize/KeepMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/src/proguard/optimize/MemberDescriptorSpecializer.java
index 1e1ba75..0d0b841 100644
--- a/src/proguard/optimize/MemberDescriptorSpecializer.java
+++ b/src/proguard/optimize/MemberDescriptorSpecializer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/src/proguard/optimize/MethodDescriptorShrinker.java
index 52c1134..48374e7 100644
--- a/src/proguard/optimize/MethodDescriptorShrinker.java
+++ b/src/proguard/optimize/MethodDescriptorShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -47,7 +47,7 @@ implements   MemberVisitor,
     private static final boolean DEBUG = false;
 
 
-    private final MemberVisitor extraParameterMemberVisitor;
+    private final MemberVisitor extraMemberVisitor;
 
 
     /**
@@ -61,13 +61,12 @@ implements   MemberVisitor,
 
     /**
      * Creates a new MethodDescriptorShrinker with an extra visitor.
-     * @param extraParameterMemberVisitor an optional extra visitor for all
-     *                                    methods whose parameters have been
-     *                                    simplified.
+     * @param extraMemberVisitor an optional extra visitor for all methods whose
+     *                           parameters have been simplified.
      */
-    public MethodDescriptorShrinker(MemberVisitor extraParameterMemberVisitor)
+    public MethodDescriptorShrinker(MemberVisitor extraMemberVisitor)
     {
-        this.extraParameterMemberVisitor = extraParameterMemberVisitor;
+        this.extraMemberVisitor = extraMemberVisitor;
     }
 
 
@@ -124,9 +123,9 @@ implements   MemberVisitor,
                 constantPoolEditor.addUtf8Constant(newDescriptor);
 
             // Visit the method, if required.
-            if (extraParameterMemberVisitor != null)
+            if (extraMemberVisitor != null)
             {
-                extraParameterMemberVisitor.visitProgramMethod(programClass, programMethod);
+                extraMemberVisitor.visitProgramMethod(programClass, programMethod);
             }
         }
     }
diff --git a/src/proguard/optimize/MethodStaticizer.java b/src/proguard/optimize/MethodStaticizer.java
index 15518a3..8dd11e1 100644
--- a/src/proguard/optimize/MethodStaticizer.java
+++ b/src/proguard/optimize/MethodStaticizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/OptimizationInfoMemberFilter.java b/src/proguard/optimize/OptimizationInfoMemberFilter.java
index f91f6bf..8760aee 100644
--- a/src/proguard/optimize/OptimizationInfoMemberFilter.java
+++ b/src/proguard/optimize/OptimizationInfoMemberFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java
index 8d61e10..a3e8a6e 100644
--- a/src/proguard/optimize/Optimizer.java
+++ b/src/proguard/optimize/Optimizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -28,12 +28,15 @@ import proguard.classfile.editor.*;
 import proguard.classfile.instruction.visitor.*;
 import proguard.classfile.util.MethodLinker;
 import proguard.classfile.visitor.*;
+import proguard.evaluation.*;
 import proguard.evaluation.value.*;
 import proguard.optimize.evaluation.*;
 import proguard.optimize.info.*;
 import proguard.optimize.peephole.*;
+import proguard.util.*;
 
 import java.io.IOException;
+import java.util.*;
 
 /**
  * This class optimizes class pools according to a given configuration.
@@ -42,6 +45,66 @@ import java.io.IOException;
  */
 public class Optimizer
 {
+    private static final String CLASS_MARKING_FINAL            = "class/marking/final";
+    private static final String CLASS_MERGING_VERTICAL         = "class/merging/vertical";
+    private static final String CLASS_MERGING_HORIZONTAL       = "class/merging/horizontal";
+    private static final String FIELD_REMOVAL_WRITEONLY        = "field/removal/writeonly";
+    private static final String FIELD_MARKING_PRIVATE          = "field/marking/private";
+    private static final String FIELD_PROPAGATION_VALUE        = "field/propagation/value";
+    private static final String METHOD_MARKING_PRIVATE         = "method/marking/private";
+    private static final String METHOD_MARKING_STATIC          = "method/marking/static";
+    private static final String METHOD_MARKING_FINAL           = "method/marking/final";
+    private static final String METHOD_REMOVAL_PARAMETER       = "method/removal/parameter";
+    private static final String METHOD_PROPAGATION_PARAMETER   = "method/propagation/parameter";
+    private static final String METHOD_PROPAGATION_RETURNVALUE = "method/propagation/returnvalue";
+    private static final String METHOD_INLINING_SHORT          = "method/inlining/short";
+    private static final String METHOD_INLINING_UNIQUE         = "method/inlining/unique";
+    private static final String METHOD_INLINING_TAILRECURSION  = "method/inlining/tailrecursion";
+    private static final String CODE_MERGING                   = "code/merging";
+    private static final String CODE_SIMPLIFICATION_VARIABLE   = "code/simplification/variable";
+    private static final String CODE_SIMPLIFICATION_ARITHMETIC = "code/simplification/arithmetic";
+    private static final String CODE_SIMPLIFICATION_CAST       = "code/simplification/cast";
+    private static final String CODE_SIMPLIFICATION_FIELD      = "code/simplification/field";
+    private static final String CODE_SIMPLIFICATION_BRANCH     = "code/simplification/branch";
+    private static final String CODE_SIMPLIFICATION_ADVANCED   = "code/simplification/advanced";
+    private static final String CODE_REMOVAL_ADVANCED          = "code/removal/advanced";
+    private static final String CODE_REMOVAL_SIMPLE            = "code/removal/simple";
+    private static final String CODE_REMOVAL_VARIABLE          = "code/removal/variable";
+    private static final String CODE_REMOVAL_EXCEPTION         = "code/removal/exception";
+    private static final String CODE_ALLOCATION_VARIABLE       = "code/allocation/variable";
+
+
+    public static final String[] OPTIMIZATION_NAMES = new String[]
+    {
+        CLASS_MARKING_FINAL,
+        CLASS_MERGING_VERTICAL,
+        CLASS_MERGING_HORIZONTAL,
+        FIELD_REMOVAL_WRITEONLY,
+        FIELD_PROPAGATION_VALUE,
+        METHOD_MARKING_PRIVATE,
+        METHOD_MARKING_STATIC,
+        METHOD_MARKING_FINAL,
+        METHOD_REMOVAL_PARAMETER,
+        METHOD_PROPAGATION_PARAMETER,
+        METHOD_PROPAGATION_RETURNVALUE,
+        METHOD_INLINING_SHORT,
+        METHOD_INLINING_UNIQUE,
+        METHOD_INLINING_TAILRECURSION,
+        CODE_MERGING,
+        CODE_SIMPLIFICATION_VARIABLE,
+        CODE_SIMPLIFICATION_ARITHMETIC,
+        CODE_SIMPLIFICATION_CAST,
+        CODE_SIMPLIFICATION_FIELD,
+        CODE_SIMPLIFICATION_BRANCH,
+        CODE_SIMPLIFICATION_ADVANCED,
+        CODE_REMOVAL_ADVANCED,
+        CODE_REMOVAL_SIMPLE,
+        CODE_REMOVAL_VARIABLE,
+        CODE_REMOVAL_EXCEPTION,
+        CODE_ALLOCATION_VARIABLE,
+    };
+
+
     private final Configuration configuration;
 
 
@@ -68,29 +131,90 @@ public class Optimizer
             throw new IOException("You have to specify '-keep' options for the optimization step.");
         }
 
+        // Create a matcher for filtering optimizations.
+        StringMatcher filter = configuration.optimizations != null ?
+            new ListParser(new NameParser()).parse(configuration.optimizations) :
+            new ConstantMatcher(true);
+
+        boolean classMarkingFinal            = filter.matches(CLASS_MARKING_FINAL);
+        boolean classMergingVertical         = filter.matches(CLASS_MERGING_VERTICAL);
+        boolean classMergingHorizontal       = filter.matches(CLASS_MERGING_HORIZONTAL);
+        boolean fieldRemovalWriteonly        = filter.matches(FIELD_REMOVAL_WRITEONLY);
+        boolean fieldMarkingPrivate          = filter.matches(FIELD_MARKING_PRIVATE);
+        boolean fieldPropagationValue        = filter.matches(FIELD_PROPAGATION_VALUE);
+        boolean methodMarkingPrivate         = filter.matches(METHOD_MARKING_PRIVATE);
+        boolean methodMarkingStatic          = filter.matches(METHOD_MARKING_STATIC);
+        boolean methodMarkingFinal           = filter.matches(METHOD_MARKING_FINAL);
+        boolean methodRemovalParameter       = filter.matches(METHOD_REMOVAL_PARAMETER);
+        boolean methodPropagationParameter   = filter.matches(METHOD_PROPAGATION_PARAMETER);
+        boolean methodPropagationReturnvalue = filter.matches(METHOD_PROPAGATION_RETURNVALUE);
+        boolean methodInliningShort          = filter.matches(METHOD_INLINING_SHORT);
+        boolean methodInliningUnique         = filter.matches(METHOD_INLINING_UNIQUE);
+        boolean methodInliningTailrecursion  = filter.matches(METHOD_INLINING_TAILRECURSION);
+        boolean codeMerging                  = filter.matches(CODE_MERGING);
+        boolean codeSimplificationVariable   = filter.matches(CODE_SIMPLIFICATION_VARIABLE);
+        boolean codeSimplificationArithmetic = filter.matches(CODE_SIMPLIFICATION_ARITHMETIC);
+        boolean codeSimplificationCast       = filter.matches(CODE_SIMPLIFICATION_CAST);
+        boolean codeSimplificationField      = filter.matches(CODE_SIMPLIFICATION_FIELD);
+        boolean codeSimplificationBranch     = filter.matches(CODE_SIMPLIFICATION_BRANCH);
+        boolean codeSimplificationAdvanced   = filter.matches(CODE_SIMPLIFICATION_ADVANCED);
+        boolean codeRemovalAdvanced          = filter.matches(CODE_REMOVAL_ADVANCED);
+        boolean codeRemovalSimple            = filter.matches(CODE_REMOVAL_SIMPLE);
+        boolean codeRemovalVariable          = filter.matches(CODE_REMOVAL_VARIABLE);
+        boolean codeRemovalException         = filter.matches(CODE_REMOVAL_EXCEPTION);
+        boolean codeAllocationVariable       = filter.matches(CODE_ALLOCATION_VARIABLE);
+
         // Create counters to count the numbers of optimizations.
-        ClassCounter       classMergeCounter       = new ClassCounter();
-        ClassCounter       finalClassCounter       = new ClassCounter();
-        MemberCounter      finalMethodCounter      = new MemberCounter();
-        MemberCounter      privateFieldCounter     = new MemberCounter();
-        MemberCounter      privateMethodCounter    = new MemberCounter();
-        MemberCounter      staticMethodCounter     = new MemberCounter();
-        MemberCounter      writeOnlyFieldCounter   = new MemberCounter();
-        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();
-        InstructionCounter inliningCounter         = new InstructionCounter();
-        InstructionCounter tailRecursionCounter    = new InstructionCounter();
-        InstructionCounter commonCodeCounter       = new InstructionCounter();
-        InstructionCounter pushCounter             = new InstructionCounter();
-        InstructionCounter branchCounter           = new InstructionCounter();
-        InstructionCounter deletedCounter          = new InstructionCounter();
-        InstructionCounter addedCounter            = new InstructionCounter();
-        InstructionCounter peepholeCounter         = new InstructionCounter();
+        ClassCounter       classMarkingFinalCounter            = new ClassCounter();
+        ClassCounter       classMergingVerticalCounter         = new ClassCounter();
+        ClassCounter       classMergingHorizontalCounter       = new ClassCounter();
+        MemberCounter      fieldRemovalWriteonlyCounter        = new MemberCounter();
+        MemberCounter      fieldMarkingPrivateCounter          = new MemberCounter();
+        MemberCounter      fieldPropagationValueCounter        = new MemberCounter();
+        MemberCounter      methodMarkingPrivateCounter         = new MemberCounter();
+        MemberCounter      methodMarkingStaticCounter          = new MemberCounter();
+        MemberCounter      methodMarkingFinalCounter           = new MemberCounter();
+        MemberCounter      methodRemovalParameterCounter       = new MemberCounter();
+        MemberCounter      methodPropagationParameterCounter   = new MemberCounter();
+        MemberCounter      methodPropagationReturnvalueCounter = new MemberCounter();
+        InstructionCounter methodInliningShortCounter          = new InstructionCounter();
+        InstructionCounter methodInliningUniqueCounter         = new InstructionCounter();
+        InstructionCounter methodInliningTailrecursionCounter  = new InstructionCounter();
+        InstructionCounter codeMergingCounter                  = new InstructionCounter();
+        InstructionCounter codeSimplificationVariableCounter   = new InstructionCounter();
+        InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter();
+        InstructionCounter codeSimplificationCastCounter       = new InstructionCounter();
+        InstructionCounter codeSimplificationFieldCounter      = new InstructionCounter();
+        InstructionCounter codeSimplificationBranchCounter     = new InstructionCounter();
+        InstructionCounter codeSimplificationAdvancedCounter   = new InstructionCounter();
+        InstructionCounter deletedCounter                      = new InstructionCounter();
+        InstructionCounter addedCounter                        = new InstructionCounter();
+        MemberCounter      codeRemovalVariableCounter          = new MemberCounter();
+        ExceptionCounter   codeRemovalExceptionCounter         = new ExceptionCounter();
+        MemberCounter      codeAllocationVariableCounter       = new MemberCounter();
+        MemberCounter      initializerFixCounter               = new MemberCounter();
+
+        // Some optimizations are required by other optimizations.
+        codeSimplificationAdvanced =
+            codeSimplificationAdvanced ||
+            fieldPropagationValue      ||
+            methodPropagationParameter ||
+            methodPropagationReturnvalue;
+
+        codeRemovalAdvanced =
+            codeRemovalAdvanced   ||
+            fieldRemovalWriteonly ||
+            methodMarkingStatic   ||
+            methodRemovalParameter;
+
+        codeRemovalSimple =
+            codeRemovalSimple ||
+            codeSimplificationBranch;
+
+        codeRemovalException =
+            codeRemovalException ||
+            codeRemovalAdvanced ||
+            codeRemovalSimple;
 
         // Clean up any old visitor info.
         programClassPool.classesAccept(new ClassCleaner());
@@ -120,14 +244,16 @@ public class Optimizer
         libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker));
 
         // We also keep all classes that are involved in .class constructs.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new AllInstructionVisitor(
-                                       new DotClassClassVisitor(keepMarker)))));
+        programClassPool.classesAccept(
+            new AllMethodVisitor(
+            new AllAttributeVisitor(
+            new AllInstructionVisitor(
+            new DotClassClassVisitor(keepMarker)))));
 
         // We also keep all classes that are involved in Class.forName constructs.
-        programClassPool.classesAccept(new AllConstantVisitor(
-                                       new ClassForNameClassVisitor(keepMarker)));
+        programClassPool.classesAccept(
+            new AllConstantVisitor(
+            new ClassForNameClassVisitor(keepMarker)));
 
         // Attach some optimization info to all classes and class members, so
         // it can be filled out later.
@@ -150,19 +276,49 @@ public class Optimizer
             libraryClassPool.accept(noClassPoolvisitor);
         }
 
-        // Make classes and methods final, as far as possible.
-        programClassPool.classesAccept(new ClassFinalizer(finalClassCounter, finalMethodCounter));
+        if (classMarkingFinal)
+        {
+            // Make classes final, whereever possible.
+            programClassPool.classesAccept(
+                new ClassFinalizer(classMarkingFinalCounter));
+        }
+
+        if (methodMarkingFinal)
+        {
+            // Make methods final, whereever possible.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new MethodFinalizer(methodMarkingFinalCounter)));
+        }
 
-        // Mark all fields that are write-only.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new AllInstructionVisitor(
-                                       new ReadWriteFieldMarker()))));
+        if (fieldRemovalWriteonly)
+        {
+            // Mark all fields that are write-only.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new AllInstructionVisitor(
+                new ReadWriteFieldMarker()))));
+
+            // Count the write-only fields.
+            programClassPool.classesAccept(
+                new AllFieldVisitor(
+                new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter)));
+        }
+        else
+        {
+            // Mark all fields as read/write.
+            programClassPool.classesAccept(
+                new AllFieldVisitor(
+                new ReadWriteFieldMarker()));
+        }
 
         // Mark all used parameters, including the 'this' parameters.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new OptimizationInfoMemberFilter(
-                                       new ParameterUsageMarker())));
+        programClassPool.classesAccept(
+            new AllMethodVisitor(
+            new OptimizationInfoMemberFilter(
+            new ParameterUsageMarker(!methodMarkingStatic,
+                                     !methodRemovalParameter))));
 
         // Mark all methods that have side effects.
         programClassPool.accept(new SideEffectMethodMarker());
@@ -174,53 +330,116 @@ public class Optimizer
         // and method return values.
         ValueFactory valueFactory = new IdentifiedValueFactory();
 
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new PartialEvaluator(valueFactory, new StoringInvocationUnit(valueFactory), 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(valueFactory, new LoadingInvocationUnit(valueFactory), false),
-                                       pushCounter, branchCounter, deletedCounter, addedCounter))));
-
-        // Shrink the parameters in the method descriptors.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new OptimizationInfoMemberFilter(
-                                       new MethodDescriptorShrinker(descriptorShrinkCounter))));
-
-        // Make all non-static methods that don't require the 'this' parameter static.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new OptimizationInfoMemberFilter(
-                                       new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC,
-                                       new MethodStaticizer(staticMethodCounter)))));
-
-        // Fix all references to class members, for MethodDescriptorShrinker.
-        // This operation also updates the stack sizes.
-        programClassPool.classesAccept(new MemberReferenceFixer());
-
-        // 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 ParameterShrinker(parameterShrinkCounter))));
-
-        // Count the write-only fields, and the constant fields and methods.
-        programClassPool.classesAccept(new MultiClassVisitor(
-                                       new ClassVisitor[]
-                                       {
-                                           new AllFieldVisitor(
-                                           new WriteOnlyFieldFilter(writeOnlyFieldCounter)),
-                                           new AllFieldVisitor(
-                                           new ConstantMemberFilter(constantFieldCounter)),
-                                           new AllMethodVisitor(
-                                           new ConstantMemberFilter(constantMethodCounter)),
-                                       }));
+        if (fieldPropagationValue      ||
+            methodPropagationParameter ||
+            methodPropagationReturnvalue)
+        {
+            // Fill out fields, method parameters, and return values, so they
+            // can be propagated.
+            InvocationUnit storingInvocationUnit =
+                new StoringInvocationUnit(valueFactory,
+                                          fieldPropagationValue,
+                                          methodPropagationParameter,
+                                          methodPropagationReturnvalue);
+
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new PartialEvaluator(valueFactory, storingInvocationUnit, false))));
+
+            // Count the constant fields and methods.
+            programClassPool.classesAccept(
+                new MultiClassVisitor(
+                new ClassVisitor[]
+                {
+                    new AllFieldVisitor(
+                    new ConstantMemberFilter(fieldPropagationValueCounter)),
+                    new AllMethodVisitor(
+                    new ConstantParameterFilter(methodPropagationParameterCounter)),
+                    new AllMethodVisitor(
+                    new ConstantMemberFilter(methodPropagationReturnvalueCounter)),
+                }));
+        }
+
+        InvocationUnit loadingInvocationUnit =
+            new LoadingInvocationUnit(valueFactory,
+                                      fieldPropagationValue,
+                                      methodPropagationParameter,
+                                      methodPropagationReturnvalue);
+
+        if (codeSimplificationAdvanced)
+        {
+            // Simplify based on partial evaluation, propagating constant
+            // field values, method parameter values, and return values.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new EvaluationSimplifier(
+                new PartialEvaluator(valueFactory, loadingInvocationUnit, false),
+                codeSimplificationAdvancedCounter))));
+        }
+
+        if (codeRemovalAdvanced)
+        {
+            // Remove code based on partial evaluation, also removing unused
+            // parameters from method invocations, and making methods static
+            // if possible.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new EvaluationShrinker(
+                new PartialEvaluator(valueFactory, loadingInvocationUnit, !codeSimplificationAdvanced),
+                deletedCounter, addedCounter))));
+        }
+
+        if (methodRemovalParameter)
+        {
+            // Shrink the parameters in the method descriptors.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new OptimizationInfoMemberFilter(
+                new MethodDescriptorShrinker())));
+        }
+
+        if (methodMarkingStatic)
+        {
+            // Make all non-static methods that don't require the 'this'
+            // parameter static.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new OptimizationInfoMemberFilter(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC,
+                new MethodStaticizer(methodMarkingStaticCounter)))));
+        }
+
+        if (methodRemovalParameter)
+        {
+            // Fix all references to class members.
+            // This operation also updates the stack sizes.
+            programClassPool.classesAccept(
+                new MemberReferenceFixer());
+        }
+
+        if (methodRemovalParameter ||
+            methodMarkingPrivate   ||
+            methodMarkingStatic)
+        {
+            // Remove all unused parameters from the byte code, shifting all
+            // remaining variables.
+            // This operation also updates the local variable frame sizes.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new ParameterShrinker(methodRemovalParameterCounter))));
+        }
+        else if (codeRemovalAdvanced)
+        {
+            // Just update the local variable frame sizes.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new StackSizeUpdater())));
+        }
 
 //        // Specializing the class member descriptors seems to increase the
 //        // class file size, on average.
@@ -234,237 +453,424 @@ public class Optimizer
 //                                       new OptimizationInfoMemberFilter(
 //                                       new ClassReferenceFixer(true))));
 
-        if (configuration.allowAccessModification)
-        {
-            // Fix the access flags of referenced classes and class members,
-            // for SingleImplementationInliner.
-            programClassPool.classesAccept(new AllConstantVisitor(
-                                           new AccessFixer()));
-        }
-
         // Mark all classes with package visible members.
         // Mark all exception catches of methods.
         // Count all method invocations.
         // Mark super invocations and other access of methods.
-        programClassPool.classesAccept(new MultiClassVisitor(
-                                       new ClassVisitor[]
-                                       {
-                                           new AllConstantVisitor(
-                                           new PackageVisibleMemberInvokingClassMarker()),
-                                           new AllMethodVisitor(
-                                           new MultiMemberVisitor(
-                                           new MemberVisitor[]
-                                           {
-                                               new PackageVisibleMemberContainingClassMarker(),
-                                               new AllAttributeVisitor(
-                                               new MultiAttributeVisitor(
-                                               new AttributeVisitor[]
-                                               {
-                                                   new CatchExceptionMarker(),
-                                                   new AllInstructionVisitor(
-                                                   new MultiInstructionVisitor(
-                                                   new InstructionVisitor[]
-                                                   {
-                                                       new InstantiationClassMarker(),
-                                                       new InstanceofClassMarker(),
-                                                       new DotClassMarker(),
-                                                       new MethodInvocationMarker(),
-                                                       new SuperInvocationMarker(),
-                                                       new BackwardBranchMarker(),
-                                                       new AccessMethodMarker(),
-                                                   })),
-                                               })),
-                                           })),
-                                       }));
-
-        // Tweak the descriptors of duplicate initializers.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new DuplicateInitializerFixer(initializerFixCounter)));
-
-        if (initializerFixCounter.getCount() > 0)
-        {
-            // Fix all invocations of tweaked initializers.
-            programClassPool.classesAccept(new AllMethodVisitor(
-                                           new AllAttributeVisitor(
-                                           new DuplicateInitializerInvocationFixer(addedCounter))));
-
-            // Fix all references to tweaked initializers.
-            programClassPool.classesAccept(new MemberReferenceFixer());
+        programClassPool.classesAccept(
+            new MultiClassVisitor(
+            new ClassVisitor[]
+            {
+                new AllConstantVisitor(
+                new PackageVisibleMemberInvokingClassMarker()),
+                new AllMethodVisitor(
+                new MultiMemberVisitor(
+                new MemberVisitor[]
+                {
+                    new PackageVisibleMemberContainingClassMarker(),
+                    new AllAttributeVisitor(
+                    new MultiAttributeVisitor(
+                    new AttributeVisitor[]
+                    {
+                        new CatchExceptionMarker(),
+                        new AllInstructionVisitor(
+                        new MultiInstructionVisitor(
+                        new InstructionVisitor[]
+                        {
+                            new InstantiationClassMarker(),
+                            new InstanceofClassMarker(),
+                            new DotClassMarker(),
+                            new MethodInvocationMarker(),
+                            new SuperInvocationMarker(),
+                            new BackwardBranchMarker(),
+                            new AccessMethodMarker(),
+                        })),
+                        new AllExceptionInfoVisitor(
+                        new ExceptionHandlerConstantVisitor(
+                        new ReferencedClassVisitor(
+                        new CaughtClassMarker()))),
+                    })),
+                })),
+            }));
+
+        if (classMergingVertical)
+        {
+            // Merge classes into their superclasses or interfaces.
+            programClassPool.classesAccept(
+                new VerticalClassMerger(configuration.allowAccessModification,
+                                        configuration.mergeInterfacesAggressively,
+                                        classMergingVerticalCounter));
         }
 
-        // Merge classes into their superclasses or interfaces.
-        programClassPool.classesAccept(new VerticalClassMerger(configuration.allowAccessModification,
-                                                               configuration.mergeInterfacesAggressively,
-                                                               classMergeCounter));
+        if (classMergingHorizontal)
+        {
+            // Merge classes into their sibling classes.
+            programClassPool.classesAccept(
+                new HorizontalClassMerger(configuration.allowAccessModification,
+                                          configuration.mergeInterfacesAggressively,
+                                          classMergingHorizontalCounter));
+        }
 
-        // Merge classes into their sibling classes.
-        programClassPool.classesAccept(new HorizontalClassMerger(configuration.allowAccessModification,
-                                                                 configuration.mergeInterfacesAggressively,
-                                                                 classMergeCounter));
+        if (classMergingVertical ||
+            classMergingHorizontal)
+        {
+            // Clean up inner class attributes to avoid loops.
+            programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover());
 
-        // Clean up inner class attributes to avoid loops.
-        programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover());
+            // Update references to merged classes.
+            programClassPool.classesAccept(new TargetClassChanger());
+            programClassPool.classesAccept(new ClassReferenceFixer(true));
+            programClassPool.classesAccept(new MemberReferenceFixer());
 
-        // Update references to merged classes.
-        programClassPool.classesAccept(new TargetClassChanger());
-        programClassPool.classesAccept(new ClassReferenceFixer(true));
-        programClassPool.classesAccept(new MemberReferenceFixer());
+            if (configuration.allowAccessModification)
+            {
+                // Fix the access flags of referenced merged classes and their
+                // class members.
+                programClassPool.classesAccept(
+                    new AllConstantVisitor(
+                    new AccessFixer()));
+            }
+        }
+
+        if (methodRemovalParameter ||
+            classMergingVertical   ||
+            classMergingHorizontal)
+        {
+            // Tweak the descriptors of duplicate initializers.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new DuplicateInitializerFixer(initializerFixCounter)));
+
+            if (initializerFixCounter.getCount() > 0)
+            {
+                // Fix all invocations of tweaked initializers.
+                programClassPool.classesAccept(
+                    new AllMethodVisitor(
+                    new AllAttributeVisitor(
+                    new DuplicateInitializerInvocationFixer(addedCounter))));
+
+                // Fix all references to tweaked initializers.
+                programClassPool.classesAccept(new MemberReferenceFixer());
+            }
+        }
 
-        if (configuration.allowAccessModification)
+        if (methodInliningUnique)
         {
-            // Fix the access flags of referenced merged classes and their class
-            // members.
-            programClassPool.classesAccept(new AllConstantVisitor(
-                                           new AccessFixer()));
+            // Inline methods that are only invoked once.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new MethodInliner(configuration.microEdition,
+                                  configuration.allowAccessModification,
+                                  true,
+                                  methodInliningUniqueCounter))));
         }
 
-        // Inline methods that are only invoked once.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new MethodInliner(configuration.microEdition, configuration.allowAccessModification, true, inliningCounter))));
+        if (methodInliningShort)
+        {
+            // Inline short methods.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new MethodInliner(configuration.microEdition,
+                                  configuration.allowAccessModification,
+                                  false,
+                                  methodInliningShortCounter))));
+        }
 
-        // Inline short methods.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new MethodInliner(configuration.microEdition, configuration.allowAccessModification, false, inliningCounter))));
+        if (methodInliningTailrecursion)
+        {
+            // Simplify tail recursion calls.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new TailRecursionSimplifier(methodInliningTailrecursionCounter))));
+        }
 
-        // Simplify tail recursion calls.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new TailRecursionSimplifier(tailRecursionCounter))));
+        if (fieldMarkingPrivate ||
+            methodMarkingPrivate)
+        {
+            // Mark all class members that can not be made private.
+            programClassPool.classesAccept(
+                new NonPrivateMemberMarker());
+        }
 
-        // Mark all class members that can not be made private.
-        programClassPool.classesAccept(new NonPrivateMemberMarker());
+        if (fieldMarkingPrivate ||
+            methodMarkingPrivate)
+        {
+            // Make all non-private fields private, whereever possible.
+            programClassPool.classesAccept(
+                new AllFieldVisitor(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MemberPrivatizer(fieldMarkingPrivateCounter))));
+        }
 
-        // Make all non-private and unmarked class members in non-interface
-        // classes private.
-        programClassPool.classesAccept(new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE,
-                                       new AllMemberVisitor(
-                                       new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                                       new MemberPrivatizer(privateFieldCounter, privateMethodCounter)))));
+        if (methodMarkingPrivate)
+        {
+            // Make all non-private methods private, whereever possible.
+            programClassPool.classesAccept(
+                new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE,
+                new AllMethodVisitor(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MemberPrivatizer(methodMarkingPrivateCounter)))));
+        }
 
-        if (configuration.allowAccessModification)
+        if ((methodInliningUnique ||
+             methodInliningShort  ||
+             methodInliningTailrecursion) &&
+            configuration.allowAccessModification)
         {
             // Fix the access flags of referenced classes and class members,
             // for MethodInliner.
-            programClassPool.classesAccept(new AllConstantVisitor(
-                                           new AccessFixer()));
+            programClassPool.classesAccept(
+                new AllConstantVisitor(
+                new AccessFixer()));
         }
 
-        // Fix invocations of methods that have become non-abstract or private,
-        // for ClassMerger, MemberPrivatizer, AccessFixer.
-        programClassPool.classesAccept(new AllMemberVisitor(
-                                       new AllAttributeVisitor(
-                                       new MethodInvocationFixer())));
+        if (methodRemovalParameter ||
+            classMergingVertical   ||
+            classMergingHorizontal ||
+            methodMarkingPrivate)
+        {
+            // Fix invocations of interface methods, of methods that have become
+            // non-abstract or private, and of methods that have moved to a
+            // different package.
+            programClassPool.classesAccept(
+                new AllMemberVisitor(
+                new AllAttributeVisitor(
+                new MethodInvocationFixer())));
+        }
+
+        if (codeMerging)
+        {
+            // Share common blocks of code at branches.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new GotoCommonCodeReplacer(codeMergingCounter))));
+        }
 
         // Create a branch target marker and a code attribute editor that can
         // be reused for all code attributes.
         BranchTargetFinder  branchTargetFinder  = new BranchTargetFinder();
         CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
 
-        // Share common blocks of code at branches.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new GotoCommonCodeReplacer(commonCodeCounter))));
-
-        // Perform various peephole optimisations.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor,
-                                       new MultiInstructionVisitor(
-                                       new InstructionVisitor[]
-                                       {
-                                           new InstructionSequencesReplacer(InstructionSequenceConstants.PATTERN_CONSTANTS,
-                                                                            InstructionSequenceConstants.INSTRUCTION_SEQUENCES,
-                                                                            branchTargetFinder, codeAttributeEditor, peepholeCounter),
-                                           new GotoGotoReplacer(                                codeAttributeEditor, peepholeCounter),
-                                           new GotoReturnReplacer(                              codeAttributeEditor, peepholeCounter),
-                                       })))));
-
-        // Remove unnecessary exception handlers.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new UnreachableExceptionRemover(exceptionCounter))));
-
-        // Remove unreachable code.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new UnreachableCodeRemover(deletedCounter))));
-
-        // Remove all unused local variables.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new VariableShrinker(variableShrinkCounter))));
-
-        // Optimize the variables.
-        programClassPool.classesAccept(new AllMethodVisitor(
-                                       new AllAttributeVisitor(
-                                       new VariableOptimizer(!configuration.microEdition))));
-
-        int classMergeCound       = classMergeCounter      .getCount();
-        int finalClassCount       = finalClassCounter      .getCount();
-        int privateFieldCount     = privateFieldCounter    .getCount();
-        int privateMethodCount    = privateMethodCounter   .getCount();
-        int staticMethodCount     = staticMethodCounter    .getCount();
-        int finalMethodCount      = finalMethodCounter     .getCount();
-        int writeOnlyFieldCount   = writeOnlyFieldCounter  .getCount();
-        int constantFieldCount    = constantFieldCounter   .getCount();
-        int constantMethodCount   = constantMethodCounter  .getCount();
-        int descriptorShrinkCount = descriptorShrinkCounter.getCount() - initializerFixCounter.getCount();
-        int parameterShrinkCount  = parameterShrinkCounter .getCount() - initializerFixCounter.getCount();
-        int variableShrinkCount   = variableShrinkCounter  .getCount();
-        int exceptionCount        = exceptionCounter       .getCount();
-        int tailRecursionCount    = tailRecursionCounter   .getCount();
-        int inliningCount         = inliningCounter        .getCount();
-        int commonCodeCount       = commonCodeCounter      .getCount();
-        int pushCount             = pushCounter            .getCount();
-        int branchCount           = branchCounter          .getCount();
-        int removedCount          = deletedCounter         .getCount() - addedCounter.getCount();
-        int peepholeCount         = peepholeCounter        .getCount();
+        List peepholeOptimizations = new ArrayList();
+        if (codeSimplificationVariable)
+        {
+            // Peephole optimizations involving local variables.
+            peepholeOptimizations.add(
+                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
+                                                 InstructionSequenceConstants.VARIABLE,
+                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationVariableCounter));
+        }
+
+        if (codeSimplificationArithmetic)
+        {
+            // Peephole optimizations involving arithmetic operations.
+            peepholeOptimizations.add(
+                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
+                                                 InstructionSequenceConstants.ARITHMETIC,
+                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationArithmeticCounter));
+        }
+
+        if (codeSimplificationCast)
+        {
+            // Peephole optimizations involving cast operations.
+            peepholeOptimizations.add(
+                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
+                                                 InstructionSequenceConstants.CAST,
+                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationCastCounter));
+        }
+
+        if (codeSimplificationField)
+        {
+            // Peephole optimizations involving fields.
+            peepholeOptimizations.add(
+                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
+                                                 InstructionSequenceConstants.FIELD,
+                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationFieldCounter));
+        }
+
+        if (codeSimplificationBranch)
+        {
+            // Peephole optimizations involving branches.
+            peepholeOptimizations.add(
+                new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS,
+                                                 InstructionSequenceConstants.BRANCH,
+                                                 branchTargetFinder, codeAttributeEditor, codeSimplificationBranchCounter));
+
+            // Include optimization of branches to branches and returns.
+            peepholeOptimizations.add(
+                new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
+            peepholeOptimizations.add(
+                new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
+        }
+
+        if (!peepholeOptimizations.isEmpty())
+        {
+            // Convert the list into an array.
+            InstructionVisitor[] peepholeOptimizationsArray =
+                new InstructionVisitor[peepholeOptimizations.size()];
+            peepholeOptimizations.toArray(peepholeOptimizationsArray);
+
+            // Perform the peephole optimisations.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new PeepholeOptimizer(branchTargetFinder, codeAttributeEditor,
+                new MultiInstructionVisitor(
+                peepholeOptimizationsArray)))));
+        }
+
+        if (codeRemovalException)
+        {
+            // Remove unnecessary exception handlers.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new UnreachableExceptionRemover(codeRemovalExceptionCounter))));
+        }
+
+        if (codeRemovalSimple)
+        {
+            // Remove unreachable code.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new UnreachableCodeRemover(deletedCounter))));
+        }
+
+        if (codeRemovalVariable)
+        {
+            // Remove all unused local variables.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new VariableShrinker(codeRemovalVariableCounter))));
+        }
+        else
+        {
+            // Clean up all unused local variables.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new VariableCleaner())));
+        }
+
+        if (codeAllocationVariable)
+        {
+            // Optimize the variables.
+            programClassPool.classesAccept(
+                new AllMethodVisitor(
+                new AllAttributeVisitor(
+                new VariableOptimizer(false, codeAllocationVariableCounter))));
+        }
+
+        int classMarkingFinalCount            = classMarkingFinalCounter           .getCount();
+        int classMergingVerticalCount         = classMergingVerticalCounter        .getCount();
+        int classMergingHorizontalCount       = classMergingHorizontalCounter      .getCount();
+        int fieldRemovalWriteonlyCount        = fieldRemovalWriteonlyCounter       .getCount();
+        int fieldMarkingPrivateCount          = fieldMarkingPrivateCounter         .getCount();
+        int fieldPropagationValueCount        = fieldPropagationValueCounter       .getCount();
+        int methodMarkingPrivateCount         = methodMarkingPrivateCounter        .getCount();
+        int methodMarkingStaticCount          = methodMarkingStaticCounter         .getCount();
+        int methodMarkingFinalCount           = methodMarkingFinalCounter          .getCount();
+        int methodRemovalParameterCount       = methodRemovalParameterCounter      .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter.getCount();
+        int methodPropagationParameterCount   = methodPropagationParameterCounter  .getCount();
+        int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount();
+        int methodInliningShortCount          = methodInliningShortCounter         .getCount();
+        int methodInliningUniqueCount         = methodInliningUniqueCounter        .getCount();
+        int methodInliningTailrecursionCount  = methodInliningTailrecursionCounter .getCount();
+        int codeMergingCount                  = codeMergingCounter                 .getCount();
+        int codeSimplificationVariableCount   = codeSimplificationVariableCounter  .getCount();
+        int codeSimplificationArithmeticCount = codeSimplificationArithmeticCounter.getCount();
+        int codeSimplificationCastCount       = codeSimplificationCastCounter      .getCount();
+        int codeSimplificationFieldCount      = codeSimplificationFieldCounter     .getCount();
+        int codeSimplificationBranchCount     = codeSimplificationBranchCounter    .getCount();
+        int codeSimplificationAdvancedCount   = codeSimplificationAdvancedCounter  .getCount();
+        int codeRemovalCount                  = deletedCounter                     .getCount() - addedCounter.getCount();
+        int codeRemovalVariableCount          = codeRemovalVariableCounter         .getCount();
+        int codeRemovalExceptionCount         = codeRemovalExceptionCounter        .getCount();
+        int codeAllocationVariableCount       = codeAllocationVariableCounter      .getCount();
 
         if (configuration.verbose)
         {
-            System.out.println("  Number of merged classes:                  "+classMergeCound);
-            System.out.println("  Number of finalized classes:               "+finalClassCount);
-            System.out.println("  Number of privatized fields:               "+privateFieldCount);
-            System.out.println("  Number of privatized methods:              "+privateMethodCount);
-            System.out.println("  Number of staticized methods:              "+staticMethodCount);
-            System.out.println("  Number of finalized methods:               "+finalMethodCount);
-            System.out.println("  Number of removed write-only fields:       "+writeOnlyFieldCount);
-            System.out.println("  Number of inlined constant fields:         "+constantFieldCount);
-            System.out.println("  Number of inlined constant methods:        "+constantMethodCount);
-            System.out.println("  Number of simplified method declarations:  "+descriptorShrinkCount);
-            System.out.println("  Number of removed parameters:              "+parameterShrinkCount);
-            System.out.println("  Number of removed local variables:         "+variableShrinkCount);
-            System.out.println("  Number of inlined method calls:            "+inliningCount);
-            System.out.println("  Number of simplified tail recursion calls: "+tailRecursionCount);
-            System.out.println("  Number of removed exception blocks:        "+exceptionCount);
-            System.out.println("  Number of merged code blocks:              "+commonCodeCount);
-            System.out.println("  Number of simplified push instructions:    "+pushCount);
-            System.out.println("  Number of simplified branches:             "+branchCount);
-            System.out.println("  Number of removed instructions:            "+removedCount);
-            System.out.println("  Number of peephole optimizations:          "+peepholeCount);
-        }
-
-        return classMergeCound       > 0 ||
-               finalClassCount       > 0 ||
-               privateFieldCount     > 0 ||
-               privateMethodCount    > 0 ||
-               staticMethodCount     > 0 ||
-               finalMethodCount      > 0 ||
-               writeOnlyFieldCount   > 0 ||
-               constantFieldCount    > 0 ||
-               constantMethodCount   > 0 ||
-               descriptorShrinkCount > 0 ||
-               parameterShrinkCount  > 0 ||
-               variableShrinkCount   > 0 ||
-               inliningCount         > 0 ||
-               tailRecursionCount    > 0 ||
-               exceptionCount        > 0 ||
-               commonCodeCount       > 0 ||
-               pushCount             > 0 ||
-               branchCount           > 0 ||
-               removedCount          > 0 ||
-               peepholeCount         > 0;
+            System.out.println("  Number of finalized classes:                 " + classMarkingFinalCount            + disabled(classMarkingFinal));
+            System.out.println("  Number of vertically merged classes:         " + classMergingVerticalCount         + disabled(classMergingVertical));
+            System.out.println("  Number of horizontally merged classes:       " + classMergingHorizontalCount       + disabled(classMergingHorizontal));
+            System.out.println("  Number of removed write-only fields:         " + fieldRemovalWriteonlyCount        + disabled(fieldRemovalWriteonly));
+            System.out.println("  Number of privatized fields:                 " + fieldMarkingPrivateCount          + disabled(fieldMarkingPrivate));
+            System.out.println("  Number of inlined constant fields:           " + fieldPropagationValueCount        + disabled(fieldPropagationValue));
+            System.out.println("  Number of privatized methods:                " + methodMarkingPrivateCount         + disabled(methodMarkingPrivate));
+            System.out.println("  Number of staticized methods:                " + methodMarkingStaticCount          + disabled(methodMarkingStatic));
+            System.out.println("  Number of finalized methods:                 " + methodMarkingFinalCount           + disabled(methodMarkingFinal));
+            System.out.println("  Number of removed method parameters:         " + methodRemovalParameterCount       + disabled(methodRemovalParameter));
+            System.out.println("  Number of inlined constant parameters:       " + methodPropagationParameterCount   + disabled(methodPropagationParameter));
+            System.out.println("  Number of inlined constant return values:    " + methodPropagationReturnvalueCount + disabled(methodPropagationReturnvalue));
+            System.out.println("  Number of inlined short method calls:        " + methodInliningShortCount          + disabled(methodInliningShort));
+            System.out.println("  Number of inlined unique method calls:       " + methodInliningUniqueCount         + disabled(methodInliningUnique));
+            System.out.println("  Number of inlined tail recursion calls:      " + methodInliningTailrecursionCount  + disabled(methodInliningTailrecursion));
+            System.out.println("  Number of merged code blocks:                " + codeMergingCount                  + disabled(codeMerging));
+            System.out.println("  Number of variable peephole optimizations:   " + codeSimplificationVariableCount   + disabled(codeSimplificationVariable));
+            System.out.println("  Number of arithmetic peephole optimizations: " + codeSimplificationArithmeticCount + disabled(codeSimplificationArithmetic));
+            System.out.println("  Number of cast peephole optimizations:       " + codeSimplificationCastCount       + disabled(codeSimplificationCast));
+            System.out.println("  Number of field peephole optimizations:      " + codeSimplificationFieldCount      + disabled(codeSimplificationField));
+            System.out.println("  Number of branch peephole optimizations:     " + codeSimplificationBranchCount     + disabled(codeSimplificationBranch));
+            System.out.println("  Number of simplified instructions:           " + codeSimplificationAdvancedCount   + disabled(codeSimplificationAdvanced));
+            System.out.println("  Number of removed instructions:              " + codeRemovalCount                  + disabled(codeRemovalAdvanced));
+            System.out.println("  Number of removed local variables:           " + codeRemovalVariableCount          + disabled(codeRemovalVariable));
+            System.out.println("  Number of removed exception blocks:          " + codeRemovalExceptionCount         + disabled(codeRemovalException));
+            System.out.println("  Number of optimized local variable frames:   " + codeAllocationVariableCount       + disabled(codeAllocationVariable));
+        }
+
+        return classMarkingFinalCount            > 0 ||
+               classMergingVerticalCount         > 0 ||
+               classMergingHorizontalCount       > 0 ||
+               fieldRemovalWriteonlyCount        > 0 ||
+               fieldMarkingPrivateCount          > 0 ||
+               methodMarkingPrivateCount         > 0 ||
+               methodMarkingStaticCount          > 0 ||
+               methodMarkingFinalCount           > 0 ||
+               fieldPropagationValueCount        > 0 ||
+               methodRemovalParameterCount       > 0 ||
+               methodPropagationParameterCount   > 0 ||
+               methodPropagationReturnvalueCount > 0 ||
+               methodInliningShortCount          > 0 ||
+               methodInliningUniqueCount         > 0 ||
+               methodInliningTailrecursionCount  > 0 ||
+               codeMergingCount                  > 0 ||
+               codeSimplificationVariableCount   > 0 ||
+               codeSimplificationArithmeticCount > 0 ||
+               codeSimplificationCastCount       > 0 ||
+               codeSimplificationFieldCount      > 0 ||
+               codeSimplificationBranchCount     > 0 ||
+               codeSimplificationAdvancedCount   > 0 ||
+               codeRemovalCount                  > 0 ||
+               codeRemovalVariableCount          > 0 ||
+               codeRemovalExceptionCount         > 0 ||
+               codeAllocationVariableCount       > 0;
+    }
+
+
+    /**
+     * Returns a String indicating whether the given flag is enabled or
+     * disabled.
+     */
+    private String disabled(boolean flag)
+    {
+        return flag ? "" : "   (disabled)";
+    }
+
+
+    /**
+     * Returns a String indicating whether the given flags are enabled or
+     * disabled.
+     */
+    private String disabled(boolean flag1, boolean flag2)
+    {
+        return flag1 && flag2 ? "" :
+               flag1 || flag2 ? "   (partially disabled)" :
+                                "   (disabled)";
     }
 }
diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java
index 9e94985..a2bc6d3 100644
--- a/src/proguard/optimize/ParameterShrinker.java
+++ b/src/proguard/optimize/ParameterShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,7 +23,7 @@ package proguard.optimize;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.visitor.AttributeVisitor;
-import proguard.classfile.editor.VariableEditor;
+import proguard.classfile.editor.*;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.MemberVisitor;
 import proguard.optimize.info.ParameterUsageMarker;
@@ -46,7 +46,7 @@ implements   AttributeVisitor
 
     private final MemberVisitor extraVariableMemberVisitor;
 
-    private final VariableEditor variableEditor = new VariableEditor();
+    private final VariableRemapper variableRemapper = new VariableRemapper();
 
 
     /**
@@ -97,21 +97,30 @@ implements   AttributeVisitor
                 System.out.println("  Max locals         = " + maxLocals);
             }
 
-            // Delete unused variables from the local variable frame.
-            variableEditor.reset(maxLocals);
+            // Create a variable map.
+            int[] variableMap = new int[maxLocals];
 
+            // Move unused parameters right after the parameter block.
+            int usedParameterIndex   = 0;
+            int unusedParameterIndex = newParameterSize;
             for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++)
             {
-                // Is the variable not required as a parameter?
-                if (!ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+                // Is the variable required as a parameter?
+                if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+                {
+                    // Keep the variable as a parameter.
+                    variableMap[parameterIndex] = usedParameterIndex++;
+                }
+                else
                 {
                     if (DEBUG)
                     {
                         System.out.println("  Deleting parameter #"+parameterIndex);
                     }
 
-                    // Delete the unused variable.
-                    variableEditor.deleteVariable(parameterIndex);
+                    // Shift the variable to the unused parameter block,
+                    // in case it is still used as a variable.
+                    variableMap[parameterIndex] = unusedParameterIndex++;
 
                     // Visit the method, if required.
                     if (extraVariableMemberVisitor != null)
@@ -121,8 +130,17 @@ implements   AttributeVisitor
                 }
             }
 
-            // Shift all remaining parameters and variables in the byte code.
-            variableEditor.visitCodeAttribute(clazz, method, codeAttribute);
+            // Fill out the remainder of the map.
+            for (int variableIndex = oldParameterSize; variableIndex < maxLocals; variableIndex++)
+            {
+                variableMap[variableIndex] = variableIndex;
+            }
+
+            // Set the map.
+            variableRemapper.setVariableMap(variableMap);
+
+            // Remap the variables.
+            variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
         }
     }
 }
diff --git a/src/proguard/optimize/TailRecursionSimplifier.java b/src/proguard/optimize/TailRecursionSimplifier.java
index c571cf7..0946b6a 100644
--- a/src/proguard/optimize/TailRecursionSimplifier.java
+++ b/src/proguard/optimize/TailRecursionSimplifier.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -43,9 +43,6 @@ implements   AttributeVisitor,
              ConstantVisitor,
              ExceptionInfoVisitor
 {
-    private static final int MAXIMUM_CODE_EXPANSION    =   2;
-    private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128;
-
     //*
     private static final boolean DEBUG = false;
     /*/
@@ -131,8 +128,7 @@ implements   AttributeVisitor,
     {
         // The code may expand, due to expanding constant and variable
         // instructions.
-        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength * MAXIMUM_CODE_EXPANSION +
-                                                MAXIMUM_EXTRA_CODE_LENGTH);
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
 
         // Copy the instructions.
         codeAttribute.instructionsAccept(clazz, method, this);
@@ -281,7 +277,7 @@ implements   AttributeVisitor,
             }
         }
 
-        codeAttributeComposer.beginCodeFragment((parameterOffset + parameterCount) * 4);
+        codeAttributeComposer.beginCodeFragment(parameterSize + 1);
 
         // Go over the parameter types backward, storing the stack entries
         // in their corresponding variables.
diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/src/proguard/optimize/WriteOnlyFieldFilter.java
index 8a1985d..578beb2 100644
--- a/src/proguard/optimize/WriteOnlyFieldFilter.java
+++ b/src/proguard/optimize/WriteOnlyFieldFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java
similarity index 61%
copy from src/proguard/optimize/evaluation/EvaluationSimplifier.java
copy to src/proguard/optimize/evaluation/EvaluationShrinker.java
index 95abf42..1463feb 100644
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ b/src/proguard/optimize/evaluation/EvaluationShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,6 +23,8 @@ package proguard.optimize.evaluation;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.RefConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.editor.CodeAttributeEditor;
 import proguard.classfile.instruction.*;
 import proguard.classfile.instruction.visitor.InstructionVisitor;
@@ -38,29 +40,25 @@ import proguard.optimize.info.*;
  *
  * @author Eric Lafortune
  */
-public class EvaluationSimplifier
+public class EvaluationShrinker
 extends      SimplifiedVisitor
 implements   AttributeVisitor
 {
     //*
     private static final boolean DEBUG_RESULTS  = false;
-    private static final boolean DEBUG_ANALYSIS = false;
     private static final boolean DEBUG          = false;
     /*/
     private static boolean DEBUG_RESULTS  = true;
-    private static boolean DEBUG_ANALYSIS = true;
     private static boolean DEBUG          = true;
     //*/
 
-    private final InstructionVisitor extraPushInstructionVisitor;
-    private final InstructionVisitor extraBranchInstructionVisitor;
     private final InstructionVisitor extraDeletedInstructionVisitor;
     private final InstructionVisitor extraAddedInstructionVisitor;
 
     private final PartialEvaluator             partialEvaluator;
     private final PartialEvaluator             simplePartialEvaluator       = new PartialEvaluator();
     private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
-    private final MyInstructionSimplifier      instructionSimplifier        = new MyInstructionSimplifier();
+    private final MyUnusedParameterSimplifier  unusedParameterSimplifier    = new MyUnusedParameterSimplifier();
     private final MyProducerMarker             producerMarker               = new MyProducerMarker();
     private final MyStackConsistencyFixer      stackConsistencyFixer        = new MyStackConsistencyFixer();
     private final CodeAttributeEditor          codeAttributeEditor          = new CodeAttributeEditor(false);
@@ -69,46 +67,36 @@ implements   AttributeVisitor
     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[]       aliasingVariables       = new int[ClassConstants.TYPICAL_CODE_LENGTH];
 
     private int maxMarkedOffset;
 
 
     /**
-     * Creates a new EvaluationSimplifier.
+     * Creates a new EvaluationShrinker.
      */
-    public EvaluationSimplifier()
+    public EvaluationShrinker()
     {
-        this(new PartialEvaluator(), null, null, null, null);
+        this(new PartialEvaluator(), null, null);
     }
 
 
     /**
-     * Creates a new EvaluationSimplifier.
+     * Creates a new EvaluationShrinker.
      * @param partialEvaluator               the partial evaluator that will
      *                                       execute the code and provide
      *                                       information about the results.
-     * @param extraPushInstructionVisitor    an optional extra visitor for all
-     *                                       simplified push instructions.
-     * @param extraBranchInstructionVisitor  an optional extra visitor for all
-     *                                       simplified branch instructions.
      * @param extraDeletedInstructionVisitor an optional extra visitor for all
      *                                       deleted instructions.
      * @param extraAddedInstructionVisitor   an optional extra visitor for all
      *                                       added instructions.
      */
-    public EvaluationSimplifier(PartialEvaluator   partialEvaluator,
-                                InstructionVisitor extraPushInstructionVisitor,
-                                InstructionVisitor extraBranchInstructionVisitor,
-                                InstructionVisitor extraDeletedInstructionVisitor,
-                                InstructionVisitor extraAddedInstructionVisitor)
+    public EvaluationShrinker(PartialEvaluator   partialEvaluator,
+                              InstructionVisitor extraDeletedInstructionVisitor,
+                              InstructionVisitor extraAddedInstructionVisitor)
     {
-        this.partialEvaluator                      = partialEvaluator;
-        this.extraPushInstructionVisitor           = extraPushInstructionVisitor;
-        this.extraBranchInstructionVisitor         = extraBranchInstructionVisitor;
-        this.extraDeletedInstructionVisitor        = extraDeletedInstructionVisitor;
-        this.extraAddedInstructionVisitor          = extraAddedInstructionVisitor;
+        this.partialEvaluator               = partialEvaluator;
+        this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor;
+        this.extraAddedInstructionVisitor   = extraAddedInstructionVisitor;
     }
 
 
@@ -119,11 +107,11 @@ implements   AttributeVisitor
 
     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-//        DEBUG = DEBUG_ANALYSIS = DEBUG_RESULTS =
+//        DEBUG = DEBUG_RESULTS =
 //            clazz.getName().equals("abc/Def") &&
 //            method.getName(clazz).equals("abc");
 
-        // TODO: Remove this when the evaluation simplifier has stabilized.
+        // TODO: Remove this when the evaluation shrinker has stabilized.
         // Catch any unexpected exceptions from the actual visiting method.
         try
         {
@@ -132,7 +120,7 @@ implements   AttributeVisitor
         }
         catch (RuntimeException ex)
         {
-            System.err.println("Unexpected error while optimizing after partial evaluation:");
+            System.err.println("Unexpected error while shrinking instructions after partial evaluation:");
             System.err.println("  Class       = ["+clazz.getName()+"]");
             System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
             System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
@@ -171,8 +159,8 @@ implements   AttributeVisitor
         // Reset the code changes.
         codeAttributeEditor.reset(codeLength);
 
-        // Replace any instructions that can be simplified.
-        if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
+        // Mark any unused method parameters on the stack.
+        if (DEBUG) System.out.println("Invocation simplification:");
 
         for (int offset = 0; offset < codeLength; offset++)
         {
@@ -181,12 +169,12 @@ implements   AttributeVisitor
                 Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
-                instruction.accept(clazz, method, codeAttribute, offset, instructionSimplifier);
+                instruction.accept(clazz, method, codeAttribute, offset, unusedParameterSimplifier);
             }
         }
 
         // Mark all essential instructions that have been encountered as used.
-        if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
+        if (DEBUG) System.out.println("Usage initialization: ");
 
         maxMarkedOffset = -1;
 
@@ -195,7 +183,7 @@ implements   AttributeVisitor
         int superInitializationOffset = partialEvaluator.superInitializationOffset();
         if (superInitializationOffset != PartialEvaluator.NONE)
         {
-            if (DEBUG_ANALYSIS) System.out.print("(super.<init>)");
+            if (DEBUG) System.out.print("(super.<init>)");
 
             markInstruction(superInitializationOffset);
         }
@@ -212,7 +200,7 @@ implements   AttributeVisitor
                 if (instruction.opcode == InstructionConstants.OP_GOTO &&
                     ((BranchInstruction)instruction).branchOffset == 0)
                 {
-                    if (DEBUG_ANALYSIS) System.out.print("(infinite loop)");
+                    if (DEBUG) System.out.print("(infinite loop)");
                     markInstruction(offset);
                 }
 
@@ -227,7 +215,7 @@ implements   AttributeVisitor
                 }
             }
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+        if (DEBUG) System.out.println();
 
 
         // Globally mark instructions and their produced variables and stack
@@ -235,7 +223,7 @@ implements   AttributeVisitor
         // 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:");
+        if (DEBUG) System.out.println("Usage marking:");
 
         while (maxMarkedOffset >= 0)
         {
@@ -247,22 +235,10 @@ implements   AttributeVisitor
             {
                 if (isInstructionNecessary(offset))
                 {
-                    if (isInstructionSimplified(offset))
-                    {
-                        int variableIndex = getAliasingVariable(offset);
-
-                        if (variableIndex >= 0)
-                        {
-                            markVariableProducers(offset, variableIndex);
-                        }
-                    }
-                    else
-                    {
-                        Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                            offset);
+                    Instruction instruction = InstructionFactory.create(codeAttribute.code,
+                                                                        offset);
 
-                        instruction.accept(clazz, method, codeAttribute, offset, producerMarker);
-                    }
+                    instruction.accept(clazz, method, codeAttribute, offset, producerMarker);
                 }
 
                 // Check if this instruction is a branch origin from a branch
@@ -278,7 +254,7 @@ implements   AttributeVisitor
                                        false);
             }
 
-            if (DEBUG_ANALYSIS)
+            if (DEBUG)
             {
                 if (maxMarkedOffset > offset)
                 {
@@ -286,13 +262,13 @@ implements   AttributeVisitor
                 }
             }
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+        if (DEBUG) 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: ");
+        if (DEBUG) System.out.println("Initialization marking: ");
 
         for (int offset = 0; offset < codeLength; offset++)
         {
@@ -314,11 +290,11 @@ implements   AttributeVisitor
                 }
             }
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+        if (DEBUG) System.out.println();
 
 
         // Locally fix instructions, in order to keep the stack consistent.
-        if (DEBUG_ANALYSIS) System.out.println("Stack consistency fixing:");
+        if (DEBUG) System.out.println("Stack consistency fixing:");
 
         maxMarkedOffset = codeLength - 1;
 
@@ -348,18 +324,18 @@ implements   AttributeVisitor
                                        false);
             }
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+        if (DEBUG) System.out.println();
 
 
         // 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:");
+        if (DEBUG) System.out.println("Infinite loop fixing:");
 
         for (int offset = 0; offset < codeLength; offset++)
         {
-            // Is a traced but unmarked backward branch, without an unmarked
+            // Is it 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)   &&
@@ -368,13 +344,35 @@ implements   AttributeVisitor
                 !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset),
                                                           offset))
             {
+                replaceByInfiniteLoop(clazz, offset);
+            }
+        }
+        if (DEBUG) System.out.println();
+
+
+        // Insert infinite loops after jumps to subroutines that don't return.
+        // The virtual machine's verification step is not smart enough to see
+        // the code isn't reachable, and may complain otherwise.
+        if (DEBUG) System.out.println("Non-returning subroutine fixing:");
+
+        for (int offset = 0; offset < codeLength; offset++)
+        {
+            // Is it a traced but unmarked backward branch, without an unmarked
+            // straddling forward branch? Note that this is still a heuristic.
+            if (isInstructionNecessary(offset) &&
+                partialEvaluator.isSubroutineInvocation(offset))
+            {
                 Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
-                replaceByInfiniteLoop(clazz, offset, instruction);
+                int nextOffset = offset + instruction.length(offset);
+                if (!isInstructionNecessary(nextOffset))
+                {
+                    replaceByInfiniteLoop(clazz, nextOffset);
+                }
             }
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+        if (DEBUG) System.out.println();
 
 
         // Delete all instructions that are not used.
@@ -387,9 +385,9 @@ implements   AttributeVisitor
             {
                 codeAttributeEditor.deleteInstruction(offset);
 
-                codeAttributeEditor.insertBeforeInstruction(offset, null);
-                codeAttributeEditor.replaceInstruction(offset, null);
-                codeAttributeEditor.insertAfterInstruction(offset, null);
+                codeAttributeEditor.insertBeforeInstruction(offset, (Instruction)null);
+                codeAttributeEditor.replaceInstruction(offset,      (Instruction)null);
+                codeAttributeEditor.insertAfterInstruction(offset,  (Instruction)null);
 
                 // Visit the instruction, if required.
                 if (extraDeletedInstructionVisitor != null)
@@ -464,239 +462,43 @@ implements   AttributeVisitor
 
 
     /**
-     * This InstructionVisitor simplifies the instructions that it visits,
-     * whenever possible, and marks them as such.
+     * This MemberVisitor marks stack entries that aren't necessary because
+     * parameters aren't used in the methods that are visited.
      */
-    private class MyInstructionSimplifier
+    private class MyUnusedParameterSimplifier
     extends       SimplifiedVisitor
-    implements    InstructionVisitor
+    implements    InstructionVisitor, ConstantVisitor, MemberVisitor
     {
-        // Implementations for InstructionVisitor.
-
-        public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
-        {
-            switch (simpleInstruction.opcode)
-            {
-                case InstructionConstants.OP_IALOAD:
-                case InstructionConstants.OP_BALOAD:
-                case InstructionConstants.OP_CALOAD:
-                case InstructionConstants.OP_SALOAD:
-                case InstructionConstants.OP_IADD:
-                case InstructionConstants.OP_ISUB:
-                case InstructionConstants.OP_IMUL:
-                case InstructionConstants.OP_IDIV:
-                case InstructionConstants.OP_IREM:
-                case InstructionConstants.OP_INEG:
-                case InstructionConstants.OP_ISHL:
-                case InstructionConstants.OP_ISHR:
-                case InstructionConstants.OP_IUSHR:
-                case InstructionConstants.OP_IAND:
-                case InstructionConstants.OP_IOR:
-                case InstructionConstants.OP_IXOR:
-                case InstructionConstants.OP_L2I:
-                case InstructionConstants.OP_F2I:
-                case InstructionConstants.OP_D2I:
-                case InstructionConstants.OP_I2B:
-                case InstructionConstants.OP_I2C:
-                case InstructionConstants.OP_I2S:
-                    replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
-                    break;
-
-                case InstructionConstants.OP_LALOAD:
-                case InstructionConstants.OP_LADD:
-                case InstructionConstants.OP_LSUB:
-                case InstructionConstants.OP_LMUL:
-                case InstructionConstants.OP_LDIV:
-                case InstructionConstants.OP_LREM:
-                case InstructionConstants.OP_LNEG:
-                case InstructionConstants.OP_LSHL:
-                case InstructionConstants.OP_LSHR:
-                case InstructionConstants.OP_LUSHR:
-                case InstructionConstants.OP_LAND:
-                case InstructionConstants.OP_LOR:
-                case InstructionConstants.OP_LXOR:
-                case InstructionConstants.OP_I2L:
-                case InstructionConstants.OP_F2L:
-                case InstructionConstants.OP_D2L:
-                    replaceLongPushInstruction(clazz, offset, simpleInstruction);
-                    break;
-
-                case InstructionConstants.OP_FALOAD:
-                case InstructionConstants.OP_FADD:
-                case InstructionConstants.OP_FSUB:
-                case InstructionConstants.OP_FMUL:
-                case InstructionConstants.OP_FDIV:
-                case InstructionConstants.OP_FREM:
-                case InstructionConstants.OP_FNEG:
-                case InstructionConstants.OP_I2F:
-                case InstructionConstants.OP_L2F:
-                case InstructionConstants.OP_D2F:
-                    replaceFloatPushInstruction(clazz, offset, simpleInstruction);
-                    break;
-
-                case InstructionConstants.OP_DALOAD:
-                case InstructionConstants.OP_DADD:
-                case InstructionConstants.OP_DSUB:
-                case InstructionConstants.OP_DMUL:
-                case InstructionConstants.OP_DDIV:
-                case InstructionConstants.OP_DREM:
-                case InstructionConstants.OP_DNEG:
-                case InstructionConstants.OP_I2D:
-                case InstructionConstants.OP_L2D:
-                case InstructionConstants.OP_F2D:
-                    replaceDoublePushInstruction(clazz, offset, simpleInstruction);
-                    break;
-
-                case InstructionConstants.OP_AALOAD:
-                    replaceReferencePushInstruction(clazz, offset, simpleInstruction);
-                    break;
-            }
-        }
-
-
-        public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
-        {
-            int variableIndex = variableInstruction.variableIndex;
-
-            switch (variableInstruction.opcode)
-            {
-                case InstructionConstants.OP_ILOAD:
-                case InstructionConstants.OP_ILOAD_0:
-                case InstructionConstants.OP_ILOAD_1:
-                case InstructionConstants.OP_ILOAD_2:
-                case InstructionConstants.OP_ILOAD_3:
-                    replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_LLOAD:
-                case InstructionConstants.OP_LLOAD_0:
-                case InstructionConstants.OP_LLOAD_1:
-                case InstructionConstants.OP_LLOAD_2:
-                case InstructionConstants.OP_LLOAD_3:
-                    replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_FLOAD:
-                case InstructionConstants.OP_FLOAD_0:
-                case InstructionConstants.OP_FLOAD_1:
-                case InstructionConstants.OP_FLOAD_2:
-                case InstructionConstants.OP_FLOAD_3:
-                    replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
+        private int                 invocationOffset;
+        private ConstantInstruction invocationInstruction;
 
-                case InstructionConstants.OP_DLOAD:
-                case InstructionConstants.OP_DLOAD_0:
-                case InstructionConstants.OP_DLOAD_1:
-                case InstructionConstants.OP_DLOAD_2:
-                case InstructionConstants.OP_DLOAD_3:
-                    replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
 
-                case InstructionConstants.OP_ALOAD:
-                case InstructionConstants.OP_ALOAD_0:
-                case InstructionConstants.OP_ALOAD_1:
-                case InstructionConstants.OP_ALOAD_2:
-                case InstructionConstants.OP_ALOAD_3:
-                    replaceReferencePushInstruction(clazz, offset, variableInstruction);
-                    break;
+        // 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)
         {
             switch (constantInstruction.opcode)
             {
-                case InstructionConstants.OP_GETSTATIC:
-                case InstructionConstants.OP_GETFIELD:
-                    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,
-                                                                     codeAttribute,
-                                                                     offset,
-                                                                     constantInstruction))
-                    {
-                        replaceAnyPushInstruction(clazz, offset, constantInstruction);
-                    }
-
-                    break;
-
-                case InstructionConstants.OP_CHECKCAST:
-                    replaceReferencePushInstruction(clazz, offset, constantInstruction);
+                    this.invocationOffset      = offset;
+                    this.invocationInstruction = constantInstruction;
+                    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
                     break;
             }
         }
 
 
-        public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
-        {
-            switch (branchInstruction.opcode)
-            {
-                case InstructionConstants.OP_GOTO:
-                case InstructionConstants.OP_GOTO_W:
-                    // Don't replace unconditional branches.
-                    break;
-
-                case InstructionConstants.OP_JSR:
-                case InstructionConstants.OP_JSR_W:
-                    replaceJsrInstruction(clazz, offset, branchInstruction);
-                    break;
-
-                default:
-                    replaceBranchInstruction(clazz, offset, branchInstruction);
-                    break;
-            }
-        }
-
-
-        public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
-        {
-            // First try to simplify it to a simple branch.
-            replaceBranchInstruction(clazz, offset, switchInstruction);
-
-            // Otherwise make sure all branch targets are valid.
-            if (!isInstructionSimplified(offset))
-            {
-                replaceSwitchInstruction(clazz, offset, switchInstruction);
-            }
-        }
-    }
-
-
-    /**
-     * This MemberVisitor marks stack entries that aren't necessary because
-     * parameters aren't used in the methods that are visited.
-     */
-    private class MyUnusedParameterSimplifier
-    extends       SimplifiedVisitor
-    implements    MemberVisitor
-    {
-        private int                 invocationOffset;
-        private ConstantInstruction invocationInstruction;
-
+        // Implementations for ConstantVisitor.
 
-        /**
-         * Creates a new MyUnusedParameterSimplifier for the given invocation
-         * at the given offset.
-         */
-        private MyUnusedParameterSimplifier(int                 invocationOffset,
-                                            ConstantInstruction invocationInstruction)
+        public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
         {
-            this.invocationOffset      = invocationOffset;
-            this.invocationInstruction = invocationInstruction;
+            refConstant.referencedMemberAccept(this);
         }
 
 
@@ -714,9 +516,9 @@ implements   AttributeVisitor
             if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 &&
                 !ParameterUsageMarker.isParameterUsed(programMethod, 0))
             {
-                replaceInvocationInstruction(programClass,
-                                             invocationOffset,
-                                             invocationInstruction);
+                replaceByStaticInvocation(programClass,
+                                          invocationOffset,
+                                          invocationInstruction);
             }
 
             // Remove unused parameters.
@@ -897,14 +699,14 @@ implements   AttributeVisitor
                     // Push some necessary stack entries.
                     if (requiredPushCount > 0)
                     {
-                        if (DEBUG_ANALYSIS) System.out.print("  Inserting before marked consumer "+instruction.toString(offset));
+                        if (DEBUG) System.out.println("  Inserting before marked consumer "+instruction.toString(offset));
 
                         if (requiredPushCount > (instruction.isCategory2() ? 2 : 1))
                         {
                             throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"]");
                         }
 
-                        increaseStackSize(offset, tracedStack.getTop(0).computationalType(), false);
+                        insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType());
                     }
                 }
 
@@ -933,9 +735,9 @@ implements   AttributeVisitor
                     // Pop the unnecessary stack entries.
                     if (requiredPopCount > 0)
                     {
-                        if (DEBUG_ANALYSIS) System.out.print("  Inserting after marked producer "+instruction.toString(offset));
+                        if (DEBUG) System.out.println("  Inserting after marked producer "+instruction.toString(offset));
 
-                        decreaseStackSize(offset, requiredPopCount, false, false);
+                        insertPopInstructions(offset, false, requiredPopCount);
                     }
                 }
             }
@@ -966,9 +768,9 @@ implements   AttributeVisitor
                     // Pop the unnecessary stack entries.
                     if (expectedPopCount > 0)
                     {
-                        if (DEBUG_ANALYSIS) System.out.print("  Replacing unmarked consumer "+instruction.toString(offset));
+                        if (DEBUG) System.out.println("  Replacing unmarked consumer "+instruction.toString(offset));
 
-                        decreaseStackSize(offset, expectedPopCount, true, true);
+                        insertPopInstructions(offset, true, expectedPopCount);
                     }
                 }
 
@@ -996,9 +798,9 @@ implements   AttributeVisitor
                     // Push some necessary stack entries.
                     if (expectedPushCount > 0)
                     {
-                        if (DEBUG_ANALYSIS) System.out.print("  Replacing unmarked producer "+instruction.toString(offset));
+                        if (DEBUG) System.out.println("  Replacing unmarked producer "+instruction.toString(offset));
 
-                        increaseStackSize(offset, tracedStack.getTop(0).computationalType(), true);
+                        insertPushInstructions(offset, true, tracedStack.getTop(0).computationalType());
                     }
                 }
             }
@@ -1225,7 +1027,7 @@ implements   AttributeVisitor
         if (!isInstructionNecessary(branchOrigin) &&
             isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd))
         {
-            if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
+            if (DEBUG) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
 
             // Mark the branch instruction.
             markInstruction(branchOrigin);
@@ -1450,14 +1252,14 @@ implements   AttributeVisitor
                 extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null);
             }
 
-            if (DEBUG_ANALYSIS) System.out.println("  Marking but deleting instruction "+instruction.toString(dupOffset));
+            if (DEBUG) System.out.println("  Marking but deleting instruction "+instruction.toString(dupOffset));
         }
         else if (newOpcode == oldOpcode)
         {
             // Leave the instruction unchanged.
             codeAttributeEditor.undeleteInstruction(dupOffset);
 
-            if (DEBUG_ANALYSIS) System.out.println("  Marking unchanged instruction "+instruction.toString(dupOffset));
+            if (DEBUG) System.out.println("  Marking unchanged instruction "+instruction.toString(dupOffset));
         }
         else
         {
@@ -1466,23 +1268,18 @@ implements   AttributeVisitor
             codeAttributeEditor.replaceInstruction(dupOffset,
                                                    replacementInstruction);
 
-            if (DEBUG_ANALYSIS) System.out.println("  Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
+            if (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
         }
     }
 
 
     /**
-     * Puts the required push instruction before the given index. The
-     * instruction is marked as necessary.
-     * @param offset            the offset of the instruction.
-     * @param computationalType the computational type on the stack, for
-     *                          push instructions.
-     * @param delete            specifies whether the instruction should be
-     *                          deleted.
+     * Pushes a specified type of stack entry before or at the given offset.
+     * The instruction is marked as necessary.
      */
-    private void increaseStackSize(int     offset,
-                                   int     computationalType,
-                                   boolean delete)
+    private void insertPushInstructions(int     offset,
+                                        boolean replace,
+                                        int     computationalType)
     {
         // Mark this instruction.
         markInstruction(offset);
@@ -1491,22 +1288,18 @@ implements   AttributeVisitor
         Instruction replacementInstruction =
             new SimpleInstruction(pushOpcode(computationalType));
 
-        if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
+        if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset));
 
-        // Delete the original instruction if necessary.
-        if (delete)
+        // Replace or insert the push instruction.
+        if (replace)
         {
-            if (DEBUG_ANALYSIS) System.out.println("  Deleting original instruction at "+offset);
-
             // Replace the push instruction.
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
+            codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
         }
         else
         {
             // Insert the push instruction.
-            codeAttributeEditor.insertBeforeInstruction(offset,
-                                                        replacementInstruction);
+            codeAttributeEditor.insertBeforeInstruction(offset, replacementInstruction);
 
             if (extraAddedInstructionVisitor != null)
             {
@@ -1538,629 +1331,144 @@ implements   AttributeVisitor
 
 
     /**
-     * Puts the required pop instruction at the given index. The
-     * instruction is marked as necessary.
-     * @param offset   the offset of the instruction.
-     * @param popCount the required reduction of the stack size.
-     * @param before   specifies whether the pop instruction should be inserted
-     *                 before or after the present instruction.
-     * @param delete   specifies whether the instruction should be deleted.
+     * Pops the given number of stack entries at or after the given offset.
+     * The instructions are marked as necessary.
      */
-    private void decreaseStackSize(int     offset,
-                                   int     popCount,
-                                   boolean before,
-                                   boolean delete)
+    private void insertPopInstructions(int offset, boolean replace, int popCount)
     {
         // Mark this instruction.
         markInstruction(offset);
 
-        boolean after = !before;
-
-        int remainingPopCount = popCount;
-
-        if (delete)
+        switch (popCount)
         {
-            // Replace the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
-
-            remainingPopCount -= count;
-
-            // We may insert other pop instructions before and after this one.
-            before = true;
-            after  = true;
-        }
-
-        if (before && remainingPopCount > 0)
-        {
-            // Insert before the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.insertBeforeInstruction(offset,
-                                                        replacementInstruction);
-
-            remainingPopCount -= count;
-
-            if (extraAddedInstructionVisitor != null)
+            case 1:
             {
-                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
-            }
-        }
-
-        if (after && remainingPopCount > 0)
-        {
-            // Insert after the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+                // Replace or insert a single pop instruction.
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP);
 
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.insertAfterInstruction(offset,
-                                                       replacementInstruction);
-
-            remainingPopCount -= count;
+                if (replace)
+                {
+                    codeAttributeEditor.replaceInstruction(offset, popInstruction);
+                }
+                else
+                {
+                    codeAttributeEditor.insertAfterInstruction(offset, popInstruction);
 
-            if (extraAddedInstructionVisitor != null)
-            {
-                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+                    if (extraAddedInstructionVisitor != null)
+                    {
+                        popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+                    }
+                }
+                break;
             }
-        }
-
-        if (remainingPopCount > 0)
-        {
-            throw new UnsupportedOperationException("Unsupported stack size reduction ["+popCount+"]");
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Replaces the push instruction at the given offset by a simpler push
-     * instruction, if possible.
-     */
-    private void replaceAnyPushInstruction(Clazz       clazz,
-                                           int         offset,
-                                           Instruction instruction)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            switch (pushedValue.computationalType())
+            case 2:
             {
-                case Value.TYPE_INTEGER:
-                    replaceIntegerPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_LONG:
-                    replaceLongPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_FLOAT:
-                    replaceFloatPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_DOUBLE:
-                    replaceDoublePushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_REFERENCE:
-                    replaceReferencePushInstruction(clazz, offset, instruction);
-                    break;
-            }
-        }
-    }
-
+                // Replace or insert a single pop2 instruction.
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP2);
 
-    /**
-     * Replaces the integer pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceIntegerPushInstruction(Clazz       clazz,
-                                               int         offset,
-                                               Instruction instruction)
-    {
-        replaceIntegerPushInstruction(clazz,
-                                      offset,
-                                      instruction,
-                                      partialEvaluator.getVariablesBefore(offset).size());
-    }
-
-
-    /**
-     * Replaces the integer pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceIntegerPushInstruction(Clazz       clazz,
-                                               int         offset,
-                                               Instruction instruction,
-                                               int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            int value = pushedValue.integerValue().value();
-            if (value << 16 >> 16 == value)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_SIPUSH,
-                                       value);
-            }
-        }
-        else if (pushedValue.isSpecific())
-        {
-            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
-            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
-            {
-                if (pushedValue.equals(variables.load(variableIndex)))
+                if (replace)
                 {
-                    replaceVariablePushInstruction(clazz,
-                                                   offset,
-                                                   instruction,
-                                                   InstructionConstants.OP_ILOAD,
-                                                   variableIndex);
+                    codeAttributeEditor.replaceInstruction(offset, popInstruction);
                 }
-            }
-        }
-    }
-
-
-    /**
-     * Replaces the long pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceLongPushInstruction(Clazz       clazz,
-                                            int         offset,
-                                            Instruction instruction)
-    {
-        replaceLongPushInstruction(clazz,
-                                   offset,
-                                   instruction,
-                                   partialEvaluator.getVariablesBefore(offset).size());
-    }
-
-
-    /**
-     * Replaces the long pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceLongPushInstruction(Clazz       clazz,
-                                            int         offset,
-                                            Instruction instruction,
-                                            int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            long value = pushedValue.longValue().value();
-            if (value == 0L ||
-                value == 1L)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_LCONST_0,
-                                       (int)value);
-            }
-        }
-        else if (pushedValue.isSpecific())
-        {
-            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
-            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
-            {
-                if (pushedValue.equals(variables.load(variableIndex)))
+                else
                 {
-                    replaceVariablePushInstruction(clazz,
-                                                   offset,
-                                                   instruction,
-                                                   InstructionConstants.OP_LLOAD,
-                                                   variableIndex);
+                    codeAttributeEditor.insertAfterInstruction(offset, popInstruction);
+
+                    if (extraAddedInstructionVisitor != null)
+                    {
+                        popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+                    }
                 }
+                break;
             }
-        }
-    }
-
-
-    /**
-     * Replaces the float pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceFloatPushInstruction(Clazz       clazz,
-                                             int         offset,
-                                             Instruction instruction)
-    {
-        replaceFloatPushInstruction(clazz,
-                                    offset,
-                                    instruction,
-                                    partialEvaluator.getVariablesBefore(offset).size());
-    }
+            default:
+            {
+                // Replace or insert the specified number of pop instructions.
+                Instruction[] popInstructions =
+                    new Instruction[popCount / 2 + popCount % 2];
 
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP2);
 
-    /**
-     * Replaces the float pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceFloatPushInstruction(Clazz       clazz,
-                                             int         offset,
-                                             Instruction instruction,
-                                             int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            float value = pushedValue.floatValue().value();
-            if (value == 0f ||
-                value == 1f ||
-                value == 2f)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_FCONST_0,
-                                       (int)value);
-            }
-        }
-        else if (pushedValue.isSpecific())
-        {
-            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
-            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
-            {
-                if (pushedValue.equals(variables.load(variableIndex)))
+                for (int index = 0; index < popCount / 2; index++)
                 {
-                    replaceVariablePushInstruction(clazz,
-                                                   offset,
-                                                   instruction,
-                                                   InstructionConstants.OP_FLOAD,
-                                                   variableIndex);
+                      popInstructions[index] = popInstruction;
                 }
-            }
-        }
-    }
 
+                if (popCount % 2 == 1)
+                {
+                    popInstruction =
+                        new SimpleInstruction(InstructionConstants.OP_POP);
 
-    /**
-     * Replaces the double pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceDoublePushInstruction(Clazz       clazz,
-                                              int         offset,
-                                              Instruction instruction)
-    {
-        replaceDoublePushInstruction(clazz,
-                                     offset,
-                                     instruction,
-                                     partialEvaluator.getVariablesBefore(offset).size());
-    }
+                    popInstructions[popCount / 2] = popInstruction;
+                }
 
+                if (replace)
+                {
+                    codeAttributeEditor.replaceInstruction(offset, popInstructions);
 
-    /**
-     * Replaces the double pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceDoublePushInstruction(Clazz       clazz,
-                                              int         offset,
-                                              Instruction instruction,
-                                              int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            double value = pushedValue.doubleValue().value();
-            if (value == 0.0 ||
-                value == 1.0)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_DCONST_0,
-                                       (int)value);
-            }
-        }
-        else if (pushedValue.isSpecific())
-        {
-            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
-            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
-            {
-                if (pushedValue.equals(variables.load(variableIndex)))
+                    for (int index = 1; index < popInstructions.length; index++)
+                    {
+                        if (extraAddedInstructionVisitor != null)
+                        {
+                            popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor);
+                        }
+                    }
+                }
+                else
                 {
-                    replaceVariablePushInstruction(clazz,
-                                                   offset,
-                                                   instruction,
-                                                   InstructionConstants.OP_DLOAD,
-                                                   variableIndex);
+                    codeAttributeEditor.insertAfterInstruction(offset, popInstructions);
+
+                    for (int index = 0; index < popInstructions.length; index++)
+                    {
+                        if (extraAddedInstructionVisitor != null)
+                        {
+                            popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor);
+                        }
+                    }
                 }
+                break;
             }
         }
     }
 
 
     /**
-     * Replaces the reference pushing instruction at the given offset by a
-     * simpler push instruction, if possible.
-     */
-    private void replaceReferencePushInstruction(Clazz       clazz,
-                                                 int         offset,
-                                                 Instruction instruction)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            // A reference value can only be specific if it is null.
-            replaceConstantPushInstruction(clazz,
-                                   offset,
-                                   instruction,
-                                   InstructionConstants.OP_ACONST_NULL,
-                                   0);
-        }
-    }
-
-
-    /**
-     * Replaces the instruction at a given offset by a given push instruction
-     * of a constant.
-     */
-    private void replaceConstantPushInstruction(Clazz       clazz,
-                                                int         offset,
-                                                Instruction instruction,
-                                                byte        replacementOpcode,
-                                                int         value)
-    {
-        replacePushInstruction(clazz,
-                               offset,
-                               instruction,
-                               new SimpleInstruction(replacementOpcode, value).shrink());
-    }
-
-
-    /**
-     * Replaces the instruction at a given offset by a given push instruction
-     * of a variable.
-     */
-    private void replaceVariablePushInstruction(Clazz       clazz,
-                                                int         offset,
-                                                Instruction instruction,
-                                                byte        replacementOpcode,
-                                                int         variableIndex)
-    {
-        replacePushInstruction(clazz,
-                               offset,
-                               instruction,
-                               new VariableInstruction(replacementOpcode, variableIndex).shrink());
-
-        // Mark that the instruction has been simplified by loading an aliasing
-        // variable.
-        setAliasingVariable(offset, variableIndex);
-    }
-
-
-    /**
-     * Replaces the instruction at a given offset by a given push instruction.
-     */
-    private void replacePushInstruction(Clazz       clazz,
-                                        int         offset,
-                                        Instruction instruction,
-                                        Instruction replacementInstruction)
-    {
-        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, instruction);
-
-        // 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 instruction at a given offset by a static invocation.
      */
-    private void replaceInvocationInstruction(Clazz               clazz,
-                                              int                 offset,
-                                              ConstantInstruction constantInstruction)
+    private void replaceByStaticInvocation(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());
+        if (DEBUG) System.out.println("  Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+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(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(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.
-            replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction);
-        }
-    }
-
-
-    /**
-     * Deletes the given branch instruction, or replaces it by a simpler branch
-     * instruction, if possible.
-     */
-    private void replaceBranchInstruction(Clazz       clazz,
-                                          int         offset,
-                                          Instruction instruction)
-    {
-        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
-
-        // Is there exactly one branch target (not from a goto or jsr)?
-        if (branchTargets != null &&
-            branchTargets.instructionOffsetCount() == 1)
-        {
-            // Is it branching to the next instruction?
-            int branchOffset = branchTargets.instructionOffset(0) - offset;
-            if (branchOffset == instruction.length(offset))
-            {
-                if (DEBUG_ANALYSIS) System.out.println("  Ignoring zero branch instruction at ["+offset+"]");
-            }
-            else
-            {
-                // Replace the branch instruction by a simple branch instruction.
-                Instruction replacementInstruction =
-                    new BranchInstruction(InstructionConstants.OP_GOTO_W,
-                                          branchOffset).shrink();
-
-                if (DEBUG_ANALYSIS) System.out.println("  Replacing branch instruction at ["+offset+"] by "+replacementInstruction.toString());
-
-                codeAttributeEditor.replaceInstruction(offset,
-                                                       replacementInstruction);
-
-                // Mark that the instruction has been simplified.
-                markSimplification(clazz, offset, instruction);
-
-                // Visit the instruction, if required.
-                if (extraBranchInstructionVisitor != null)
-                {
-                    extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Makes sure all branch targets of the given switch instruction are valid.
-     */
-    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;
-
-        Instruction replacementInstruction = null;
-
-        // Check the jump offsets.
-        int[] jumpOffsets = switchInstruction.jumpOffsets;
-        for (int index = 0; index < jumpOffsets.length; index++)
-        {
-            if (!branchTargets.contains(offset + jumpOffsets[index]))
-            {
-                // Replace the unused offset.
-                jumpOffsets[index] = defaultOffset;
-
-                // Remember to replace the instruction.
-                replacementInstruction = switchInstruction;
-            }
-        }
-
-        // Check the default offset.
-        if (!branchTargets.contains(offset + switchInstruction.defaultOffset))
-        {
-            // Replace the unused offset.
-            switchInstruction.defaultOffset = defaultOffset;
-
-            // Remember to replace the instruction.
-            replacementInstruction = switchInstruction;
-        }
-
-        if (replacementInstruction != null)
-        {
-            if (DEBUG_ANALYSIS) System.out.println("  Replacing switch instruction at ["+offset+"] by "+replacementInstruction.toString());
-
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
-
-            // Visit the instruction, if required.
-            if (extraBranchInstructionVisitor != null)
-            {
-                // Note: we're not passing the right arguments for now,
-                // knowing that they aren't used anyway.
-                extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
-            }
-        }
     }
 
 
     /**
      * Replaces the given instruction by an infinite loop.
      */
-    private void replaceByInfiniteLoop(Clazz       clazz,
-                                       int         offset,
-                                       Instruction instruction)
+    private void replaceByInfiniteLoop(Clazz clazz,
+                                       int   offset)
     {
-        if (DEBUG_ANALYSIS) System.out.println("  Inserting infinite loop at ["+offset+"]");
+        if (DEBUG) System.out.println("  Inserting infinite loop at ["+offset+"]");
+
+        // Mark the instruction.
+        markInstruction(offset);
 
         // Replace the instruction by an infinite loop.
         Instruction replacementInstruction =
             new BranchInstruction(InstructionConstants.OP_GOTO, 0);
 
-        codeAttributeEditor.replaceInstruction(offset,
-                                               replacementInstruction);
-
-        // Mark the instruction.
-        markInstruction(offset);
-        markSimplification(clazz, offset, instruction);
+        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
     }
 
 
@@ -2329,23 +1637,15 @@ implements   AttributeVisitor
 
         if (instructionsNecessary.length < codeLength)
         {
-            instructionsNecessary  = new boolean[codeLength];
-            instructionsSimplified = new boolean[codeLength];
-            aliasingVariables      = new int[codeLength];
+            instructionsNecessary = new boolean[codeLength];
         }
         else
         {
             for (int index = 0; index < codeLength; index++)
             {
-                instructionsNecessary[index]  = false;
-                instructionsSimplified[index] = false;
+                instructionsNecessary[index] = false;
             }
         }
-
-        for (int index = 0; index < codeLength; index++)
-        {
-            aliasingVariables[index] = -1;
-        }
     }
 
 
@@ -2384,13 +1684,13 @@ implements   AttributeVisitor
         // Is the variable necessary anywhere at all?
         if (isVariableNecessaryAfterAny(0, codeLength, variableIndex))
         {
-            if (DEBUG_ANALYSIS) System.out.println("Simple partial evalutation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
+            if (DEBUG) System.out.println("Simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
 
             // Lazily compute perform simple partial evaluation, the way the
             // JVM preverifier would do it.
             simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
 
-            if (DEBUG_ANALYSIS) System.out.println("End of simple partial evalutation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
+            if (DEBUG) System.out.println("End of simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
 
             // Check if the variable is necessary elsewhere.
             for (int offset = 0; offset < codeLength; offset++)
@@ -2435,7 +1735,7 @@ implements   AttributeVisitor
     {
         if (!isVariableNecessaryAfter(instructionOffset, variableIndex))
         {
-            if (DEBUG_ANALYSIS) System.out.print("["+instructionOffset+".v"+variableIndex+"],");
+            if (DEBUG) System.out.print("["+instructionOffset+".v"+variableIndex+"],");
 
             variablesNecessaryAfter[instructionOffset][variableIndex] = true;
 
@@ -2502,7 +1802,7 @@ implements   AttributeVisitor
     {
         if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex))
         {
-            if (DEBUG_ANALYSIS) System.out.print("["+instructionOffset+".s"+stackIndex+"],");
+            if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],");
 
             stacksNecessaryAfter[instructionOffset][stackIndex] = true;
 
@@ -2553,51 +1853,11 @@ implements   AttributeVisitor
     }
 
 
-    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 setAliasingVariable(int instructionOffset,
-                                     int variableIndex)
-    {
-        aliasingVariables[instructionOffset] = variableIndex;
-    }
-
-
-    private int getAliasingVariable(int instructionOffset)
-    {
-        return aliasingVariables[instructionOffset];
-    }
-
-
     private void markInstruction(int instructionOffset)
     {
         if (!isInstructionNecessary(instructionOffset))
         {
-            if (DEBUG_ANALYSIS) System.out.print(instructionOffset+",");
+            if (DEBUG) System.out.print(instructionOffset+",");
 
             instructionsNecessary[instructionOffset] = true;
 
@@ -2649,4 +1909,4 @@ implements   AttributeVisitor
         return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY ||
                instructionsNecessary[instructionOffset];
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
index 95abf42..0c3a9c7 100644
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -23,7 +23,7 @@ package proguard.optimize.evaluation;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.visitor.AttributeVisitor;
-import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.editor.*;
 import proguard.classfile.instruction.*;
 import proguard.classfile.instruction.visitor.InstructionVisitor;
 import proguard.classfile.util.*;
@@ -40,75 +40,44 @@ import proguard.optimize.info.*;
  */
 public class EvaluationSimplifier
 extends      SimplifiedVisitor
-implements   AttributeVisitor
+implements   AttributeVisitor,
+             InstructionVisitor
 {
     //*
-    private static final boolean DEBUG_RESULTS  = false;
-    private static final boolean DEBUG_ANALYSIS = false;
-    private static final boolean DEBUG          = false;
+    private static final boolean DEBUG = false;
     /*/
-    private static boolean DEBUG_RESULTS  = true;
-    private static boolean DEBUG_ANALYSIS = true;
-    private static boolean DEBUG          = true;
+    private static boolean DEBUG       = true;
     //*/
 
-    private final InstructionVisitor extraPushInstructionVisitor;
-    private final InstructionVisitor extraBranchInstructionVisitor;
-    private final InstructionVisitor extraDeletedInstructionVisitor;
-    private final InstructionVisitor extraAddedInstructionVisitor;
+    private final InstructionVisitor extraInstructionVisitor;
 
     private final PartialEvaluator             partialEvaluator;
-    private final PartialEvaluator             simplePartialEvaluator       = new PartialEvaluator();
     private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
-    private final MyInstructionSimplifier      instructionSimplifier        = new MyInstructionSimplifier();
-    private final MyProducerMarker             producerMarker               = new MyProducerMarker();
-    private final MyStackConsistencyFixer      stackConsistencyFixer        = new MyStackConsistencyFixer();
     private final CodeAttributeEditor          codeAttributeEditor          = new CodeAttributeEditor(false);
 
-    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[]       aliasingVariables       = new int[ClassConstants.TYPICAL_CODE_LENGTH];
-
-    private int maxMarkedOffset;
-
 
     /**
      * Creates a new EvaluationSimplifier.
      */
     public EvaluationSimplifier()
     {
-        this(new PartialEvaluator(), null, null, null, null);
+        this(new PartialEvaluator(), null);
     }
 
 
     /**
      * Creates a new EvaluationSimplifier.
-     * @param partialEvaluator               the partial evaluator that will
-     *                                       execute the code and provide
-     *                                       information about the results.
-     * @param extraPushInstructionVisitor    an optional extra visitor for all
-     *                                       simplified push instructions.
-     * @param extraBranchInstructionVisitor  an optional extra visitor for all
-     *                                       simplified branch instructions.
-     * @param extraDeletedInstructionVisitor an optional extra visitor for all
-     *                                       deleted instructions.
-     * @param extraAddedInstructionVisitor   an optional extra visitor for all
-     *                                       added instructions.
+     * @param partialEvaluator        the partial evaluator that will
+     *                                execute the code and provide
+     *                                information about the results.
+     * @param extraInstructionVisitor an optional extra visitor for all
+     *                                simplified instructions.
      */
     public EvaluationSimplifier(PartialEvaluator   partialEvaluator,
-                                InstructionVisitor extraPushInstructionVisitor,
-                                InstructionVisitor extraBranchInstructionVisitor,
-                                InstructionVisitor extraDeletedInstructionVisitor,
-                                InstructionVisitor extraAddedInstructionVisitor)
+                                InstructionVisitor extraInstructionVisitor)
     {
-        this.partialEvaluator                      = partialEvaluator;
-        this.extraPushInstructionVisitor           = extraPushInstructionVisitor;
-        this.extraBranchInstructionVisitor         = extraBranchInstructionVisitor;
-        this.extraDeletedInstructionVisitor        = extraDeletedInstructionVisitor;
-        this.extraAddedInstructionVisitor          = extraAddedInstructionVisitor;
+        this.partialEvaluator        = partialEvaluator;
+        this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
@@ -119,7 +88,7 @@ implements   AttributeVisitor
 
     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-//        DEBUG = DEBUG_ANALYSIS = DEBUG_RESULTS =
+//        DEBUG =
 //            clazz.getName().equals("abc/Def") &&
 //            method.getName(clazz).equals("abc");
 
@@ -132,7 +101,7 @@ implements   AttributeVisitor
         }
         catch (RuntimeException ex)
         {
-            System.err.println("Unexpected error while optimizing after partial evaluation:");
+            System.err.println("Unexpected error while simplifying instructions after partial evaluation:");
             System.err.println("  Class       = ["+clazz.getName()+"]");
             System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
             System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
@@ -150,7 +119,7 @@ implements   AttributeVisitor
 
     public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        if (DEBUG_RESULTS)
+        if (DEBUG)
         {
             System.out.println();
             System.out.println("Class "+ClassUtil.externalClassName(clazz.getName()));
@@ -160,9 +129,6 @@ implements   AttributeVisitor
                                                                                  method.getDescriptor(clazz)));
         }
 
-        // Initialize the necessary array.
-        initializeNecessary(codeAttribute);
-
         // Evaluate the method.
         partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
 
@@ -172,8 +138,6 @@ implements   AttributeVisitor
         codeAttributeEditor.reset(codeLength);
 
         // Replace any instructions that can be simplified.
-        if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
-
         for (int offset = 0; offset < codeLength; offset++)
         {
             if (partialEvaluator.isTraced(offset))
@@ -181,1596 +145,376 @@ implements   AttributeVisitor
                 Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
-                instruction.accept(clazz, method, codeAttribute, offset, instructionSimplifier);
+                instruction.accept(clazz, method, codeAttribute, offset, this);
             }
         }
 
-        // Mark all essential instructions that have been encountered as used.
-        if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
+        // Apply all accumulated changes to the code.
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+    }
 
-        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("(super.<init>)");
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        switch (simpleInstruction.opcode)
+        {
+            case InstructionConstants.OP_IALOAD:
+            case InstructionConstants.OP_BALOAD:
+            case InstructionConstants.OP_CALOAD:
+            case InstructionConstants.OP_SALOAD:
+            case InstructionConstants.OP_IADD:
+            case InstructionConstants.OP_ISUB:
+            case InstructionConstants.OP_IMUL:
+            case InstructionConstants.OP_IDIV:
+            case InstructionConstants.OP_IREM:
+            case InstructionConstants.OP_INEG:
+            case InstructionConstants.OP_ISHL:
+            case InstructionConstants.OP_ISHR:
+            case InstructionConstants.OP_IUSHR:
+            case InstructionConstants.OP_IAND:
+            case InstructionConstants.OP_IOR:
+            case InstructionConstants.OP_IXOR:
+            case InstructionConstants.OP_L2I:
+            case InstructionConstants.OP_F2I:
+            case InstructionConstants.OP_D2I:
+            case InstructionConstants.OP_I2B:
+            case InstructionConstants.OP_I2C:
+            case InstructionConstants.OP_I2S:
+                replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
+                break;
 
-            markInstruction(superInitializationOffset);
-        }
+            case InstructionConstants.OP_LALOAD:
+            case InstructionConstants.OP_LADD:
+            case InstructionConstants.OP_LSUB:
+            case InstructionConstants.OP_LMUL:
+            case InstructionConstants.OP_LDIV:
+            case InstructionConstants.OP_LREM:
+            case InstructionConstants.OP_LNEG:
+            case InstructionConstants.OP_LSHL:
+            case InstructionConstants.OP_LSHR:
+            case InstructionConstants.OP_LUSHR:
+            case InstructionConstants.OP_LAND:
+            case InstructionConstants.OP_LOR:
+            case InstructionConstants.OP_LXOR:
+            case InstructionConstants.OP_I2L:
+            case InstructionConstants.OP_F2L:
+            case InstructionConstants.OP_D2L:
+                replaceLongPushInstruction(clazz, offset, simpleInstruction);
+                break;
 
-        // Also mark infinite loops and instructions that cause side effects.
-        for (int offset = 0; offset < codeLength; offset++)
-        {
-            if (partialEvaluator.isTraced(offset))
-            {
-                Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                    offset);
+            case InstructionConstants.OP_FALOAD:
+            case InstructionConstants.OP_FADD:
+            case InstructionConstants.OP_FSUB:
+            case InstructionConstants.OP_FMUL:
+            case InstructionConstants.OP_FDIV:
+            case InstructionConstants.OP_FREM:
+            case InstructionConstants.OP_FNEG:
+            case InstructionConstants.OP_I2F:
+            case InstructionConstants.OP_L2F:
+            case InstructionConstants.OP_D2F:
+                replaceFloatPushInstruction(clazz, offset, simpleInstruction);
+                break;
 
-                // Mark that the instruction is necessary if it is an infinite loop.
-                if (instruction.opcode == InstructionConstants.OP_GOTO &&
-                    ((BranchInstruction)instruction).branchOffset == 0)
-                {
-                    if (DEBUG_ANALYSIS) System.out.print("(infinite loop)");
-                    markInstruction(offset);
-                }
+            case InstructionConstants.OP_DALOAD:
+            case InstructionConstants.OP_DADD:
+            case InstructionConstants.OP_DSUB:
+            case InstructionConstants.OP_DMUL:
+            case InstructionConstants.OP_DDIV:
+            case InstructionConstants.OP_DREM:
+            case InstructionConstants.OP_DNEG:
+            case InstructionConstants.OP_I2D:
+            case InstructionConstants.OP_L2D:
+            case InstructionConstants.OP_F2D:
+                replaceDoublePushInstruction(clazz, offset, simpleInstruction);
+                break;
 
-                // Mark that the instruction is necessary if it has side effects.
-                else if (sideEffectInstructionChecker.hasSideEffects(clazz,
-                                                                     method,
-                                                                     codeAttribute,
-                                                                     offset,
-                                                                     instruction))
-                {
-                    markInstruction(offset);
-                }
-            }
+            case InstructionConstants.OP_AALOAD:
+                replaceReferencePushInstruction(clazz, offset, simpleInstruction);
+                break;
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+    }
 
 
-        // 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:");
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        int variableIndex = variableInstruction.variableIndex;
 
-        while (maxMarkedOffset >= 0)
+        switch (variableInstruction.opcode)
         {
-            int offset = maxMarkedOffset;
-
-            maxMarkedOffset = offset - 1;
+            case InstructionConstants.OP_ILOAD:
+            case InstructionConstants.OP_ILOAD_0:
+            case InstructionConstants.OP_ILOAD_1:
+            case InstructionConstants.OP_ILOAD_2:
+            case InstructionConstants.OP_ILOAD_3:
+                replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
+                break;
 
-            if (partialEvaluator.isTraced(offset))
-            {
-                if (isInstructionNecessary(offset))
-                {
-                    if (isInstructionSimplified(offset))
-                    {
-                        int variableIndex = getAliasingVariable(offset);
-
-                        if (variableIndex >= 0)
-                        {
-                            markVariableProducers(offset, variableIndex);
-                        }
-                    }
-                    else
-                    {
-                        Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                            offset);
-
-                        instruction.accept(clazz, method, codeAttribute, offset, producerMarker);
-                    }
-                }
+            case InstructionConstants.OP_LLOAD:
+            case InstructionConstants.OP_LLOAD_0:
+            case InstructionConstants.OP_LLOAD_1:
+            case InstructionConstants.OP_LLOAD_2:
+            case InstructionConstants.OP_LLOAD_3:
+                replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
+                break;
 
-                // 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);
-            }
+            case InstructionConstants.OP_FLOAD:
+            case InstructionConstants.OP_FLOAD_0:
+            case InstructionConstants.OP_FLOAD_1:
+            case InstructionConstants.OP_FLOAD_2:
+            case InstructionConstants.OP_FLOAD_3:
+                replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
+                break;
 
-            if (DEBUG_ANALYSIS)
-            {
-                if (maxMarkedOffset > offset)
-                {
-                    System.out.println(" -> "+maxMarkedOffset);
-                }
-            }
-        }
-        if (DEBUG_ANALYSIS) System.out.println();
+            case InstructionConstants.OP_DLOAD:
+            case InstructionConstants.OP_DLOAD_0:
+            case InstructionConstants.OP_DLOAD_1:
+            case InstructionConstants.OP_DLOAD_2:
+            case InstructionConstants.OP_DLOAD_3:
+                replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
+                break;
 
+            case InstructionConstants.OP_ALOAD:
+            case InstructionConstants.OP_ALOAD_0:
+            case InstructionConstants.OP_ALOAD_1:
+            case InstructionConstants.OP_ALOAD_2:
+            case InstructionConstants.OP_ALOAD_3:
+                replaceReferencePushInstruction(clazz, offset, variableInstruction);
+                break;
 
-        // 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: ");
+            case InstructionConstants.OP_ASTORE:
+            case InstructionConstants.OP_ASTORE_0:
+            case InstructionConstants.OP_ASTORE_1:
+            case InstructionConstants.OP_ASTORE_2:
+            case InstructionConstants.OP_ASTORE_3:
+                deleteReferencePopInstruction(clazz, offset, variableInstruction);
+                break;
 
-        for (int offset = 0; offset < codeLength; offset++)
-        {
-            // Is it a variable initialization that hasn't been marked yet?
-            if (partialEvaluator.isTraced(offset) &&
-                !isInstructionNecessary(offset))
-            {
-                // Is the corresponding variable necessary anywhere in the code,
-                // accoriding to a simple partial evaluation?
-                int variableIndex = partialEvaluator.initializedVariable(offset);
-                if (variableIndex >= 0 &&
-                    isVariableInitializationNecessary(clazz,
-                                                      method,
-                                                      codeAttribute,
-                                                      offset,
-                                                      variableIndex))
-                {
-                    markInstruction(offset);
-                }
-            }
+            case InstructionConstants.OP_RET:
+                replaceBranchInstruction(clazz, offset, variableInstruction);
+                break;
         }
-        if (DEBUG_ANALYSIS) System.out.println();
-
-
-        // Locally fix instructions, in order to keep the stack consistent.
-        if (DEBUG_ANALYSIS) System.out.println("Stack consistency fixing:");
+    }
 
-        maxMarkedOffset = codeLength - 1;
 
-        while (maxMarkedOffset >= 0)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        switch (constantInstruction.opcode)
         {
-            int offset = maxMarkedOffset;
-
-            maxMarkedOffset = offset - 1;
-
-            if (partialEvaluator.isTraced(offset))
-            {
-                Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                    offset);
-
-                instruction.accept(clazz, method, codeAttribute, offset, stackConsistencyFixer);
-
-                // 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) System.out.println();
-
+            case InstructionConstants.OP_GETSTATIC:
+            case InstructionConstants.OP_GETFIELD:
+                replaceAnyPushInstruction(clazz, offset, constantInstruction);
+                break;
 
-        // 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:");
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                if (constantInstruction.stackPushCount(clazz) > 0 &&
+                    !sideEffectInstructionChecker.hasSideEffects(clazz,
+                                                                 method,
+                                                                 codeAttribute,
+                                                                 offset,
+                                                                 constantInstruction))
+                {
+                    replaceAnyPushInstruction(clazz, offset, constantInstruction);
+                }
 
-        for (int offset = 0; offset < codeLength; 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);
+                break;
 
-                replaceByInfiniteLoop(clazz, offset, instruction);
-            }
+            case InstructionConstants.OP_CHECKCAST:
+                replaceReferencePushInstruction(clazz, offset, constantInstruction);
+                break;
         }
-        if (DEBUG_ANALYSIS) System.out.println();
+    }
 
 
-        // Delete all instructions that are not used.
-        int offset = 0;
-        do
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        switch (branchInstruction.opcode)
         {
-            Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                offset);
-            if (!isInstructionNecessary(offset))
-            {
-                codeAttributeEditor.deleteInstruction(offset);
-
-                codeAttributeEditor.insertBeforeInstruction(offset, null);
-                codeAttributeEditor.replaceInstruction(offset, null);
-                codeAttributeEditor.insertAfterInstruction(offset, null);
+            case InstructionConstants.OP_GOTO:
+            case InstructionConstants.OP_GOTO_W:
+                // Don't replace unconditional branches.
+                break;
 
-                // Visit the instruction, if required.
-                if (extraDeletedInstructionVisitor != null)
-                {
-                    instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor);
-                }
-            }
+            case InstructionConstants.OP_JSR:
+            case InstructionConstants.OP_JSR_W:
+                replaceJsrInstruction(clazz, offset, branchInstruction);
+                break;
 
-            offset += instruction.length(offset);
+            default:
+                replaceBranchInstruction(clazz, offset, branchInstruction);
+                break;
         }
-        while (offset < codeLength);
-
-
-        if (DEBUG_RESULTS)
-        {
-            System.out.println("Simplification results:");
+    }
 
-            offset = 0;
-            do
-            {
-                Instruction instruction = InstructionFactory.create(codeAttribute.code,
-                                                                    offset);
-                System.out.println((isInstructionNecessary(offset) ? " + " : " - ")+instruction.toString(offset));
 
-                if (partialEvaluator.isTraced(offset))
-                {
-                    int initializationOffset = partialEvaluator.initializationOffset(offset);
-                    if (initializationOffset != PartialEvaluator.NONE)
-                    {
-                        System.out.println("     is to be initialized at ["+initializationOffset+"]");
-                    }
-
-                    InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
-                    if (branchTargets != null)
-                    {
-                        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)
-                    {
-                        System.out.println("     is preceded by: "+preInsertion);
-                    }
-
-                    Instruction replacement = codeAttributeEditor.replacements[offset];
-                    if (replacement != null)
-                    {
-                        System.out.println("     is replaced by: "+replacement);
-                    }
-
-                    Instruction postInsertion = codeAttributeEditor.postInsertions[offset];
-                    if (postInsertion != null)
-                    {
-                        System.out.println("     is followed by: "+postInsertion);
-                    }
-                }
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        // First try to simplify it to a simple branch.
+        replaceBranchInstruction(clazz, offset, switchInstruction);
 
-                offset += instruction.length(offset);
-            }
-            while (offset < codeLength);
+        // Otherwise make sure all branch targets are valid.
+        if (!codeAttributeEditor.isModified(offset))
+        {
+            replaceSwitchInstruction(clazz, offset, switchInstruction);
         }
-
-        // Apply all accumulated changes to the code.
-        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
     }
 
 
+    // Small utility methods.
+
     /**
-     * This InstructionVisitor simplifies the instructions that it visits,
-     * whenever possible, and marks them as such.
+     * Replaces the push instruction at the given offset by a simpler push
+     * instruction, if possible.
      */
-    private class MyInstructionSimplifier
-    extends       SimplifiedVisitor
-    implements    InstructionVisitor
+    private void replaceAnyPushInstruction(Clazz       clazz,
+                                           int         offset,
+                                           Instruction instruction)
     {
-        // Implementations for InstructionVisitor.
-
-        public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+        if (pushedValue.isParticular())
         {
-            switch (simpleInstruction.opcode)
+            switch (pushedValue.computationalType())
             {
-                case InstructionConstants.OP_IALOAD:
-                case InstructionConstants.OP_BALOAD:
-                case InstructionConstants.OP_CALOAD:
-                case InstructionConstants.OP_SALOAD:
-                case InstructionConstants.OP_IADD:
-                case InstructionConstants.OP_ISUB:
-                case InstructionConstants.OP_IMUL:
-                case InstructionConstants.OP_IDIV:
-                case InstructionConstants.OP_IREM:
-                case InstructionConstants.OP_INEG:
-                case InstructionConstants.OP_ISHL:
-                case InstructionConstants.OP_ISHR:
-                case InstructionConstants.OP_IUSHR:
-                case InstructionConstants.OP_IAND:
-                case InstructionConstants.OP_IOR:
-                case InstructionConstants.OP_IXOR:
-                case InstructionConstants.OP_L2I:
-                case InstructionConstants.OP_F2I:
-                case InstructionConstants.OP_D2I:
-                case InstructionConstants.OP_I2B:
-                case InstructionConstants.OP_I2C:
-                case InstructionConstants.OP_I2S:
-                    replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
+                case Value.TYPE_INTEGER:
+                    replaceIntegerPushInstruction(clazz, offset, instruction);
                     break;
-
-                case InstructionConstants.OP_LALOAD:
-                case InstructionConstants.OP_LADD:
-                case InstructionConstants.OP_LSUB:
-                case InstructionConstants.OP_LMUL:
-                case InstructionConstants.OP_LDIV:
-                case InstructionConstants.OP_LREM:
-                case InstructionConstants.OP_LNEG:
-                case InstructionConstants.OP_LSHL:
-                case InstructionConstants.OP_LSHR:
-                case InstructionConstants.OP_LUSHR:
-                case InstructionConstants.OP_LAND:
-                case InstructionConstants.OP_LOR:
-                case InstructionConstants.OP_LXOR:
-                case InstructionConstants.OP_I2L:
-                case InstructionConstants.OP_F2L:
-                case InstructionConstants.OP_D2L:
-                    replaceLongPushInstruction(clazz, offset, simpleInstruction);
+                case Value.TYPE_LONG:
+                    replaceLongPushInstruction(clazz, offset, instruction);
                     break;
-
-                case InstructionConstants.OP_FALOAD:
-                case InstructionConstants.OP_FADD:
-                case InstructionConstants.OP_FSUB:
-                case InstructionConstants.OP_FMUL:
-                case InstructionConstants.OP_FDIV:
-                case InstructionConstants.OP_FREM:
-                case InstructionConstants.OP_FNEG:
-                case InstructionConstants.OP_I2F:
-                case InstructionConstants.OP_L2F:
-                case InstructionConstants.OP_D2F:
-                    replaceFloatPushInstruction(clazz, offset, simpleInstruction);
+                case Value.TYPE_FLOAT:
+                    replaceFloatPushInstruction(clazz, offset, instruction);
                     break;
-
-                case InstructionConstants.OP_DALOAD:
-                case InstructionConstants.OP_DADD:
-                case InstructionConstants.OP_DSUB:
-                case InstructionConstants.OP_DMUL:
-                case InstructionConstants.OP_DDIV:
-                case InstructionConstants.OP_DREM:
-                case InstructionConstants.OP_DNEG:
-                case InstructionConstants.OP_I2D:
-                case InstructionConstants.OP_L2D:
-                case InstructionConstants.OP_F2D:
-                    replaceDoublePushInstruction(clazz, offset, simpleInstruction);
+                case Value.TYPE_DOUBLE:
+                    replaceDoublePushInstruction(clazz, offset, instruction);
                     break;
-
-                case InstructionConstants.OP_AALOAD:
-                    replaceReferencePushInstruction(clazz, offset, simpleInstruction);
+                case Value.TYPE_REFERENCE:
+                    replaceReferencePushInstruction(clazz, offset, instruction);
                     break;
             }
         }
+    }
 
 
-        public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
-        {
-            int variableIndex = variableInstruction.variableIndex;
-
-            switch (variableInstruction.opcode)
-            {
-                case InstructionConstants.OP_ILOAD:
-                case InstructionConstants.OP_ILOAD_0:
-                case InstructionConstants.OP_ILOAD_1:
-                case InstructionConstants.OP_ILOAD_2:
-                case InstructionConstants.OP_ILOAD_3:
-                    replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_LLOAD:
-                case InstructionConstants.OP_LLOAD_0:
-                case InstructionConstants.OP_LLOAD_1:
-                case InstructionConstants.OP_LLOAD_2:
-                case InstructionConstants.OP_LLOAD_3:
-                    replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_FLOAD:
-                case InstructionConstants.OP_FLOAD_0:
-                case InstructionConstants.OP_FLOAD_1:
-                case InstructionConstants.OP_FLOAD_2:
-                case InstructionConstants.OP_FLOAD_3:
-                    replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_DLOAD:
-                case InstructionConstants.OP_DLOAD_0:
-                case InstructionConstants.OP_DLOAD_1:
-                case InstructionConstants.OP_DLOAD_2:
-                case InstructionConstants.OP_DLOAD_3:
-                    replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
-                    break;
-
-                case InstructionConstants.OP_ALOAD:
-                case InstructionConstants.OP_ALOAD_0:
-                case InstructionConstants.OP_ALOAD_1:
-                case InstructionConstants.OP_ALOAD_2:
-                case InstructionConstants.OP_ALOAD_3:
-                    replaceReferencePushInstruction(clazz, offset, variableInstruction);
-                    break;
-
-            }
-        }
+    /**
+     * Replaces the integer pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
+     */
+    private void replaceIntegerPushInstruction(Clazz       clazz,
+                                               int         offset,
+                                               Instruction instruction)
+    {
+        replaceIntegerPushInstruction(clazz,
+                                      offset,
+                                      instruction,
+                                      partialEvaluator.getVariablesBefore(offset).size());
+    }
 
 
-        public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    /**
+     * Replaces the integer pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
+     */
+    private void replaceIntegerPushInstruction(Clazz       clazz,
+                                               int         offset,
+                                               Instruction instruction,
+                                               int         maxVariableIndex)
+    {
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+        if (pushedValue.isParticular())
         {
-            switch (constantInstruction.opcode)
+            int value = pushedValue.integerValue().value();
+            if (value << 16 >> 16 == value)
             {
-                case InstructionConstants.OP_GETSTATIC:
-                case InstructionConstants.OP_GETFIELD:
-                    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,
-                                                                     codeAttribute,
-                                                                     offset,
-                                                                     constantInstruction))
-                    {
-                        replaceAnyPushInstruction(clazz, offset, constantInstruction);
-                    }
-
-                    break;
-
-                case InstructionConstants.OP_CHECKCAST:
-                    replaceReferencePushInstruction(clazz, offset, constantInstruction);
-                    break;
+                replaceConstantPushInstruction(clazz,
+                                               offset,
+                                               instruction,
+                                               InstructionConstants.OP_SIPUSH,
+                                               value);
             }
-        }
-
-
-        public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
-        {
-            switch (branchInstruction.opcode)
+            else
             {
-                case InstructionConstants.OP_GOTO:
-                case InstructionConstants.OP_GOTO_W:
-                    // Don't replace unconditional branches.
-                    break;
+                ConstantPoolEditor constantPoolEditor =
+                    new ConstantPoolEditor((ProgramClass)clazz);
 
-                case InstructionConstants.OP_JSR:
-                case InstructionConstants.OP_JSR_W:
-                    replaceJsrInstruction(clazz, offset, branchInstruction);
-                    break;
+                Instruction replacementInstruction =
+                    new ConstantInstruction(InstructionConstants.OP_LDC,
+                                            constantPoolEditor.addIntegerConstant(value)).shrink();
 
-                default:
-                    replaceBranchInstruction(clazz, offset, branchInstruction);
-                    break;
+                replaceInstruction(clazz, offset, instruction, replacementInstruction);
             }
         }
-
-
-        public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+        else if (pushedValue.isSpecific())
         {
-            // First try to simplify it to a simple branch.
-            replaceBranchInstruction(clazz, offset, switchInstruction);
-
-            // Otherwise make sure all branch targets are valid.
-            if (!isInstructionSimplified(offset))
+            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
+            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
             {
-                replaceSwitchInstruction(clazz, offset, switchInstruction);
+                if (pushedValue.equals(variables.load(variableIndex)))
+                {
+                    replaceVariablePushInstruction(clazz,
+                                                   offset,
+                                                   instruction,
+                                                   InstructionConstants.OP_ILOAD,
+                                                   variableIndex);
+                }
             }
         }
     }
 
 
     /**
-     * This MemberVisitor marks stack entries that aren't necessary because
-     * parameters aren't used in the methods that are visited.
+     * Replaces the long pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
      */
-    private class MyUnusedParameterSimplifier
-    extends       SimplifiedVisitor
-    implements    MemberVisitor
+    private void replaceLongPushInstruction(Clazz       clazz,
+                                            int         offset,
+                                            Instruction instruction)
     {
-        private int                 invocationOffset;
-        private ConstantInstruction invocationInstruction;
-
-
-        /**
-         * Creates a new MyUnusedParameterSimplifier for the given invocation
-         * at the given offset.
-         */
-        private MyUnusedParameterSimplifier(int                 invocationOffset,
-                                            ConstantInstruction invocationInstruction)
-        {
-            this.invocationOffset      = invocationOffset;
-            this.invocationInstruction = invocationInstruction;
-        }
-
-
-        // Implementations for MemberVisitor.
-
-        public void visitAnyMember(Clazz clazz, Member member) {}
+        replaceLongPushInstruction(clazz,
+                                   offset,
+                                   instruction,
+                                   partialEvaluator.getVariablesBefore(offset).size());
+    }
 
 
-        public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    /**
+     * Replaces the long pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
+     */
+    private void replaceLongPushInstruction(Clazz       clazz,
+                                            int         offset,
+                                            Instruction instruction,
+                                            int         maxVariableIndex)
+    {
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+        if (pushedValue.isParticular())
         {
-            // Get the total size of the parameters.
-            int parameterSize = ParameterUsageMarker.getParameterSize(programMethod);
-
-            // Make the method invocation static, if possible.
-            if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 &&
-                !ParameterUsageMarker.isParameterUsed(programMethod, 0))
+            long value = pushedValue.longValue().value();
+            if (value == 0L ||
+                value == 1L)
             {
-                replaceInvocationInstruction(programClass,
-                                             invocationOffset,
-                                             invocationInstruction);
+                replaceConstantPushInstruction(clazz,
+                                       offset,
+                                       instruction,
+                                       InstructionConstants.OP_LCONST_0,
+                                       (int)value);
             }
-
-            // Remove unused parameters.
-            for (int index = 0; index < parameterSize; index++)
+            else
             {
-                if (!ParameterUsageMarker.isParameterUsed(programMethod, index))
-                {
-                    TracedStack stack =
-                        partialEvaluator.getStackBefore(invocationOffset);
+                ConstantPoolEditor constantPoolEditor =
+                    new ConstantPoolEditor((ProgramClass)clazz);
 
-                    int stackIndex = stack.size() - parameterSize + index;
-
-                    if (DEBUG)
-                    {
-                        System.out.println("  ["+invocationOffset+"] Ignoring parameter #"+index+" of "+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])");
-                        System.out.println("    Full stack: "+stack);
-                    }
+                Instruction replacementInstruction =
+                    new ConstantInstruction(InstructionConstants.OP_LDC2_W,
+                                            constantPoolEditor.addLongConstant(value)).shrink();
 
-                    markStackSimplificationBefore(invocationOffset, stackIndex);
-                }
+                replaceInstruction(clazz, offset, instruction, replacementInstruction);
             }
         }
-    }
-
-
-    /**
-     * This InstructionVisitor marks the producing instructions and produced
-     * variables and stack entries of the instructions that it visits.
-     * Simplified method arguments are ignored.
-     */
-    private class MyProducerMarker
-    extends       SimplifiedVisitor
-    implements    InstructionVisitor
-    {
-        // Implementations for InstructionVisitor.
-
-        public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
-        {
-            markStackProducers(clazz, offset, instruction);
-        }
-
-
-        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;
-            }
-        }
-
-
-        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);
-            }
-        }
-
-
-        public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
-        {
-            // Mark the initializer invocation, if this is a 'new' instruction.
-            if (constantInstruction.opcode == InstructionConstants.OP_NEW)
-            {
-                markInitialization(offset);
-            }
-
-            markStackProducers(clazz, offset, constantInstruction);
-        }
-
-
-        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);
-            }
-        }
-    }
-
-
-    /**
-     * 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 class MyStackConsistencyFixer
-    extends       SimplifiedVisitor
-    implements    InstructionVisitor
-    {
-        // Implementations for InstructionVisitor.
-
-        public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
-        {
-            // 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;
-
-                    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));
-
-                        if (requiredPushCount > (instruction.isCategory2() ? 2 : 1))
-                        {
-                            throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"]");
-                        }
-
-                        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
-            {
-                // 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;
-
-                    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);
-            }
-        }
-    }
-
-
-    // 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 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 void markVariableProducers(InstructionOffsetValue producerOffsets,
-                                       int                    variableIndex)
-    {
-        if (producerOffsets != null)
-        {
-            int offsetCount = producerOffsets.instructionOffsetCount();
-            for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
-            {
-                // Make sure the variable and the instruction are marked
-                // at the producing offset.
-                int offset = producerOffsets.instructionOffset(offsetIndex);
-
-                markVariableAfter(offset, variableIndex);
-                markInstruction(offset);
-            }
-        }
-    }
-
-
-    /**
-     * 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 void markStackProducers(Clazz       clazz,
-                                    int         consumerOffset,
-                                    Instruction consumer)
-    {
-        // Mark the producers of the popped values.
-        int popCount = consumer.stackPopCount(clazz);
-        for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
-        {
-            markStackEntryProducers(consumerOffset, stackIndex);
-        }
-    }
-
-
-    /**
-     * 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;
-
-        if (isStackEntryNecessaryAfter(consumerOffset, top - consumerStackIndex))
-        {
-            markStackEntryProducers(consumerOffset, producerStackIndex);
-        }
-    }
-
-
-    /**
-     * 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 void markStackEntryProducers(int consumerOffset,
-                                         int stackIndex)
-    {
-        TracedStack tracedStack =
-            partialEvaluator.getStackBefore(consumerOffset);
-
-        int stackBottomIndex = tracedStack.size() - 1 - stackIndex;
-
-        if (!isStackSimplifiedBefore(consumerOffset, stackBottomIndex))
-        {
-            markStackEntryProducers(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(),
-                                    stackBottomIndex);
-        }
-    }
-
-
-    /**
-     * 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 void markStackEntryProducers(InstructionOffsetValue producerOffsets,
-                                         int                    stackIndex)
-    {
-        if (producerOffsets != null)
-        {
-            int offsetCount = producerOffsets.instructionOffsetCount();
-            for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
-            {
-                // Make sure the stack entry and the instruction are marked
-                // at the producing offset.
-                int offset = producerOffsets.instructionOffset(offsetIndex);
-
-                markStackEntryAfter(offset, stackIndex);
-                markInstruction(offset);
-            }
-        }
-    }
-
-
-    /**
-     * 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 void markInitialization(int newInstructionOffset)
-    {
-        int initializationOffset =
-            partialEvaluator.initializationOffset(newInstructionOffset);
-
-        TracedStack tracedStack =
-            partialEvaluator.getStackAfter(newInstructionOffset);
-
-        markStackEntryAfter(initializationOffset, tracedStack.size() - 1);
-        markInstruction(initializationOffset);
-    }
-
-
-    /**
-     * 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++)
-            {
-                // Is the branch straddling forward any necessary instructions?
-                int branchOffset = branchOffsets.instructionOffset(branchIndex);
-
-                // 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);
-                }
-            }
-        }
-    }
-
-
-    private void markStraddlingBranch(int instructionOffsetStart,
-                                      int instructionOffsetEnd,
-                                      int branchOrigin,
-                                      int branchTarget)
-    {
-        if (!isInstructionNecessary(branchOrigin) &&
-            isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd))
-        {
-            if (DEBUG_ANALYSIS) System.out.print("["+branchOrigin+"->"+branchTarget+"]");
-
-            // Mark the branch instruction.
-            markInstruction(branchOrigin);
-        }
-    }
-
-
-    /**
-     * Marks the specified instruction if it is a required dup/swap instruction,
-     * replacing it by an appropriate variant if necessary.
-     * @param clazz         the class that is being checked.
-     * @param codeAttribute the code that is being checked.
-     * @param dupOffset     the offset of the dup/swap instruction.
-     * @param instruction   the dup/swap instruction.
-     */
-    private void fixDupInstruction(Clazz         clazz,
-                                   CodeAttribute codeAttribute,
-                                   int           dupOffset,
-                                   Instruction   instruction)
-    {
-        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, top - 0);
-                boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1);
-
-                // Should either the original element or the copy be present?
-                if (stackEntryPresent0 ||
-                    stackEntryPresent1)
-                {
-                    // Should both the original element and the copy be present?
-                    if (stackEntryPresent0 &&
-                        stackEntryPresent1)
-                    {
-                        newOpcode = InstructionConstants.OP_DUP;
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_DUP_X1:
-            {
-                boolean stackEntryPresent0 = 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)
-                {
-                    // Should the copy be present?
-                    if (stackEntryPresent2)
-                    {
-                        // Compute the number of elements to be skipped.
-                        int skipCount = stackEntryPresent1 ? 1 : 0;
-
-                        // Should the original element be present?
-                        if (stackEntryPresent0)
-                        {
-                            // Copy the original element.
-                            newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount);
-                        }
-                        else if (skipCount == 1)
-                        {
-                            // Move the original element.
-                            newOpcode = InstructionConstants.OP_SWAP;
-                        }
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_DUP_X2:
-            {
-                boolean stackEntryPresent0 = 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)
-                {
-                    // Should the copy be present?
-                    if (stackEntryPresent3)
-                    {
-                        int skipCount = (stackEntryPresent1 ? 1 : 0) +
-                                        (stackEntryPresent2 ? 1 : 0);
-
-                        // Should the original element be present?
-                        if (stackEntryPresent0)
-                        {
-                            // Copy the original element.
-                            newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount);
-                        }
-                        else if (skipCount == 1)
-                        {
-                            // Move the original element.
-                            newOpcode = InstructionConstants.OP_SWAP;
-                        }
-                        else if (skipCount == 2)
-                        {
-                            // We can't easily move the original element.
-                            throw new UnsupportedOperationException("Can't handle dup_x2 instruction moving original element across two elements at ["+dupOffset +"]");
-                        }
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_DUP2:
-            {
-                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)
-                {
-                    // Should both the original element and the copy be present?
-                    if (stackEntriesPresent01 &&
-                        stackEntriesPresent23)
-                    {
-                        newOpcode = InstructionConstants.OP_DUP2;
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_DUP2_X1:
-            {
-                boolean stackEntriesPresent01 = 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)
-                {
-                    // Should the copy be present?
-                    if (stackEntriesPresent34)
-                    {
-                        int skipCount = stackEntryPresent2 ? 1 : 0;
-
-                        // Should the original element be present?
-                        if (stackEntriesPresent01)
-                        {
-                            // Copy the original element.
-                            newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount);
-                        }
-                        else if (skipCount > 0)
-                        {
-                            // We can't easily move the original element.
-                            throw new UnsupportedOperationException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]");
-                        }
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_DUP2_X2:
-            {
-                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)
-                {
-                    // Should the copy be present?
-                    if (stackEntriesPresent45)
-                    {
-                        int skipCount = (stackEntryPresent2 ? 1 : 0) +
-                                        (stackEntryPresent3 ? 1 : 0);
-
-                        // Should the original element be present?
-                        if (stackEntriesPresent01)
-                        {
-                            // Copy the original element.
-                            newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount);
-                        }
-                        else if (skipCount > 0)
-                        {
-                            // We can't easily move the original element.
-                            throw new UnsupportedOperationException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]");
-                        }
-                    }
-                }
-                break;
-            }
-            case InstructionConstants.OP_SWAP:
-            {
-                boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0);
-                boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1);
-
-                // Will either element be present?
-                if (stackEntryPresent0 ||
-                    stackEntryPresent1)
-                {
-                    // Will both elements be present?
-                    if (stackEntryPresent0 &&
-                        stackEntryPresent1)
-                    {
-                        newOpcode = InstructionConstants.OP_SWAP;
-                    }
-                }
-                break;
-            }
-        }
-
-        if      (newOpcode == 0)
-        {
-            // Delete the instruction.
-            codeAttributeEditor.deleteInstruction(dupOffset);
-
-            if (extraDeletedInstructionVisitor != null)
-            {
-                extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null);
-            }
-
-            if (DEBUG_ANALYSIS) System.out.println("  Marking but deleting instruction "+instruction.toString(dupOffset));
-        }
-        else if (newOpcode == oldOpcode)
-        {
-            // Leave the instruction unchanged.
-            codeAttributeEditor.undeleteInstruction(dupOffset);
-
-            if (DEBUG_ANALYSIS) System.out.println("  Marking unchanged instruction "+instruction.toString(dupOffset));
-        }
-        else
-        {
-            // 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());
-        }
-    }
-
-
-    /**
-     * Puts the required push instruction before the given index. The
-     * instruction is marked as necessary.
-     * @param offset            the offset of the instruction.
-     * @param computationalType the computational type on the stack, for
-     *                          push instructions.
-     * @param delete            specifies whether the instruction should be
-     *                          deleted.
-     */
-    private void increaseStackSize(int     offset,
-                                   int     computationalType,
-                                   boolean delete)
-    {
-        // Mark this instruction.
-        markInstruction(offset);
-
-        // Create a simple push instrucion.
-        Instruction replacementInstruction =
-            new SimpleInstruction(pushOpcode(computationalType));
-
-        if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-        // Delete the original instruction if necessary.
-        if (delete)
-        {
-            if (DEBUG_ANALYSIS) System.out.println("  Deleting original instruction at "+offset);
-
-            // Replace the push instruction.
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
-        }
-        else
-        {
-            // Insert the push instruction.
-            codeAttributeEditor.insertBeforeInstruction(offset,
-                                                        replacementInstruction);
-
-            if (extraAddedInstructionVisitor != null)
-            {
-                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
-            }
-        }
-    }
-
-
-    /**
-     * Returns the opcode of a push instruction corresponding to the given
-     * computational type.
-     * @param computationalType the computational type to be pushed on the stack.
-     */
-    private byte pushOpcode(int computationalType)
-    {
-        switch (computationalType)
-        {
-            case Value.TYPE_INTEGER:            return InstructionConstants.OP_ICONST_0;
-            case Value.TYPE_LONG:               return InstructionConstants.OP_LCONST_0;
-            case Value.TYPE_FLOAT:              return InstructionConstants.OP_FCONST_0;
-            case Value.TYPE_DOUBLE:             return InstructionConstants.OP_DCONST_0;
-            case Value.TYPE_REFERENCE:
-            case Value.TYPE_INSTRUCTION_OFFSET: return InstructionConstants.OP_ACONST_NULL;
-        }
-
-        throw new IllegalArgumentException("No push opcode for computational type ["+computationalType+"]");
-    }
-
-
-    /**
-     * Puts the required pop instruction at the given index. The
-     * instruction is marked as necessary.
-     * @param offset   the offset of the instruction.
-     * @param popCount the required reduction of the stack size.
-     * @param before   specifies whether the pop instruction should be inserted
-     *                 before or after the present instruction.
-     * @param delete   specifies whether the instruction should be deleted.
-     */
-    private void decreaseStackSize(int     offset,
-                                   int     popCount,
-                                   boolean before,
-                                   boolean delete)
-    {
-        // Mark this instruction.
-        markInstruction(offset);
-
-        boolean after = !before;
-
-        int remainingPopCount = popCount;
-
-        if (delete)
-        {
-            // Replace the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
-
-            remainingPopCount -= count;
-
-            // We may insert other pop instructions before and after this one.
-            before = true;
-            after  = true;
-        }
-
-        if (before && remainingPopCount > 0)
-        {
-            // Insert before the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.insertBeforeInstruction(offset,
-                                                        replacementInstruction);
-
-            remainingPopCount -= count;
-
-            if (extraAddedInstructionVisitor != null)
-            {
-                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
-            }
-        }
-
-        if (after && remainingPopCount > 0)
-        {
-            // Insert after the original instruction.
-            int count = remainingPopCount == 1 ? 1 : 2;
-
-            // Create a simple pop instrucion.
-            byte replacementOpcode = count == 1 ?
-                InstructionConstants.OP_POP :
-                InstructionConstants.OP_POP2;
-
-            Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
-
-            if (DEBUG_ANALYSIS) System.out.println(": "+replacementInstruction.toString(offset));
-
-            // Insert the pop instruction.
-            codeAttributeEditor.insertAfterInstruction(offset,
-                                                       replacementInstruction);
-
-            remainingPopCount -= count;
-
-            if (extraAddedInstructionVisitor != null)
-            {
-                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
-            }
-        }
-
-        if (remainingPopCount > 0)
-        {
-            throw new UnsupportedOperationException("Unsupported stack size reduction ["+popCount+"]");
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Replaces the push instruction at the given offset by a simpler push
-     * instruction, if possible.
-     */
-    private void replaceAnyPushInstruction(Clazz       clazz,
-                                           int         offset,
-                                           Instruction instruction)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            switch (pushedValue.computationalType())
-            {
-                case Value.TYPE_INTEGER:
-                    replaceIntegerPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_LONG:
-                    replaceLongPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_FLOAT:
-                    replaceFloatPushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_DOUBLE:
-                    replaceDoublePushInstruction(clazz, offset, instruction);
-                    break;
-                case Value.TYPE_REFERENCE:
-                    replaceReferencePushInstruction(clazz, offset, instruction);
-                    break;
-            }
-        }
-    }
-
-
-    /**
-     * Replaces the integer pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceIntegerPushInstruction(Clazz       clazz,
-                                               int         offset,
-                                               Instruction instruction)
-    {
-        replaceIntegerPushInstruction(clazz,
-                                      offset,
-                                      instruction,
-                                      partialEvaluator.getVariablesBefore(offset).size());
-    }
-
-
-    /**
-     * Replaces the integer pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceIntegerPushInstruction(Clazz       clazz,
-                                               int         offset,
-                                               Instruction instruction,
-                                               int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            int value = pushedValue.integerValue().value();
-            if (value << 16 >> 16 == value)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_SIPUSH,
-                                       value);
-            }
-        }
-        else if (pushedValue.isSpecific())
-        {
-            TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
-            for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
-            {
-                if (pushedValue.equals(variables.load(variableIndex)))
-                {
-                    replaceVariablePushInstruction(clazz,
-                                                   offset,
-                                                   instruction,
-                                                   InstructionConstants.OP_ILOAD,
-                                                   variableIndex);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Replaces the long pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceLongPushInstruction(Clazz       clazz,
-                                            int         offset,
-                                            Instruction instruction)
-    {
-        replaceLongPushInstruction(clazz,
-                                   offset,
-                                   instruction,
-                                   partialEvaluator.getVariablesBefore(offset).size());
-    }
-
-
-    /**
-     * Replaces the long pushing instruction at the given offset by a simpler
-     * push instruction, if possible.
-     */
-    private void replaceLongPushInstruction(Clazz       clazz,
-                                            int         offset,
-                                            Instruction instruction,
-                                            int         maxVariableIndex)
-    {
-        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
-        if (pushedValue.isParticular())
-        {
-            long value = pushedValue.longValue().value();
-            if (value == 0L ||
-                value == 1L)
-            {
-                replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_LCONST_0,
-                                       (int)value);
-            }
-        }
-        else if (pushedValue.isSpecific())
+        else if (pushedValue.isSpecific())
         {
             TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
             for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
@@ -1821,10 +565,21 @@ implements   AttributeVisitor
                 value == 2f)
             {
                 replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_FCONST_0,
-                                       (int)value);
+                                               offset,
+                                               instruction,
+                                               InstructionConstants.OP_FCONST_0,
+                                               (int)value);
+            }
+            else
+            {
+                ConstantPoolEditor constantPoolEditor =
+                    new ConstantPoolEditor((ProgramClass)clazz);
+
+                Instruction replacementInstruction =
+                    new ConstantInstruction(InstructionConstants.OP_LDC,
+                                            constantPoolEditor.addFloatConstant(value)).shrink();
+
+                replaceInstruction(clazz, offset, instruction, replacementInstruction);
             }
         }
         else if (pushedValue.isSpecific())
@@ -1877,10 +632,21 @@ implements   AttributeVisitor
                 value == 1.0)
             {
                 replaceConstantPushInstruction(clazz,
-                                       offset,
-                                       instruction,
-                                       InstructionConstants.OP_DCONST_0,
-                                       (int)value);
+                                               offset,
+                                               instruction,
+                                               InstructionConstants.OP_DCONST_0,
+                                               (int)value);
+            }
+            else
+            {
+                ConstantPoolEditor constantPoolEditor =
+                    new ConstantPoolEditor((ProgramClass)clazz);
+
+                Instruction replacementInstruction =
+                    new ConstantInstruction(InstructionConstants.OP_LDC2_W,
+                                            constantPoolEditor.addDoubleConstant(value)).shrink();
+
+                replaceInstruction(clazz, offset, instruction, replacementInstruction);
             }
         }
         else if (pushedValue.isSpecific())
@@ -1914,10 +680,10 @@ implements   AttributeVisitor
         {
             // A reference value can only be specific if it is null.
             replaceConstantPushInstruction(clazz,
-                                   offset,
-                                   instruction,
-                                   InstructionConstants.OP_ACONST_NULL,
-                                   0);
+                                           offset,
+                                           instruction,
+                                           InstructionConstants.OP_ACONST_NULL,
+                                           0);
         }
     }
 
@@ -1932,10 +698,10 @@ implements   AttributeVisitor
                                                 byte        replacementOpcode,
                                                 int         value)
     {
-        replacePushInstruction(clazz,
-                               offset,
-                               instruction,
-                               new SimpleInstruction(replacementOpcode, value).shrink());
+        Instruction replacementInstruction =
+            new SimpleInstruction(replacementOpcode, value).shrink();
+
+        replaceInstruction(clazz, offset, instruction, replacementInstruction);
     }
 
 
@@ -1944,86 +710,31 @@ implements   AttributeVisitor
      * of a variable.
      */
     private void replaceVariablePushInstruction(Clazz       clazz,
-                                                int         offset,
-                                                Instruction instruction,
-                                                byte        replacementOpcode,
-                                                int         variableIndex)
-    {
-        replacePushInstruction(clazz,
-                               offset,
-                               instruction,
-                               new VariableInstruction(replacementOpcode, variableIndex).shrink());
-
-        // Mark that the instruction has been simplified by loading an aliasing
-        // variable.
-        setAliasingVariable(offset, variableIndex);
-    }
-
-
-    /**
-     * Replaces the instruction at a given offset by a given push instruction.
-     */
-    private void replacePushInstruction(Clazz       clazz,
-                                        int         offset,
-                                        Instruction instruction,
-                                        Instruction replacementInstruction)
-    {
-        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, instruction);
-
-        // 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 instruction at a given offset by a static invocation.
-     */
-    private void replaceInvocationInstruction(Clazz               clazz,
-                                              int                 offset,
-                                              ConstantInstruction constantInstruction)
+                                                int         offset,
+                                                Instruction instruction,
+                                                byte        replacementOpcode,
+                                                int         variableIndex)
     {
-        // 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);
+            new VariableInstruction(replacementOpcode, variableIndex).shrink();
 
-        // 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);
-        //}
+        replaceInstruction(clazz, offset, instruction, replacementInstruction);
     }
 
 
     /**
      * Replaces the given 'jsr' instruction by a simpler branch instruction,
-     * if possible.
+     * if it jumps to a subroutine that doesn't return or a subroutine that
+     * is only called from one place.
      */
     private void replaceJsrInstruction(Clazz             clazz,
                                        int               offset,
                                        BranchInstruction branchInstruction)
     {
         // Is the subroutine ever returning?
-        if (!partialEvaluator.isSubroutineReturning(offset + branchInstruction.branchOffset))
+        int subroutineStart = offset + branchInstruction.branchOffset;
+        if (!partialEvaluator.isSubroutineReturning(subroutineStart) ||
+            partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1)
         {
             // All 'jsr' instructions to this subroutine can be replaced
             // by unconditional branch instructions.
@@ -2039,6 +750,27 @@ implements   AttributeVisitor
 
 
     /**
+     * Deletes the reference popping instruction at the given offset, if
+     * it is at the start of a subroutine that doesn't return or a subroutine
+     * that is only called from one place.
+     */
+    private void deleteReferencePopInstruction(Clazz       clazz,
+                                               int         offset,
+                                               Instruction instruction)
+    {
+        if (partialEvaluator.isSubroutineStart(offset) &&
+            (!partialEvaluator.isSubroutineReturning(offset) ||
+             partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1))
+        {
+            if (DEBUG) System.out.println("  Deleting store of subroutine return address "+instruction.toString(offset));
+
+            // A reference value can only be specific if it is null.
+            codeAttributeEditor.deleteInstruction(offset);
+        }
+    }
+
+
+    /**
      * Deletes the given branch instruction, or replaces it by a simpler branch
      * instruction, if possible.
      */
@@ -2056,7 +788,7 @@ implements   AttributeVisitor
             int branchOffset = branchTargets.instructionOffset(0) - offset;
             if (branchOffset == instruction.length(offset))
             {
-                if (DEBUG_ANALYSIS) System.out.println("  Ignoring zero branch instruction at ["+offset+"]");
+                if (DEBUG) System.out.println("  Ignoring zero branch instruction at ["+offset+"]");
             }
             else
             {
@@ -2065,19 +797,7 @@ implements   AttributeVisitor
                     new BranchInstruction(InstructionConstants.OP_GOTO_W,
                                           branchOffset).shrink();
 
-                if (DEBUG_ANALYSIS) System.out.println("  Replacing branch instruction at ["+offset+"] by "+replacementInstruction.toString());
-
-                codeAttributeEditor.replaceInstruction(offset,
-                                                       replacementInstruction);
-
-                // Mark that the instruction has been simplified.
-                markSimplification(clazz, offset, instruction);
-
-                // Visit the instruction, if required.
-                if (extraBranchInstructionVisitor != null)
-                {
-                    extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
-                }
+                replaceInstruction(clazz, offset, instruction, replacementInstruction);
             }
         }
     }
@@ -2126,18 +846,7 @@ implements   AttributeVisitor
 
         if (replacementInstruction != null)
         {
-            if (DEBUG_ANALYSIS) System.out.println("  Replacing switch instruction at ["+offset+"] by "+replacementInstruction.toString());
-
-            codeAttributeEditor.replaceInstruction(offset,
-                                                   replacementInstruction);
-
-            // Visit the instruction, if required.
-            if (extraBranchInstructionVisitor != null)
-            {
-                // Note: we're not passing the right arguments for now,
-                // knowing that they aren't used anyway.
-                extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
-            }
+            replaceInstruction(clazz, offset, switchInstruction, replacementInstruction);
         }
     }
 
@@ -2149,504 +858,111 @@ implements   AttributeVisitor
                                        int         offset,
                                        Instruction 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);
 
-        codeAttributeEditor.replaceInstruction(offset,
-                                               replacementInstruction);
-
-        // Mark the instruction.
-        markInstruction(offset);
-        markSimplification(clazz, offset, instruction);
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Returns whether the given instruction is a dup or swap instruction
-     * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap).
-     */
-    private boolean isDupOrSwap(Instruction instruction)
-    {
-        return instruction.opcode >= InstructionConstants.OP_DUP &&
-               instruction.opcode <= InstructionConstants.OP_SWAP;
-    }
-
-
-    /**
-     * Returns whether the given instruction is a pop instruction
-     * (pop, pop2).
-     */
-    private boolean isPop(Instruction instruction)
-    {
-        return instruction.opcode == InstructionConstants.OP_POP ||
-               instruction.opcode == InstructionConstants.OP_POP2;
-    }
-
-
-    /**
-     * Returns whether any traced but unnecessary instruction between the two
-     * given offsets is branching over the second given offset.
-     */
-    private boolean isAnyUnnecessaryInstructionBranchingOver(int instructionOffset1,
-                                                             int instructionOffset2)
-    {
-        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;
-            }
-        }
-
-        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)
-        {
-            // 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;
-            }
-        }
-
-        return false;
-    }
+        if (DEBUG) System.out.println("  Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset));
 
+        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
-    /**
-     * 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)
+        // Visit the instruction, if required.
+        if (extraInstructionVisitor != null)
         {
-            // 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 true;
-                    }
-                }
-            }
+            // Note: we're not passing the right arguments for now, knowing that
+            // they aren't used anyway.
+            instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
         }
-
-        return false;
     }
 
 
     /**
-     * Initializes the necessary data structure.
+     * Replaces the instruction at a given offset by a given push instruction.
      */
-    private void initializeNecessary(CodeAttribute codeAttribute)
+    private void replaceInstruction(Clazz       clazz,
+                                    int         offset,
+                                    Instruction instruction,
+                                    Instruction replacementInstruction)
     {
-        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;
-                }
-            }
-        }
+        // Pop unneeded stack entries if necessary.
+        int popCount =
+            instruction.stackPopCount(clazz) -
+            replacementInstruction.stackPopCount(clazz);
 
-        if (stacksNecessaryAfter.length    < codeLength ||
-            stacksNecessaryAfter[0].length < maxStack)
-        {
-            stacksNecessaryAfter = new boolean[codeLength][maxStack];
-        }
-        else
-        {
-            for (int offset = 0; offset < codeLength; offset++)
-            {
-                for (int index = 0; index < maxStack; index++)
-                {
-                    stacksNecessaryAfter[offset][index] = false;
-                }
-            }
-        }
+        insertPopInstructions(offset, popCount);
 
-        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 (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
 
-        if (instructionsNecessary.length < codeLength)
-        {
-            instructionsNecessary  = new boolean[codeLength];
-            instructionsSimplified = new boolean[codeLength];
-            aliasingVariables      = new int[codeLength];
-        }
-        else
-        {
-            for (int index = 0; index < codeLength; index++)
-            {
-                instructionsNecessary[index]  = false;
-                instructionsSimplified[index] = false;
-            }
-        }
+        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
-        for (int index = 0; index < codeLength; index++)
+        // Visit the instruction, if required.
+        if (extraInstructionVisitor != null)
         {
-            aliasingVariables[index] = -1;
+            // Note: we're not passing the right arguments for now, knowing that
+            // they aren't used anyway.
+            instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
         }
     }
 
 
     /**
-     * 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 present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1);
-        boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2);
-
-//        if (present1 ^ present2)
-//        {
-//            throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions");
-//        }
-
-        return present1 || present2;
-    }
-
-
-    /**
-     * Returns whether the specified variable must be initialized at the
-     * specified offset, according to the verifier of the JVM.
+     * Pops the given number of stack entries before the instruction at the
+     * given offset.
      */
-    private boolean isVariableInitializationNecessary(Clazz         clazz,
-                                                      Method        method,
-                                                      CodeAttribute codeAttribute,
-                                                      int           initializationOffset,
-                                                      int           variableIndex)
-    {
-        int codeLength = codeAttribute.u4codeLength;
-
-        // Is the variable necessary anywhere at all?
-        if (isVariableNecessaryAfterAny(0, codeLength, variableIndex))
-        {
-            if (DEBUG_ANALYSIS) System.out.println("Simple partial evalutation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
-
-            // Lazily compute perform simple partial evaluation, the way the
-            // JVM preverifier would do it.
-            simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
-
-            if (DEBUG_ANALYSIS) System.out.println("End of simple partial evalutation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
-
-            // Check if the variable is necessary elsewhere.
-            for (int offset = 0; offset < codeLength; offset++)
-            {
-                if (isInstructionNecessary(offset))
-                {
-                    Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex);
-                    if (producer != null)
-                    {
-                        Value simpleProducer = simplePartialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex);
-                        if (simpleProducer != null)
-                        {
-                            InstructionOffsetValue producerOffsets =
-                                producer.instructionOffsetValue();
-                            InstructionOffsetValue simpleProducerOffsets =
-                                simpleProducer.instructionOffsetValue();
-
-                            // Does the sophisticated partial evaluation have fewer
-                            // producers than the simple one?
-                            // And does the simple partial evaluation point to an
-                            // initialization of the variable?
-                            if (producerOffsets.instructionOffsetCount() <
-                                simpleProducerOffsets.instructionOffsetCount() &&
-                                isVariableNecessaryAfterAny(producerOffsets, variableIndex) &&
-                                simpleProducerOffsets.contains(initializationOffset))
-                            {
-                                // Then the initialization is necessary.
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    private void markVariableAfter(int instructionOffset,
-                                   int variableIndex)
+    private void insertPopInstructions(int offset, int popCount)
     {
-        if (!isVariableNecessaryAfter(instructionOffset, variableIndex))
+        switch (popCount)
         {
-            if (DEBUG_ANALYSIS) System.out.print("["+instructionOffset+".v"+variableIndex+"],");
-
-            variablesNecessaryAfter[instructionOffset][variableIndex] = true;
-
-            if (maxMarkedOffset < instructionOffset)
+            case 0:
             {
-                maxMarkedOffset = instructionOffset;
+                break;
             }
-        }
-    }
-
-
-    /**
-     * Returns whether the specified variable is ever necessary after any
-     * instruction in the specified block.
-     */
-    private boolean isVariableNecessaryAfterAny(int startOffset,
-                                                int endOffset,
-                                                int variableIndex)
-    {
-        for (int offset = startOffset; offset < endOffset; offset++)
-        {
-            if (isVariableNecessaryAfter(offset, variableIndex))
+            case 1:
             {
-                return true;
-            }
-        }
-
-        return false;
-    }
+                // Insert a single pop instruction.
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP);
 
-
-    /**
-     * Returns whether the specified variable is ever necessary after any
-     * instruction in the specified set of instructions offsets.
-     */
-    private boolean isVariableNecessaryAfterAny(InstructionOffsetValue instructionOffsetValue,
-                                                int                    variableIndex)
-    {
-        int count = instructionOffsetValue.instructionOffsetCount();
-
-        for (int index = 0; index < count; index++)
-        {
-            if (isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index),
-                                         variableIndex))
-            {
-                return true;
+                codeAttributeEditor.insertBeforeInstruction(offset,
+                                                            popInstruction);
+                break;
             }
-        }
-
-        return false;
-    }
-
-
-    private boolean isVariableNecessaryAfter(int instructionOffset,
-                                             int variableIndex)
-    {
-        return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY ||
-               variablesNecessaryAfter[instructionOffset][variableIndex];
-    }
-
-
-    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)
+            case 2:
             {
-                maxMarkedOffset = instructionOffset;
-            }
-        }
-    }
+                // Insert a single pop2 instruction.
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP2);
 
-
-    private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets,
-                                                  int                    stackIndex)
-    {
-        int offsetCount = instructionOffsets.instructionOffsetCount();
-
-        for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
-        {
-            if (isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex))
-            {
-                return true;
+                codeAttributeEditor.insertBeforeInstruction(offset,
+                                                            popInstruction);
+                break;
             }
-        }
-
-        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 setAliasingVariable(int instructionOffset,
-                                     int variableIndex)
-    {
-        aliasingVariables[instructionOffset] = variableIndex;
-    }
-
-
-    private int getAliasingVariable(int instructionOffset)
-    {
-        return aliasingVariables[instructionOffset];
-    }
-
-
-    private void markInstruction(int instructionOffset)
-    {
-        if (!isInstructionNecessary(instructionOffset))
-        {
-            if (DEBUG_ANALYSIS) System.out.print(instructionOffset+",");
-
-            instructionsNecessary[instructionOffset] = true;
-
-            if (maxMarkedOffset < instructionOffset)
+            default:
             {
-                maxMarkedOffset = instructionOffset;
-            }
-        }
-    }
+                // Insert the specified number of pop instructions.
+                Instruction[] popInstructions =
+                    new Instruction[popCount / 2 + popCount % 2];
 
+                Instruction popInstruction =
+                    new SimpleInstruction(InstructionConstants.OP_POP2);
 
-    private boolean isAnyInstructionNecessary(int instructionOffset1,
-                                              int instructionOffset2)
-    {
-        for (int instructionOffset = instructionOffset1;
-             instructionOffset < instructionOffset2;
-             instructionOffset++)
-        {
-            if (isInstructionNecessary(instructionOffset))
-            {
-                return true;
-            }
-        }
+                for (int index = 0; index < popCount / 2; index++)
+                {
+                      popInstructions[index] = popInstruction;
+                }
 
-        return false;
-    }
+                if (popCount % 2 == 1)
+                {
+                    popInstruction =
+                        new SimpleInstruction(InstructionConstants.OP_POP);
 
+                    popInstructions[popCount / 2] = popInstruction;
+                }
 
-    /**
-     * 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 offset;
+                codeAttributeEditor.insertBeforeInstruction(offset,
+                                                            popInstructions);
+                break;
             }
         }
-
-        return 0;
-    }
-
-
-    private boolean isInstructionNecessary(int instructionOffset)
-    {
-        return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY ||
-               instructionsNecessary[instructionOffset];
     }
 }
diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
index e45842d..9915027 100644
--- a/src/proguard/optimize/evaluation/LivenessAnalyzer.java
+++ b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -348,7 +348,7 @@ implements   AttributeVisitor,
                 // Start marking the variable before the load instruction.
                 alive |= livenessMask;
             }
-            else if (variableInstruction.opcode != InstructionConstants.OP_IINC)
+            else
             {
                 // Stop marking the variable before the store instruction.
                 alive &= ~livenessMask;
@@ -429,6 +429,10 @@ implements   AttributeVisitor,
     }
 
 
+    /**
+     * Returns the combined liveness mask of the variables right before the
+     * specified instruction offsets.
+     */
     private long combinedLiveness(InstructionOffsetValue instructionOffsetValue)
     {
         long alive = 0L;
diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
index 61de720..8379c57 100644
--- a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
+++ b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -35,12 +35,34 @@ import proguard.evaluation.value.*;
 public class LoadingInvocationUnit
 extends      BasicInvocationUnit
 {
+    private boolean loadFieldValues;
+    private boolean loadMethodParameterValues;
+    private boolean loadMethodReturnValues;
+
+
     /**
      * Creates a new LoadingInvocationUnit with the given value factory.
      */
     public LoadingInvocationUnit(ValueFactory valueFactory)
     {
+        this(valueFactory, false, false, false);
+    }
+
+
+    /**
+     * Creates a new LoadingInvocationUnit with the given value factory, for
+     * loading the specified values.
+     */
+    public LoadingInvocationUnit(ValueFactory valueFactory,
+                                 boolean      loadFieldValues,
+                                 boolean      loadMethodParameterValues,
+                                 boolean      loadMethodReturnValues)
+    {
         super(valueFactory);
+
+        this.loadFieldValues           = loadFieldValues;
+        this.loadMethodParameterValues = loadMethodParameterValues;
+        this.loadMethodReturnValues    = loadMethodReturnValues;
     }
 
 
@@ -50,18 +72,21 @@ extends      BasicInvocationUnit
                                        RefConstant refConstant,
                                        String      type)
     {
-        // Do we know this field?
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (loadFieldValues)
         {
-            // Retrieve the stored field class value.
-            ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember);
-            if (value != null &&
-                value.isParticular())
+            // Do we know this field?
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
             {
-                return value;
+                // Retrieve the stored field class value.
+                ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember);
+                if (value != null &&
+                    value.isParticular())
+                {
+                    return value;
 //                // Make sure the value is refreshed.
 //                return refresh(value);
+                }
             }
         }
 
@@ -73,18 +98,21 @@ extends      BasicInvocationUnit
                                   RefConstant refConstant,
                                   String      type)
     {
-        // Do we know this field?
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (loadFieldValues)
         {
-            // Retrieve the stored field value.
-            Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember);
-            if (value != null &&
-                value.isParticular())
+            // Do we know this field?
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
             {
-                return value;
+                // Retrieve the stored field value.
+                Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember);
+                if (value != null &&
+                    value.isParticular())
+                {
+                    return value;
 //                // Make sure the value is refreshed.
 //                return refresh(value);
+                }
             }
         }
 
@@ -98,14 +126,17 @@ extends      BasicInvocationUnit
                                             String type,
                                             Clazz  referencedClass)
     {
-        // Retrieve the stored method parameter value.
-        Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex);
+        if (loadMethodParameterValues)
+        {
+            // Retrieve the stored method parameter value.
+            Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex);
             if (value != null &&
                 value.isParticular())
             {
                 return value;
 //            // Make sure the value is refreshed.
 //            return refresh(value);
+            }
         }
 
         return super.getMethodParameterValue(clazz,
@@ -120,18 +151,21 @@ extends      BasicInvocationUnit
                                          RefConstant refConstant,
                                          String      type)
     {
-        // Do we know this method?
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (loadMethodReturnValues)
         {
-            // Retrieve the stored method return value.
-            Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember);
-            if (value != null &&
-                value.isParticular())
+            // Do we know this method?
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
             {
-                return value;
+                // Retrieve the stored method return value.
+                Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember);
+                if (value != null &&
+                    value.isParticular())
+                {
+                    return value;
 //                // Make sure the value is refreshed.
 //                return refresh(value);
+                }
             }
         }
 
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
index 12d4cc0..5790a36 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -26,7 +26,7 @@ import proguard.classfile.attribute.visitor.*;
 import proguard.classfile.constant.ClassConstant;
 import proguard.classfile.instruction.*;
 import proguard.classfile.util.*;
-import proguard.classfile.visitor.ClassPrinter;
+import proguard.classfile.visitor.*;
 import proguard.evaluation.*;
 import proguard.evaluation.value.*;
 import proguard.optimize.peephole.BranchTargetFinder;
@@ -72,8 +72,10 @@ implements   AttributeVisitor,
     private boolean                  evaluateExceptions;
 
     private final BasicBranchUnit    branchUnit;
-    private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
-//    private ClassCleaner       classCleaner       = new ClassCleaner();
+    private final BranchTargetFinder branchTargetFinder;
+
+    private final java.util.Stack callingInstructionBlockStack;
+    private final java.util.Stack instructionBlockStack = new java.util.Stack();
 
 
     /**
@@ -81,7 +83,9 @@ implements   AttributeVisitor,
      */
     public PartialEvaluator()
     {
-        this(new ValueFactory(), new BasicInvocationUnit(new ValueFactory()), true);
+        this(new ValueFactory(),
+             new BasicInvocationUnit(new ValueFactory()),
+             true);
     }
 
 
@@ -92,20 +96,68 @@ implements   AttributeVisitor,
      * @param invocationUnit  the invocation unit that will handle all
      *                        communication with other fields and methods.
      * @param evaluateAllCode a flag that specifies whether all branch targets
-     *                        and exception 'catch' blocks should be evaluated,
+     *                        and exception handlers should be evaluated,
      *                        even if they are unreachable.
      */
     public PartialEvaluator(ValueFactory   valueFactory,
                             InvocationUnit invocationUnit,
                             boolean        evaluateAllCode)
     {
-        this.valueFactory    = valueFactory;
-        this.invocationUnit  = invocationUnit;
-        this.evaluateAllCode = evaluateAllCode;
+        this(valueFactory,
+             invocationUnit,
+             evaluateAllCode,
+             evaluateAllCode ?
+                 new BasicBranchUnit() :
+                 new TracedBranchUnit(),
+             new BranchTargetFinder(),
+             null);
+    }
+
 
-        this.branchUnit = evaluateAllCode ?
-            new BasicBranchUnit() :
-            new TracedBranchUnit();
+    /**
+     * Creates a new PartialEvaluator, based on an existing one.
+     * @param partialEvaluator the subroutine calling partial evaluator.
+     */
+    private PartialEvaluator(PartialEvaluator partialEvaluator)
+    {
+        this(partialEvaluator.valueFactory,
+             partialEvaluator.invocationUnit,
+             partialEvaluator.evaluateAllCode,
+             partialEvaluator.branchUnit,
+             partialEvaluator.branchTargetFinder,
+             partialEvaluator.instructionBlockStack);
+    }
+
+
+    /**
+     * Creates a new PartialEvaluator.
+     * @param valueFactory            the value factory that will create all
+     *                                values during evaluation.
+     * @param invocationUnit          the invocation unit that will handle all
+     *                                communication with other fields and methods.
+     * @param evaluateAllCode         a flag that specifies whether all branch
+     *                                targets and exception handlers should be
+     *                                evaluated, even if they are unreachable.
+     * @param branchUnit              the branch unit that will handle all
+     *                                branches.
+     * @param branchTargetFinder      the utility class that will find all
+     *                                branches.
+     */
+    private PartialEvaluator(ValueFactory       valueFactory,
+                             InvocationUnit     invocationUnit,
+                             boolean            evaluateAllCode,
+                             BasicBranchUnit    branchUnit,
+                             BranchTargetFinder branchTargetFinder,
+                             java.util.Stack    callingInstructionBlockStack)
+    {
+        this.valueFactory       = valueFactory;
+        this.invocationUnit     = invocationUnit;
+        this.evaluateAllCode    = evaluateAllCode;
+        this.branchUnit         = branchUnit;
+        this.branchTargetFinder = branchTargetFinder;
+        this.callingInstructionBlockStack = callingInstructionBlockStack == null ?
+            this.instructionBlockStack :
+            callingInstructionBlockStack;
     }
 
 
@@ -166,24 +218,13 @@ implements   AttributeVisitor,
         codeAttribute.accept(clazz, method, branchTargetFinder);
 
         // Start executing the first instruction block.
-        evaluateInstructionBlock(clazz,
-                                 method,
-                                 codeAttribute,
-                                 variables,
-                                 stack,
-                                 0);
-
-        // Evaluate the exception catch blocks, until their entry variables
-        // have stabilized.
-        do
-        {
-            // Reset the flag to stop evaluating.
-            evaluateExceptions = false;
-
-            // Evaluate all relevant exception catch blocks once.
-            codeAttribute.exceptionsAccept(clazz, method, this);
-        }
-        while (evaluateExceptions);
+        evaluateInstructionBlockAndExceptionHandlers(clazz,
+                                                     method,
+                                                     codeAttribute,
+                                                     variables,
+                                                     stack,
+                                                     0,
+                                                     codeAttribute.u4codeLength);
 
         if (DEBUG_RESULTS)
         {
@@ -194,7 +235,7 @@ implements   AttributeVisitor,
             {
                 if (isBranchOrExceptionTarget(offset))
                 {
-                    System.out.println("Branch target:");
+                    System.out.println("Branch target from ["+branchOriginValues[offset]+"]:");
                     if (isTraced(offset))
                     {
                         System.out.println("  Vars:  "+variablesBefore[offset]);
@@ -265,6 +306,15 @@ implements   AttributeVisitor,
 
 
     /**
+     * Returns whether there is an instruction at the given offset.
+     */
+    public boolean isInstruction(int instructionOffset)
+    {
+        return branchTargetFinder.isInstruction(instructionOffset);
+    }
+
+
+    /**
      * Returns whether the instruction at the given offset is the target of a
      * branch instruction or an exception.
      */
@@ -286,6 +336,16 @@ implements   AttributeVisitor,
 
 
     /**
+     * Returns whether the instruction at the given offset is a subroutine
+     * invocation.
+     */
+    public boolean isSubroutineInvocation(int instructionOffset)
+    {
+        return branchTargetFinder.isSubroutineInvocation(instructionOffset);
+    }
+
+
+    /**
      * Returns whether the instruction at the given offset is part of a
      * subroutine.
      */
@@ -429,102 +489,63 @@ implements   AttributeVisitor,
     }
 
 
-    // Implementations for ExceptionInfoVisitor.
+    // Utility methods to evaluate instruction blocks.
 
-    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    /**
+     * Pushes block of instructions to be executed in the calling partial
+     * evaluator.
+     */
+    private void pushCallingInstructionBlock(TracedVariables variables,
+                                             TracedStack     stack,
+                                             int             startOffset)
     {
-        int startPC = exceptionInfo.u2startPC;
-        int endPC   = exceptionInfo.u2endPC;
-
-        // Do we have to evaluate this exception catch block?
-        if (isTraced(startPC, endPC))
-        {
-            int handlerPC = exceptionInfo.u2handlerPC;
-            int catchType = exceptionInfo.u2catchType;
-
-            if (DEBUG) System.out.println("Partial evaluation of exception ["+startPC +","+endPC +"] -> ["+handlerPC+"]:");
-
-            // Reuse the existing variables and stack objects, ensuring the
-            // right size.
-            TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals);
-            TracedStack     stack     = new TracedStack(codeAttribute.u2maxStack);
-
-            // Initialize the trace values.
-            Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY);
-            variables.setProducerValue(storeValue);
-            stack.setProducerValue(storeValue);
-
-            // Initialize the variables by generalizing the variables of the
-            // try block. Make sure to include the results of the last
-            // instruction for preverification.
-            generalizeVariables(startPC,
-                                endPC,
-                                evaluateAllCode,
-                                variables);
-
-            // Initialize the the stack.
-            //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false));
-            String catchClassName = catchType != 0 ?
-                 clazz.getClassName(catchType) :
-                 ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE;
-
-            Clazz catchClass = catchType != 0 ?
-                ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass :
-                null;
-
-            stack.push(valueFactory.createReferenceValue(catchClassName,
-                                                         catchClass,
-                                                         false));
-
-            int evaluationCount = evaluationCounts[handlerPC];
-
-            // Evaluate the instructions, starting at the entry point.
-            evaluateInstructionBlock(clazz,
-                                     method,
-                                     codeAttribute,
-                                     variables,
-                                     stack,
-                                     handlerPC);
-
-            // Remember to evaluate all exception handlers once more.
-            if (!evaluateExceptions)
-            {
-                evaluateExceptions = evaluationCount < evaluationCounts[handlerPC];
-            }
-        }
-        else if (evaluateAllCode)
-        {
-            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +","+endPC +"] -> ["+exceptionInfo.u2handlerPC+"] yet");
-
-            // We don't have any information on the try block yet, but we do
-            // have to evaluate the exception handler.
-            // Remember to evaluate all exception handlers once more.
-            evaluateExceptions = true;
-        }
-        else
-        {
-            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +","+endPC +"] -> ["+exceptionInfo.u2handlerPC+"]");
-        }
+        callingInstructionBlockStack.push(new MyInstructionBlock(variables,
+                                                                 stack,
+                                                                 startOffset));
     }
 
 
-    // Utility methods to evaluate instruction blocks.
-
     /**
-     * Pushes block of instructions to be executed on the given stack.
+     * Pushes block of instructions to be executed in this partial evaluator.
      */
     private void pushInstructionBlock(TracedVariables variables,
                                       TracedStack     stack,
-                                      int             startOffset,
-                                      java.util.Stack instructionBlocKStack)
+                                      int             startOffset)
     {
-        instructionBlocKStack.push(new MyInstructionBlock(variables,
+        instructionBlockStack.push(new MyInstructionBlock(variables,
                                                           stack,
                                                           startOffset));
     }
 
 
     /**
+     * Evaluates the instruction block and the exception handlers covering the
+     * given instruction range in the given code.
+     */
+    private void evaluateInstructionBlockAndExceptionHandlers(Clazz           clazz,
+                                                              Method          method,
+                                                              CodeAttribute   codeAttribute,
+                                                              TracedVariables variables,
+                                                              TracedStack     stack,
+                                                              int             startOffset,
+                                                              int             endOffset)
+    {
+        evaluateInstructionBlock(clazz,
+                                 method,
+                                 codeAttribute,
+                                 variables,
+                                 stack,
+                                 startOffset);
+
+        evaluateExceptionHandlers(clazz,
+                                  method,
+                                  codeAttribute,
+                                  startOffset,
+                                  endOffset);
+    }
+
+
+    /**
      * Evaluates a block of instructions, starting at the given offset and ending
      * at a branch instruction, a return instruction, or a throw instruction.
      */
@@ -535,18 +556,13 @@ implements   AttributeVisitor,
                                           TracedStack     stack,
                                           int             startOffset)
     {
-        // We're now defining out own stack instead of more elegant recursion,
-        // in order to avoid stack overflow errors in the VM.
-        java.util.Stack instructionBlockStack = new java.util.Stack();
-
         // Execute the initial instruction block.
-        evaluateInstructionBlock(clazz,
-                                 method,
-                                 codeAttribute,
-                                 variables,
-                                 stack,
-                                 startOffset,
-                                 instructionBlockStack);
+        evaluateSingleInstructionBlock(clazz,
+                                       method,
+                                       codeAttribute,
+                                       variables,
+                                       stack,
+                                       startOffset);
 
         // Execute all resulting instruction blocks on the execution stack.
         while (!instructionBlockStack.empty())
@@ -556,13 +572,12 @@ implements   AttributeVisitor,
             MyInstructionBlock instructionBlock =
                 (MyInstructionBlock)instructionBlockStack.pop();
 
-            evaluateInstructionBlock(clazz,
-                                     method,
-                                     codeAttribute,
-                                     instructionBlock.variables,
-                                     instructionBlock.stack,
-                                     instructionBlock.startOffset,
-                                     instructionBlockStack);
+            evaluateSingleInstructionBlock(clazz,
+                                           method,
+                                           codeAttribute,
+                                           instructionBlock.variables,
+                                           instructionBlock.stack,
+                                           instructionBlock.startOffset);
         }
     }
 
@@ -573,13 +588,12 @@ implements   AttributeVisitor,
      * Instruction blocks that are to be evaluated as a result are pushed on
      * the given stack.
      */
-    private void evaluateInstructionBlock(Clazz            clazz,
-                                          Method           method,
-                                          CodeAttribute    codeAttribute,
-                                          TracedVariables  variables,
-                                          TracedStack      stack,
-                                          int              startOffset,
-                                          java.util.Stack  instructionBlockStack)
+    private void evaluateSingleInstructionBlock(Clazz            clazz,
+                                                Method           method,
+                                                CodeAttribute    codeAttribute,
+                                                TracedVariables  variables,
+                                                TracedStack      stack,
+                                                int              startOffset)
     {
         byte[] code = codeAttribute.code;
 
@@ -594,7 +608,11 @@ implements   AttributeVisitor,
              System.out.println("Init stack: "+stack);
         }
 
-        Processor processor = new Processor(variables, stack, valueFactory, branchUnit, invocationUnit);
+        Processor processor = new Processor(variables,
+                                            stack,
+                                            valueFactory,
+                                            branchUnit,
+                                            invocationUnit);
 
         int instructionOffset = startOffset;
 
@@ -810,8 +828,7 @@ implements   AttributeVisitor,
 
                         pushInstructionBlock(new TracedVariables(variables),
                                              new TracedStack(stack),
-                                             branchTargets.instructionOffset(index),
-                                             instructionBlockStack);
+                                             branchTargets.instructionOffset(index));
                     }
 
                     break;
@@ -823,48 +840,252 @@ implements   AttributeVisitor,
             // Just continue with the next instruction.
             instructionOffset = branchTargets.instructionOffset(0);
 
-            // Clear the context of a subroutine before entering it, in order
-            // to avoid context conflicts across different invocations.
+            // Is this a subroutine invocation?
             if (instruction.opcode == InstructionConstants.OP_JSR ||
                 instruction.opcode == InstructionConstants.OP_JSR_W)
             {
-                // A subroutine has a single entry point and a single exit point,
-                // so we can easily loop over its instructions.
-                int subroutineEnd = branchTargetFinder.subroutineEnd(instructionOffset);
+                // Evaluate the subroutine, possibly in another partial
+                // evaluator.
+                evaluateSubroutine(clazz,
+                                   method,
+                                   codeAttribute,
+                                   variables,
+                                   stack,
+                                   instructionOffset,
+                                   instructionBlockStack);
+
+                break;
+            }
+            else if (instruction.opcode == InstructionConstants.OP_RET)
+            {
+                // Let the partial evaluator that has called the subroutine
+                // handle the evaluation after the return.
+                pushCallingInstructionBlock(new TracedVariables(variables),
+                                            new TracedStack(stack),
+                                            instructionOffset);
+                break;
+            }
+        }
+
+        if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]");
+    }
+
+
+    /**
+     * Evaluates a subroutine and its exception handlers, starting at the given
+     * offset and ending at a subroutine return instruction.
+     */
+    private void evaluateSubroutine(Clazz           clazz,
+                                    Method          method,
+                                    CodeAttribute   codeAttribute,
+                                    TracedVariables variables,
+                                    TracedStack     stack,
+                                    int             subroutineStart,
+                                    java.util.Stack instructionBlockStack)
+    {
+        int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart);
+
+        if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd);
+
+        PartialEvaluator subroutinePartialEvaluator = this;
+
+        // Create a temporary partial evaluator if necessary.
+        if (evaluationCounts[subroutineStart] > 0)
+        {
+            if (DEBUG) System.out.println("Creating new partial evaluator for subroutine");
+
+            subroutinePartialEvaluator = new PartialEvaluator(this);
+
+            subroutinePartialEvaluator.initializeVariables(clazz,
+                                                           method,
+                                                           codeAttribute,
+                                                           variables,
+                                                           stack);
+        }
+
+        // Evaluate the subroutine.
+        subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz,
+                                                                                method,
+                                                                                codeAttribute,
+                                                                                variables,
+                                                                                stack,
+                                                                                subroutineStart,
+                                                                                subroutineEnd);
+
+        // Merge back the temporary partial evaluator if necessary.
+        if (subroutinePartialEvaluator != this)
+        {
+            generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength);
+        }
+
+        if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd);
+    }
+
+
+    /**
+     * Generalizes the results of this partial evaluator with those of another
+     * given partial evaluator, over a given range of instructions.
+     */
+    private void generalize(PartialEvaluator other,
+                            int              codeStart,
+                            int              codeEnd)
+    {
+        if (DEBUG) System.out.println("Generalizing with temporary partial evaluation");
+
+        for (int offset = codeStart; offset < codeEnd; offset++)
+        {
+            if (other.branchOriginValues[offset] != null)
+            {
+                branchOriginValues[offset] = branchOriginValues[offset] == null ?
+                    other.branchOriginValues[offset] :
+                    branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue();
+            }
 
-                if (DEBUG) System.out.println("Clearing context of subroutine from "+instructionOffset+" to "+subroutineEnd);
+            if (other.isTraced(offset))
+            {
+                if (other.branchTargetValues[offset] != null)
+                {
+                    branchTargetValues[offset] = branchTargetValues[offset] == null ?
+                        other.branchTargetValues[offset] :
+                        branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue();
+                }
 
-                for (int offset = instructionOffset; offset < subroutineEnd; offset++)
+                if (evaluationCounts[offset] == 0)
                 {
-                    if (branchTargetFinder.isInstruction(offset))
-                    {
-                        evaluationCounts[offset] = 0;
-                    }
+                    variablesBefore[offset]      = other.variablesBefore[offset];
+                    stacksBefore[offset]         = other.stacksBefore[offset];
+                    variablesAfter[offset]       = other.variablesAfter[offset];
+                    stacksAfter[offset]          = other.stacksAfter[offset];
+                    generalizedContexts[offset]  = other.generalizedContexts[offset];
+                    evaluationCounts[offset]     = other.evaluationCounts[offset];
+                    initializedVariables[offset] = other.initializedVariables[offset];
+                }
+                else
+                {
+                    variablesBefore[offset].generalize(other.variablesBefore[offset], false);
+                    stacksBefore[offset]   .generalize(other.stacksBefore[offset]);
+                    variablesAfter[offset] .generalize(other.variablesAfter[offset], false);
+                    stacksAfter[offset]    .generalize(other.stacksAfter[offset]);
+                    //generalizedContexts[offset]
+                    evaluationCounts[offset] += other.evaluationCounts[offset];
+                    //initializedVariables[offset]
                 }
+            }
+        }
+    }
 
-                evaluateInstructionBlock(clazz,
-                                         method,
-                                         codeAttribute,
-                                         new TracedVariables(variables),
-                                         new TracedStack(stack),
-                                         instructionOffset);
 
-                if (DEBUG) System.out.println("Evaluating exceptions of subroutine from "+instructionOffset+" to "+subroutineEnd);
+    /**
+     * Evaluates the exception handlers covering and targeting the given
+     * instruction range in the given code.
+     */
+    private void evaluateExceptionHandlers(Clazz         clazz,
+                                           Method        method,
+                                           CodeAttribute codeAttribute,
+                                           int           startOffset,
+                                           int           endOffset)
+    {
+        if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:");
 
-                // Evaluate all relevant exception catch blocks once.
-                codeAttribute.exceptionsAccept(clazz,
-                                               method,
-                                               instructionOffset,
-                                               subroutineEnd,
-                                               this);
+        ExceptionHandlerFilter exceptionEvaluator =
+            new ExceptionHandlerFilter(startOffset,
+                                       endOffset,
+                                       this);
 
-                if (DEBUG) System.out.println("Ending subroutine from "+instructionOffset+" to "+subroutineEnd);
+        // Evaluate the exception catch blocks, until their entry variables
+        // have stabilized.
+        do
+        {
+            // Reset the flag to stop evaluating.
+            evaluateExceptions = false;
 
-                break;
-            }
+            // Evaluate all relevant exception catch blocks once.
+            codeAttribute.exceptionsAccept(clazz,
+                                           method,
+                                           startOffset,
+                                           endOffset,
+                                           exceptionEvaluator);
         }
+        while (evaluateExceptions);
+    }
 
-        if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]");
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        int startPC = exceptionInfo.u2startPC;
+        int endPC   = exceptionInfo.u2endPC;
+
+        // Do we have to evaluate this exception catch block?
+        if (isTraced(startPC, endPC))
+        {
+            int handlerPC = exceptionInfo.u2handlerPC;
+            int catchType = exceptionInfo.u2catchType;
+
+            if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:");
+
+            // Reuse the existing variables and stack objects, ensuring the
+            // right size.
+            TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals);
+            TracedStack     stack     = new TracedStack(codeAttribute.u2maxStack);
+
+            // Initialize the trace values.
+            Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY);
+            variables.setProducerValue(storeValue);
+            stack.setProducerValue(storeValue);
+
+            // Initialize the variables by generalizing the variables of the
+            // try block. Make sure to include the results of the last
+            // instruction for preverification.
+            generalizeVariables(startPC,
+                                endPC,
+                                evaluateAllCode,
+                                variables);
+
+            // Initialize the the stack.
+            //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false));
+            String catchClassName = catchType != 0 ?
+                 clazz.getClassName(catchType) :
+                 ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE;
+
+            Clazz catchClass = catchType != 0 ?
+                ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass :
+                null;
+
+            stack.push(valueFactory.createReferenceValue(catchClassName,
+                                                         catchClass,
+                                                         false));
+
+            int evaluationCount = evaluationCounts[handlerPC];
+
+            // Evaluate the instructions, starting at the entry point.
+            evaluateInstructionBlock(clazz,
+                                     method,
+                                     codeAttribute,
+                                     variables,
+                                     stack,
+                                     handlerPC);
+
+            // Remember to evaluate all exception handlers once more.
+            if (!evaluateExceptions)
+            {
+                evaluateExceptions = evaluationCount < evaluationCounts[handlerPC];
+            }
+        }
+//        else if (evaluateAllCode)
+//        {
+//            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet");
+//
+//            // We don't have any information on the try block yet, but we do
+//            // have to evaluate the exception handler.
+//            // Remember to evaluate all exception handlers once more.
+//            evaluateExceptions = true;
+//        }
+        else
+        {
+            if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]");
+        }
     }
 
 
diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
index 1511a60..bcbb69f 100644
--- a/src/proguard/optimize/evaluation/StoringInvocationUnit.java
+++ b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -36,12 +36,34 @@ import proguard.optimize.info.*;
 public class StoringInvocationUnit
 extends      BasicInvocationUnit
 {
+    private boolean storeFieldValues;
+    private boolean storeMethodParameterValues;
+    private boolean storeMethodReturnValues;
+
+
     /**
      * Creates a new StoringInvocationUnit with the given value factory.
      */
     public StoringInvocationUnit(ValueFactory valueFactory)
     {
+        this(valueFactory, true, true, true);
+    }
+
+
+    /**
+     * Creates a new StoringInvocationUnit with the given value factory, for
+     * storing the specified values.
+     */
+    public StoringInvocationUnit(ValueFactory valueFactory,
+                                 boolean      storeFieldValues,
+                                 boolean      storeMethodParameterValues,
+                                 boolean      storeMethodReturnValues)
+    {
         super(valueFactory);
+
+        this.storeFieldValues           = storeFieldValues;
+        this.storeMethodParameterValues = storeMethodParameterValues;
+        this.storeMethodReturnValues    = storeMethodReturnValues;
     }
 
 
@@ -51,10 +73,13 @@ extends      BasicInvocationUnit
                                       RefConstant    refConstant,
                                       ReferenceValue value)
     {
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (storeFieldValues)
         {
-            generalizeFieldClassValue((Field)referencedMember, value);
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
+            {
+                generalizeFieldClassValue((Field)referencedMember, value);
+            }
         }
     }
 
@@ -63,10 +88,13 @@ extends      BasicInvocationUnit
                                  RefConstant refConstant,
                                  Value       value)
     {
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (storeFieldValues)
         {
-            generalizeFieldValue((Field)referencedMember, value);
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
+            {
+                generalizeFieldValue((Field)referencedMember, value);
+            }
         }
     }
 
@@ -76,12 +104,15 @@ extends      BasicInvocationUnit
                                            int         parameterIndex,
                                            Value       value)
     {
-        Member referencedMember = refConstant.referencedMember;
-        if (referencedMember != null)
+        if (storeMethodParameterValues)
         {
-            generalizeMethodParameterValue((Method)referencedMember,
-                                           parameterIndex,
-                                           value);
+            Member referencedMember = refConstant.referencedMember;
+            if (referencedMember != null)
+            {
+                generalizeMethodParameterValue((Method)referencedMember,
+                                               parameterIndex,
+                                               value);
+            }
         }
     }
 
@@ -90,9 +121,12 @@ extends      BasicInvocationUnit
                                         Method method,
                                         Value  value)
     {
-        generalizeMethodReturnValue(method, value);
+        if (storeMethodReturnValues)
+        {
+            generalizeMethodReturnValue(method, value);
+        }
     }
-
+    
 
     // Small utility methods.
 
diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java
index 422ce11..fa5bb79 100644
--- a/src/proguard/optimize/evaluation/TracedBranchUnit.java
+++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java
index c500bdc..b3ae81c 100644
--- a/src/proguard/optimize/evaluation/VariableOptimizer.java
+++ b/src/proguard/optimize/evaluation/VariableOptimizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,6 +21,7 @@
 package proguard.optimize.evaluation;
 
 import proguard.classfile.*;
+import proguard.classfile.visitor.MemberVisitor;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.visitor.AttributeVisitor;
 import proguard.classfile.editor.VariableRemapper;
@@ -45,7 +46,8 @@ implements   AttributeVisitor
     private static final int MAX_VARIABLES_SIZE = 64;
 
 
-    private final boolean reuseThis;
+    private final boolean       reuseThis;
+    private final MemberVisitor extraVariableMemberVisitor;
 
     private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
     private final VariableRemapper variableRemapper = new VariableRemapper();
@@ -56,10 +58,29 @@ implements   AttributeVisitor
     /**
      * Creates a new VariableOptimizer.
      * @param reuseThis specifies whether the 'this' variable can be reused.
+     *                  Many JVMs for JME and IBM's JVMs for JSE can't handle
+     *                  such reuse.
      */
     public VariableOptimizer(boolean reuseThis)
     {
-        this.reuseThis = reuseThis;
+        this(reuseThis, null);
+    }
+
+
+    /**
+     * Creates a new VariableOptimizer with an extra visitor.
+     * @param reuseThis                  specifies whether the 'this' variable
+     *                                   can be reused. Many JVMs for JME and
+     *                                   IBM's JVMs for JSE can't handle such
+     *                                   reuse.
+     * @param extraVariableMemberVisitor an optional extra visitor for all
+     *                                   removed variables.
+     */
+    public VariableOptimizer(boolean       reuseThis,
+                             MemberVisitor extraVariableMemberVisitor)
+    {
+        this.reuseThis                  = reuseThis;
+        this.extraVariableMemberVisitor = extraVariableMemberVisitor;
     }
 
 
@@ -140,6 +161,12 @@ implements   AttributeVisitor
 
             variableRemapper.setVariableMap(variableMap);
             variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
+
+            // Visit the method, if required.
+            if (extraVariableMemberVisitor != null)
+            {
+                method.accept(clazz, extraVariableMemberVisitor);
+            }
         }
     }
 
diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/src/proguard/optimize/info/AccessMethodMarker.java
index 16319a0..6965cec 100644
--- a/src/proguard/optimize/info/AccessMethodMarker.java
+++ b/src/proguard/optimize/info/AccessMethodMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/BackwardBranchMarker.java b/src/proguard/optimize/info/BackwardBranchMarker.java
index d988f8d..9e09b0f 100644
--- a/src/proguard/optimize/info/BackwardBranchMarker.java
+++ b/src/proguard/optimize/info/BackwardBranchMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/CatchExceptionMarker.java b/src/proguard/optimize/info/CatchExceptionMarker.java
index 8830722..3f2a06f 100644
--- a/src/proguard/optimize/info/CatchExceptionMarker.java
+++ b/src/proguard/optimize/info/CatchExceptionMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/DotClassFilter.java b/src/proguard/optimize/info/CaughtClassFilter.java
similarity index 81%
copy from src/proguard/optimize/info/DotClassFilter.java
copy to src/proguard/optimize/info/CaughtClassFilter.java
index 08aa6e5..5e17763 100644
--- a/src/proguard/optimize/info/DotClassFilter.java
+++ b/src/proguard/optimize/info/CaughtClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -25,18 +25,18 @@ import proguard.classfile.visitor.ClassVisitor;
 
 /**
  * This ClassVisitor delegates all its method calls to another ClassVisitor,
- * but only for Clazz objects that are used in a .class construct.
+ * but only for Clazz objects that are caught as exceptions.
  *
- * @see DotClassMarker
+ * @see CaughtClassMarker
  * @author Eric Lafortune
  */
-public class DotClassFilter
+public class CaughtClassFilter
 implements   ClassVisitor
 {
     private final ClassVisitor classVisitor;
 
 
-    public DotClassFilter(ClassVisitor classVisitor)
+    public CaughtClassFilter(ClassVisitor classVisitor)
     {
         this.classVisitor = classVisitor;
     }
@@ -46,7 +46,7 @@ implements   ClassVisitor
 
     public void visitProgramClass(ProgramClass programClass)
     {
-        if (DotClassMarker.isDotClassed(programClass))
+        if (CaughtClassMarker.isCaught(programClass))
         {
             classVisitor.visitProgramClass(programClass);
         }
@@ -55,7 +55,7 @@ implements   ClassVisitor
 
     public void visitLibraryClass(LibraryClass libraryClass)
     {
-        if (DotClassMarker.isDotClassed(libraryClass))
+        if (CaughtClassMarker.isCaught(libraryClass))
         {
             classVisitor.visitLibraryClass(libraryClass);
         }
diff --git a/src/proguard/optimize/info/DotClassFilter.java b/src/proguard/optimize/info/CaughtClassMarker.java
similarity index 59%
copy from src/proguard/optimize/info/DotClassFilter.java
copy to src/proguard/optimize/info/CaughtClassMarker.java
index 08aa6e5..0cc350e 100644
--- a/src/proguard/optimize/info/DotClassFilter.java
+++ b/src/proguard/optimize/info/CaughtClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,43 +21,43 @@
 package proguard.optimize.info;
 
 import proguard.classfile.*;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassVisitor delegates all its method calls to another ClassVisitor,
- * but only for Clazz objects that are used in a .class construct.
+ * This InstructionVisitor marks all classes that are used in an 'instanceof'
+ * test by any of the instructions that it visits.
  *
- * @see DotClassMarker
  * @author Eric Lafortune
  */
-public class DotClassFilter
+public class CaughtClassMarker
 implements   ClassVisitor
 {
-    private final ClassVisitor classVisitor;
+    // Implementations for ClassVisitor.
 
+    public void visitLibraryClass(LibraryClass libraryClass) {}
 
-    public DotClassFilter(ClassVisitor classVisitor)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        this.classVisitor = classVisitor;
+        setCaught(programClass);
     }
 
 
-    // Implementations for ClassVisitor.
+    // Small utility methods.
 
-    public void visitProgramClass(ProgramClass programClass)
+    private static void setCaught(Clazz clazz)
     {
-        if (DotClassMarker.isDotClassed(programClass))
+        ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
+        if (info != null)
         {
-            classVisitor.visitProgramClass(programClass);
+            info.setCaught();
         }
     }
 
 
-    public void visitLibraryClass(LibraryClass libraryClass)
+    public static boolean isCaught(Clazz clazz)
     {
-        if (DotClassMarker.isDotClassed(libraryClass))
-        {
-            classVisitor.visitLibraryClass(libraryClass);
-        }
+        ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
+        return info == null || info.isCaught();
     }
 }
\ No newline at end of file
diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/src/proguard/optimize/info/ClassOptimizationInfo.java
index e848a6e..99b6e7b 100644
--- a/src/proguard/optimize/info/ClassOptimizationInfo.java
+++ b/src/proguard/optimize/info/ClassOptimizationInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -33,6 +33,7 @@ public class ClassOptimizationInfo
     private boolean isInstantiated                = false;
     private boolean isInstanceofed                = false;
     private boolean isDotClassed                  = false;
+    private boolean isCaught                      = false;
     private boolean containsPackageVisibleMembers = false;
     private boolean invokesPackageVisibleMembers  = false;
     private Clazz   targetClass;
@@ -74,6 +75,18 @@ public class ClassOptimizationInfo
     }
 
 
+    public void setCaught()
+    {
+        isCaught = true;
+    }
+
+
+    public boolean isCaught()
+    {
+        return isCaught;
+    }
+
+
     public void setContainsPackageVisibleMembers()
     {
         containsPackageVisibleMembers = true;
@@ -110,6 +123,17 @@ public class ClassOptimizationInfo
     }
 
 
+    public void merge(ClassOptimizationInfo other)
+    {
+        this.isInstantiated                |= other.isInstantiated;
+        this.isInstanceofed                |= other.isInstanceofed;
+        this.isDotClassed                  |= other.isDotClassed;
+        this.isCaught                      |= other.isCaught;
+        this.containsPackageVisibleMembers |= other.containsPackageVisibleMembers;
+        this.invokesPackageVisibleMembers  |= other.invokesPackageVisibleMembers;
+    }
+
+
     public static void setClassOptimizationInfo(Clazz clazz)
     {
         clazz.setVisitorInfo(new ClassOptimizationInfo());
diff --git a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java
index 5f5cc5b..9cb167c 100644
--- a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java
+++ b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/DotClassFilter.java b/src/proguard/optimize/info/DotClassFilter.java
index 08aa6e5..8cbe7f0 100644
--- a/src/proguard/optimize/info/DotClassFilter.java
+++ b/src/proguard/optimize/info/DotClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/DotClassMarker.java b/src/proguard/optimize/info/DotClassMarker.java
index a5c9395..b5f12a7 100644
--- a/src/proguard/optimize/info/DotClassMarker.java
+++ b/src/proguard/optimize/info/DotClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -78,7 +78,7 @@ implements   InstructionVisitor,
 
     // Small utility methods.
 
-    public static void setDotClassed(Clazz clazz)
+    private static void setDotClassed(Clazz clazz)
     {
         ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
         if (info != null)
diff --git a/src/proguard/optimize/info/ExceptionInstructionChecker.java b/src/proguard/optimize/info/ExceptionInstructionChecker.java
index 1e7e500..2792d90 100644
--- a/src/proguard/optimize/info/ExceptionInstructionChecker.java
+++ b/src/proguard/optimize/info/ExceptionInstructionChecker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/FieldOptimizationInfo.java b/src/proguard/optimize/info/FieldOptimizationInfo.java
index 706ca0a..7a2d068 100644
--- a/src/proguard/optimize/info/FieldOptimizationInfo.java
+++ b/src/proguard/optimize/info/FieldOptimizationInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/InstanceofClassFilter.java b/src/proguard/optimize/info/InstanceofClassFilter.java
index 68d114c..35e1d77 100644
--- a/src/proguard/optimize/info/InstanceofClassFilter.java
+++ b/src/proguard/optimize/info/InstanceofClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/InstanceofClassMarker.java b/src/proguard/optimize/info/InstanceofClassMarker.java
index 75be332..c60e1f8 100644
--- a/src/proguard/optimize/info/InstanceofClassMarker.java
+++ b/src/proguard/optimize/info/InstanceofClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -75,7 +75,7 @@ implements   InstructionVisitor,
 
     // Small utility methods.
 
-    public static void setInstanceofed(Clazz clazz)
+    private static void setInstanceofed(Clazz clazz)
     {
         ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
         if (info != null)
diff --git a/src/proguard/optimize/info/InstantiationClassFilter.java b/src/proguard/optimize/info/InstantiationClassFilter.java
index e2dbb53..a24e617 100644
--- a/src/proguard/optimize/info/InstantiationClassFilter.java
+++ b/src/proguard/optimize/info/InstantiationClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/InstantiationClassMarker.java b/src/proguard/optimize/info/InstantiationClassMarker.java
index caf89ad..124c23b 100644
--- a/src/proguard/optimize/info/InstantiationClassMarker.java
+++ b/src/proguard/optimize/info/InstantiationClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -75,7 +75,7 @@ implements   InstructionVisitor,
 
     // Small utility methods.
 
-    public static void setInstantiated(Clazz clazz)
+    private static void setInstantiated(Clazz clazz)
     {
         ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
         if (info != null)
diff --git a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java
index 5c1a1fe..a170a8e 100644
--- a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java
+++ b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/src/proguard/optimize/info/MethodInvocationMarker.java
index 40a2abe..2528c94 100644
--- a/src/proguard/optimize/info/MethodInvocationMarker.java
+++ b/src/proguard/optimize/info/MethodInvocationMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/MethodOptimizationInfo.java b/src/proguard/optimize/info/MethodOptimizationInfo.java
index e48f5aa..d3b1bde 100644
--- a/src/proguard/optimize/info/MethodOptimizationInfo.java
+++ b/src/proguard/optimize/info/MethodOptimizationInfo.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -256,6 +256,35 @@ public class MethodOptimizationInfo
     }
 
 
+    public void merge(MethodOptimizationInfo other)
+    {
+        if (other != null)
+        {
+            this.hasNoSideEffects      &= other.hasNoSideEffects;
+            this.hasSideEffects        |= other.hasSideEffects;
+            //this.canBeMadePrivate    &= other.canBeMadePrivate;
+            this.catchesExceptions     |= other.catchesExceptions;
+            this.branchesBackward      |= other.branchesBackward;
+            this.invokesSuperMethods   |= other.invokesSuperMethods;
+            this.accessesPrivateCode   |= other.accessesPrivateCode;
+            this.accessesPackageCode   |= other.accessesPackageCode;
+            this.accessesProtectedCode |= other.accessesProtectedCode;
+        }
+        else
+        {
+            this.hasNoSideEffects      = false;
+            this.hasSideEffects        = true;
+            //this.canBeMadePrivate    = false;
+            this.catchesExceptions     = true;
+            this.branchesBackward      = true;
+            this.invokesSuperMethods   = true;
+            this.accessesPrivateCode   = true;
+            this.accessesPackageCode   = true;
+            this.accessesProtectedCode = true;
+        }
+    }
+
+
     public static void setMethodOptimizationInfo(Clazz clazz, Method method)
     {
         MethodLinker.lastMember(method).setVisitorInfo(new MethodOptimizationInfo(clazz, method));
diff --git a/src/proguard/optimize/info/NoSideEffectMethodMarker.java b/src/proguard/optimize/info/NoSideEffectMethodMarker.java
index 4840354..5c78408 100644
--- a/src/proguard/optimize/info/NoSideEffectMethodMarker.java
+++ b/src/proguard/optimize/info/NoSideEffectMethodMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/NonPrivateMemberMarker.java b/src/proguard/optimize/info/NonPrivateMemberMarker.java
index 57dcf72..d451643 100644
--- a/src/proguard/optimize/info/NonPrivateMemberMarker.java
+++ b/src/proguard/optimize/info/NonPrivateMemberMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java
index 9753812..d40bc6b 100644
--- a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java
+++ b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -49,7 +49,7 @@ implements   MemberVisitor
 
     // Small utility methods.
 
-    public static void setPackageVisibleMembers(Clazz clazz)
+    private static void setPackageVisibleMembers(Clazz clazz)
     {
         ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
         if (info != null)
diff --git a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java
index 4dde955..9ec8ec6 100644
--- a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java
+++ b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -63,7 +63,7 @@ implements   ConstantVisitor
 
     // Small utility methods.
 
-    public static void setInvokesPackageVisibleMembers(Clazz clazz)
+    private static void setInvokesPackageVisibleMembers(Clazz clazz)
     {
         ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz);
         if (info != null)
diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java
index 828f190..15ce88a 100644
--- a/src/proguard/optimize/info/ParameterUsageMarker.java
+++ b/src/proguard/optimize/info/ParameterUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,8 +21,14 @@
 package proguard.optimize.info;
 
 import proguard.classfile.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.attribute.*;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.evaluation.PartialEvaluator;
+import proguard.evaluation.value.*;
 
 /**
  * This MemberVisitor counts the parameters and marks the used parameters
@@ -33,12 +39,40 @@ import proguard.classfile.visitor.MemberVisitor;
  */
 public class ParameterUsageMarker
 extends      SimplifiedVisitor
-implements   MemberVisitor
+implements   MemberVisitor,
+             AttributeVisitor,
+             InstructionVisitor
 {
     private static final boolean DEBUG = false;
 
 
-    private final VariableUsageMarker variableUsageMarker = new VariableUsageMarker();
+    private final boolean          markThisParameter;
+    private final boolean          markAllParameters;
+    private final PartialEvaluator partialEvaluator = new PartialEvaluator();
+
+
+    /**
+     * Creates a new ParameterUsageMarker.
+     */
+    public ParameterUsageMarker()
+    {
+        this(false, false);
+    }
+
+
+    /**
+     * Creates a new ParameterUsageMarker that optionally marks all parameters.
+     * @param markThisParameter specifies whether all 'this' parameters should
+     *                          be marked as being used.
+     * @param markAllParameters specifies whether all other parameters should
+     *                          be marked as being used.
+     */
+    public ParameterUsageMarker(boolean markThisParameter,
+                                boolean markAllParameters)
+    {
+        this.markThisParameter = markThisParameter;
+        this.markAllParameters = markAllParameters;
+    }
 
 
     // Implementations for MemberVisitor.
@@ -51,8 +85,26 @@ implements   MemberVisitor
 
         if (parameterSize > 0)
         {
-            // Is it a native method?
             int accessFlags = programMethod.getAccessFlags();
+
+            // Must we mark the 'this' parameter?
+            if (markThisParameter &&
+                (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0)
+            {
+                // Mark the 'this' parameter.
+                markParameterUsed(programMethod, 0);
+            }
+
+            // Must we mark all other parameters?
+            if (markAllParameters)
+            {
+                // Mark all parameters, without the 'this' parameter.
+                markUsedParameters(programMethod,
+                                   (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
+                                       -1L : -2L);
+            }
+
+            // Is it a native method?
             if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
             {
                 // Mark all parameters.
@@ -80,45 +132,8 @@ implements   MemberVisitor
                     markParameterUsed(programMethod, 0);
                 }
 
-                // Figure out the local variables that are used by the code.
-                programMethod.attributesAccept(programClass, variableUsageMarker);
-
                 // Mark the parameters that are used by the code.
-                for (int index = 0; index < parameterSize; index++)
-                {
-                    if (variableUsageMarker.isVariableUsed(index))
-                    {
-                        markParameterUsed(programMethod, index);
-                    }
-                }
-
-                // Mark the category 2 parameters that are half-used.
-                InternalTypeEnumeration internalTypeEnumeration =
-                    new InternalTypeEnumeration(programMethod.getDescriptor(programClass));
-
-                // All parameters of non-static methods are shifted by one in
-                // the local variable frame.
-                int index =
-                    (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
-                        0 : 1;
-
-                while (internalTypeEnumeration.hasMoreTypes())
-                {
-                    String type = internalTypeEnumeration.nextType();
-                    if (ClassUtil.isInternalCategory2Type(type))
-                    {
-                        if (variableUsageMarker.isVariableUsed(index) ||
-                            variableUsageMarker.isVariableUsed(index+1))
-                        {
-                            markParameterUsed(programMethod, index);
-                            markParameterUsed(programMethod, index+1);
-                        }
-
-                        index++;
-                    }
-
-                    index++;
-                }
+                programMethod.attributesAccept(programClass, this);
             }
 
             if (DEBUG)
@@ -145,10 +160,54 @@ implements   MemberVisitor
         {
             // All implementations must keep all parameters of this method,
             // including the 'this' parameter.
-            long usedParameters = -1L;
+            markUsedParameters(libraryMethod, -1L);
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Evaluate the code.
+        partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Mark the parameters that are used by the code.
+        codeAttribute.instructionsAccept(clazz, method, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        if (partialEvaluator.isTraced(offset) &&
+            variableInstruction.isLoad())
+        {
+            int parameterIndex = variableInstruction.variableIndex;
+            if (parameterIndex < codeAttribute.u2maxLocals)
+            {
+                Value producer =
+                    partialEvaluator.getVariablesBefore(offset).getProducerValue(parameterIndex);
+                if (producer != null &&
+                    producer.instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY))
+                {
+                    // Mark the variable.
+                    markParameterUsed(method, parameterIndex);
 
-            // Mark it.
-            markUsedParameters(libraryMethod, usedParameters);
+                    // Account for Category 2 instructions, which take up two entries.
+                    if (variableInstruction.isCategory2())
+                    {
+                        markParameterUsed(method, parameterIndex + 1);
+                    }
+                }
+            }
         }
     }
 
@@ -223,25 +282,4 @@ implements   MemberVisitor
         MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
         return info != null ? info.getUsedParameters() : -1L;
     }
-
-
-    /**
-     * Returns a bit mask of 1-bits of the given size.
-     */
-    private int parameterMask(int parameterSize)
-    {
-        return (1 << parameterSize) - 1;
-    }
-
-
-    /**
-     * Returns the parameter size of the given method, including the 'this'
-     * parameter, if any.
-     */
-    private int parameterSize(Clazz clazz, Method method)
-    {
-
-        return ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
-                                                     method.getAccessFlags());
-    }
 }
diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/src/proguard/optimize/info/ReadWriteFieldMarker.java
index f082cb1..57d8561 100644
--- a/src/proguard/optimize/info/ReadWriteFieldMarker.java
+++ b/src/proguard/optimize/info/ReadWriteFieldMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -41,8 +41,8 @@ implements   InstructionVisitor,
              MemberVisitor
 {
     // Parameters for the visitor methods.
-    private boolean reading;
-    private boolean writing;
+    private boolean reading = true;
+    private boolean writing = true;
 
 
     // Implementations for InstructionVisitor.
diff --git a/src/proguard/optimize/info/SideEffectInstructionChecker.java b/src/proguard/optimize/info/SideEffectInstructionChecker.java
index da69924..8be9dc1 100644
--- a/src/proguard/optimize/info/SideEffectInstructionChecker.java
+++ b/src/proguard/optimize/info/SideEffectInstructionChecker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/SideEffectMethodMarker.java b/src/proguard/optimize/info/SideEffectMethodMarker.java
index 221c9df..25fda72 100644
--- a/src/proguard/optimize/info/SideEffectMethodMarker.java
+++ b/src/proguard/optimize/info/SideEffectMethodMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/src/proguard/optimize/info/SuperInvocationMarker.java
index 2125f68..6f3d3bd 100644
--- a/src/proguard/optimize/info/SuperInvocationMarker.java
+++ b/src/proguard/optimize/info/SuperInvocationMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/info/VariableUsageMarker.java b/src/proguard/optimize/info/VariableUsageMarker.java
index db0e206..660c4ba 100644
--- a/src/proguard/optimize/info/VariableUsageMarker.java
+++ b/src/proguard/optimize/info/VariableUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java
index 5279b49..8f650bb 100644
--- a/src/proguard/optimize/peephole/BranchTargetFinder.java
+++ b/src/proguard/optimize/peephole/BranchTargetFinder.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/src/proguard/optimize/peephole/ClassFinalizer.java
index 00654ee..b5e54f8 100644
--- a/src/proguard/optimize/peephole/ClassFinalizer.java
+++ b/src/proguard/optimize/peephole/ClassFinalizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -26,20 +26,16 @@ import proguard.classfile.visitor.*;
 import proguard.optimize.KeepMarker;
 
 /**
- * This <code>ClassVisitor</code> and <code>MemberVisitor</code>
- * makes the classes it visits, and their methods, final, if possible.
+ * This <code>ClassVisitor</code> makes the program classes that it visits
+ * final, if possible.
  *
  * @author Eric Lafortune
  */
 public class ClassFinalizer
 extends      SimplifiedVisitor
-implements   ClassVisitor,
-             MemberVisitor
+implements   ClassVisitor
 {
-    private final ClassVisitor  extraClassVisitor;
-    private final MemberVisitor extraMemberVisitor;
-
-    private final MemberFinder memberFinder = new MemberFinder();
+    private final ClassVisitor extraClassVisitor;
 
 
     /**
@@ -47,22 +43,18 @@ implements   ClassVisitor,
      */
     public ClassFinalizer()
     {
-        this(null, null);
+        this(null);
     }
 
 
     /**
      * Creates a new ClassFinalizer.
-     * @param extraClassVisitor  an optional extra visitor for all finalized
-     *                           classes.
-     * @param extraMemberVisitor an optional extra visitor for all finalized
-     *                           methods.
+     * @param extraClassVisitor an optional extra visitor for all finalized
+     *                          classes.
      */
-    public ClassFinalizer(ClassVisitor  extraClassVisitor,
-                          MemberVisitor extraMemberVisitor)
+    public ClassFinalizer(ClassVisitor  extraClassVisitor)
     {
-        this.extraClassVisitor  = extraClassVisitor;
-        this.extraMemberVisitor = extraMemberVisitor;
+        this.extraClassVisitor = extraClassVisitor;
     }
 
 
@@ -88,40 +80,5 @@ implements   ClassVisitor,
                 extraClassVisitor.visitProgramClass(programClass);
             }
         }
-
-        // Check all methods.
-        programClass.methodsAccept(this);
-    }
-
-
-    // Implementations for MemberVisitor.
-
-    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
-    {
-        String name = programMethod.getName(programClass);
-
-        // If the method is not already private/static/final/abstract,
-        // and it is not a constructor,
-        // and its class is final,
-        //     or it is not being kept and it is not overridden,
-        // then make it final.
-        if ((programMethod.u2accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE |
-                                            ClassConstants.INTERNAL_ACC_STATIC  |
-                                            ClassConstants.INTERNAL_ACC_FINAL   |
-                                            ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 &&
-            !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)                      &&
-            ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0 ||
-             (!KeepMarker.isKept(programMethod) &&
-              (programClass.subClasses == null ||
-               !memberFinder.isOverriden(programClass, programMethod)))))
-        {
-            programMethod.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
-
-            // Visit the method, if required.
-            if (extraMemberVisitor != null)
-            {
-                extraMemberVisitor.visitProgramMethod(programClass, programMethod);
-            }
-        }
     }
 }
diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java
index edd95ec..1e1a950 100644
--- a/src/proguard/optimize/peephole/ClassMerger.java
+++ b/src/proguard/optimize/peephole/ClassMerger.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -35,8 +35,11 @@ import java.util.*;
  * This ClassVisitor inlines the classes that it visits in a given target class,
  * whenever possible.
  *
+ * @see RetargetedInnerClassAttributeRemover
  * @see TargetClassChanger
  * @see ClassReferenceFixer
+ * @see MemberReferenceFixer
+ * @see AccessFixer
  * @author Eric Lafortune
  */
 public class ClassMerger
@@ -185,6 +188,10 @@ implements   ClassVisitor,
             // that are tested with 'instanceof'.
             instanceofedSuperClasses(programClass).equals(instanceofedSuperClasses(targetClass)) &&
 
+            // The two classes must have the same superclasses that are caught
+            // as exceptions.
+            caughtSuperClasses(programClass).equals(caughtSuperClasses(targetClass)) &&
+
             // The two classes must not both be part of a .class construct.
             !(DotClassMarker.isDotClassed(programClass) &&
               DotClassMarker.isDotClassed(targetClass)) &&
@@ -258,40 +265,16 @@ implements   ClassVisitor,
             programClass.fieldsAccept(memberAdder);
             programClass.methodsAccept(memberAdder);
 
-//            // Delete any inner classes and enclosing method attributes in
-//            // both classes.
-//            programClass.attributesAccept(
-//                new AttributeNameFilter(new FixedStringMatcher(ClassConstants.ATTR_InnerClasses),
-//                new ReferencedClassVisitor(
-//                new NamedAttributeDeleter())));
-//            AttributesEditor attributesEditor =
-//                new AttributesEditor(programClass, false);
-//            attributesEditor.deleteAttribute(ClassConstants.ATTR_InnerClasses);
-//            attributesEditor.deleteAttribute(ClassConstants.ATTR_EnclosingMethod);
-//
-//            attributesEditor =
-//                new AttributesEditor((ProgramClass)targetClass, false);
-//            attributesEditor.deleteAttribute(ClassConstants.ATTR_InnerClasses);
-//            attributesEditor.deleteAttribute(ClassConstants.ATTR_EnclosingMethod);
-
             // Copy over the other attributes.
             programClass.attributesAccept(
                 new AttributeAdder(targetClass, true));
 
             // Update the optimization information of the target class.
-            if (InstantiationClassMarker.isInstantiated(programClass))
-            {
-                InstantiationClassMarker.setInstantiated(targetClass);
-            }
-
-            if (InstanceofClassMarker.isInstanceofed(programClass))
+            ClassOptimizationInfo info =
+                ClassOptimizationInfo.getClassOptimizationInfo(targetClass);
+            if (info != null)
             {
-                InstanceofClassMarker.setInstanceofed(targetClass);
-            }
-
-            if (DotClassMarker.isDotClassed(programClass))
-            {
-                DotClassMarker.setDotClassed(targetClass);
+                info.merge(ClassOptimizationInfo.getClassOptimizationInfo(programClass));
             }
 
             // Remember to replace the inlined class by the target class.
@@ -381,6 +364,22 @@ implements   ClassVisitor,
 
 
     /**
+     * Returns the set of superclasses that are caught as exceptions.
+     */
+    private Set caughtSuperClasses(Clazz clazz)
+    {
+        Set set = new HashSet();
+
+        // Visit all superclasses, collecting the ones that are caught.
+        clazz.hierarchyAccept(true, true, false, false,
+                              new CaughtClassFilter(
+                              new ClassCollector(set)));
+
+        return set;
+    }
+
+
+    /**
      * Returns whether the given class would introduce any unwanted fields
      * in the target class.
      */
@@ -406,6 +405,7 @@ implements   ClassVisitor,
         MemberCounter counter = new MemberCounter();
 
         // TODO: Currently checking shared methods, not just initializers.
+        // TODO: Allow identical methods.
         // Visit all methods, counting the ones that are also present in the
         // target class.
         clazz.methodsAccept(//new MemberNameFilter(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT),
diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
index 3b367c0..4833275 100644
--- a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
+++ b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -123,9 +123,9 @@ implements   AttributeVisitor,
                     int deleteOffset = offset - delta;
                     if (branchTargetFinder.isInstruction(deleteOffset))
                     {
-                        codeAttributeEditor.replaceInstruction(     deleteOffset, null);
-                        codeAttributeEditor.insertBeforeInstruction(deleteOffset, null);
-                        codeAttributeEditor.insertAfterInstruction( deleteOffset, null);
+                        codeAttributeEditor.replaceInstruction(     deleteOffset, (Instruction)null);
+                        codeAttributeEditor.insertBeforeInstruction(deleteOffset, (Instruction)null);
+                        codeAttributeEditor.insertAfterInstruction( deleteOffset, (Instruction)null);
 
                         codeAttributeEditor.deleteInstruction(deleteOffset);
                     }
diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java
index 4e9f8ed..7d7e66c 100644
--- a/src/proguard/optimize/peephole/GotoGotoReplacer.java
+++ b/src/proguard/optimize/peephole/GotoGotoReplacer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java
index 71d59a7..5c3eb77 100644
--- a/src/proguard/optimize/peephole/GotoReturnReplacer.java
+++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/HorizontalClassMerger.java b/src/proguard/optimize/peephole/HorizontalClassMerger.java
index b1c52cb..a37b9a5 100644
--- a/src/proguard/optimize/peephole/HorizontalClassMerger.java
+++ b/src/proguard/optimize/peephole/HorizontalClassMerger.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
index 70dbf99..b33204b 100644
--- a/src/proguard/optimize/peephole/InstructionSequenceConstants.java
+++ b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -97,7 +97,7 @@ public class InstructionSequenceConstants
     private static final int TYPE_D               = 42;
 
 
-    public static final Constant[] PATTERN_CONSTANTS = new Constant[]
+    public static final Constant[] CONSTANTS = new Constant[]
     {
         new IntegerConstant(32768),
         new IntegerConstant(65536),
@@ -153,8 +153,7 @@ public class InstructionSequenceConstants
     };
 
 
-    public static final Instruction[][][] INSTRUCTION_SEQUENCES =
-        new Instruction[][][]
+    public static final Instruction[][][] VARIABLE = new Instruction[][][]
     {
         {   // nop = nothing
             {
@@ -163,6 +162,46 @@ public class InstructionSequenceConstants
                 // Nothing.
             },
         },
+        {   // iload/pop = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },{
+                // Nothing.
+            },
+        },
+        {   // lload/pop2 = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },{
+                // Nothing.
+            },
+        },
+        {   // fload/pop = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },{
+                // Nothing.
+            },
+        },
+        {   // dload/pop2 = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },{
+                // Nothing.
+            },
+        },
+        {   // aload/pop = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },{
+                // Nothing.
+            },
+        },
         {   // i = i = nothing
             {
                 new VariableInstruction(InstructionConstants.OP_ILOAD, X),
@@ -198,7 +237,7 @@ public class InstructionSequenceConstants
         {   // a = a = nothing
             {
                 new VariableInstruction(InstructionConstants.OP_ALOAD, X),
-                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+                new SimpleInstruction(InstructionConstants.OP_POP),
             },{
                 // Nothing.
             },
@@ -293,6 +332,10 @@ public class InstructionSequenceConstants
                 new VariableInstruction(InstructionConstants.OP_ASTORE, X),
             },
         },
+    };
+
+    public static final Instruction[][][] ARITHMETIC = new Instruction[][][]
+    {
         {   // c + i = i + c
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
@@ -2083,6 +2126,10 @@ public class InstructionSequenceConstants
                 // Nothing.
             },
         },
+    };
+
+    public static final Instruction[][][] FIELD = new Instruction[][][]
+    {
         {   // getfield/putfield = nothing
             {
                 new VariableInstruction(InstructionConstants.OP_ALOAD, X),
@@ -2245,6 +2292,10 @@ public class InstructionSequenceConstants
                 new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
             },
         },
+    };
+
+    public static final Instruction[][][] CAST = new Instruction[][][]
+    {
         {   // (byte)(byte)... = (byte)...
             {
                 new SimpleInstruction(InstructionConstants.OP_I2B),
@@ -2382,6 +2433,10 @@ public class InstructionSequenceConstants
 //                new SimpleInstruction(InstructionConstants.OP_SASTORE),
 //            },
 //        },
+    };
+
+    public static final Instruction[][][] BRANCH = new Instruction[][][]
+    {
         {   // goto +3 = nothing
             {
                 new BranchInstruction(InstructionConstants.OP_GOTO, 3),
@@ -2529,7 +2584,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFEQ, X),
             },
         },
-        {   // if (0 == i) = aload/getfield/ifeq
+        {   // if (0 == i) = getfield/ifeq
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2559,7 +2614,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNE, X),
             },
         },
-        {   // if (0 != i) = iload/ifeq
+        {   // if (0 != i) = getstatic/ifeq
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2569,7 +2624,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNE, X),
             },
         },
-        {   // if (0 != i) = iload/ifeq
+        {   // if (0 != i) = getfield/ifeq
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2589,6 +2644,14 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLT, X),
             },
         },
+        {   // if (... < 1) = ifle
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
         {   // if (0 > i) = iload/iflt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2599,7 +2662,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLT, X),
             },
         },
-        {   // if (0 > i) = iload/iflt
+        {   // if (1 > i) = iload/ifle
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
+        {   // if (0 > i) = getstatic/iflt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2609,7 +2682,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLT, X),
             },
         },
-        {   // if (0 > i) = iload/iflt
+        {   // if (1 > i) = getstatic/ifle
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },{
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
+        {   // if (0 > i) = getfield/iflt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2621,6 +2704,18 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLT, X),
             },
         },
+        {   // if (1 > i) = getfield/ifle
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
         {   // if (... >= 0) = ifge
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2629,6 +2724,14 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGE, X),
             },
         },
+        {   // if (... >= 1) = ifgt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
         {   // if (0 <= i) = iload/ifge
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2639,7 +2742,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGE, X),
             },
         },
-        {   // if (0 <= i) = iload/ifge
+        {   // if (1 <= i) = iload/ifgt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
+        {   // if (0 <= i) = getstatic/ifge
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2649,7 +2762,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGE, X),
             },
         },
-        {   // if (0 <= i) = iload/ifge
+        {   // if (1 <= i) = getstatic/ifgt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },{
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
+        {   // if (0 <= i) = getfield/ifge
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2661,6 +2784,18 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGE, X),
             },
         },
+        {   // if (1 <= i) = getfield/ifgt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
         {   // if (... > 0) = ifgt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2669,6 +2804,14 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGT, X),
             },
         },
+        {   // if (... > -1) = ifge
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
         {   // if (0 < i) = iload/ifgt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2679,7 +2822,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGT, X),
             },
         },
-        {   // if (0 < i) = iload/ifgt
+        {   // if (-1 < i) = iload/ifge
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
+        {   // if (0 < i) = getstatic/ifgt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2689,7 +2842,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGT, X),
             },
         },
-        {   // if (0 < i) = iload/ifgt
+        {   // if (-1 < i) = getstatic/ifge
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },{
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
+        {   // if (0 < i) = getfield/ifgt
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2701,6 +2864,18 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFGT, X),
             },
         },
+        {   // if (-1 < i) = getfield/ifge
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
         {   // if (... <= 0) = ifle
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2709,6 +2884,14 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLE, X),
             },
         },
+        {   // if (... <= -1) = iflt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
         {   // if (0 >= i) = iload/ifle
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
@@ -2719,7 +2902,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLE, X),
             },
         },
-        {   // if (0 >= i) = iload/ifle
+        {   // if (-1 >= i) = iload/iflt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, Y),
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
+        {   // if (0 >= i) = getstatic/ifle
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2729,7 +2922,17 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLE, X),
             },
         },
-        {   // if (0 >= i) = iload/ifle
+        {   // if (-1 >= i) = getstatic/iflt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },{
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
+        {   // if (0 >= i) = getfield/ifle
             {
                 new SimpleInstruction(InstructionConstants.OP_ICONST_0),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2741,6 +2944,18 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFLE, X),
             },
         },
+        {   // if (-1 >= i) = getfield/iflt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Z),
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
         {   // if (... == null) = ifnull
             {
                 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
@@ -2759,7 +2974,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNULL, X),
             },
         },
-        {   // if (null == a) = aload/ifnull
+        {   // if (null == a) = getstatic/ifnull
             {
                 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2769,7 +2984,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNULL, X),
             },
         },
-        {   // if (null == a) = aload/ifnull
+        {   // if (null == a) = getfield/ifnull
             {
                 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
@@ -2799,7 +3014,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNONNULL, X),
             },
         },
-        {   // if (null != a) = aload/ifnonnull
+        {   // if (null != a) = getstatic/ifnonnull
             {
                 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
                 new ConstantInstruction(InstructionConstants.OP_GETSTATIC, Y),
@@ -2809,7 +3024,7 @@ public class InstructionSequenceConstants
                 new BranchInstruction(InstructionConstants.OP_IFNONNULL, X),
             },
         },
-        {   // if (null != a) = aload/ifnonnull
+        {   // if (null != a) = getfield/ifnonnull
             {
                 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
                 new VariableInstruction(InstructionConstants.OP_ALOAD, Y),
diff --git a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java
index eae441e..bce06e2 100644
--- a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java
+++ b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java
index 6aab0a4..f12b51a 100644
--- a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java
+++ b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/LoadStoreRemover.java b/src/proguard/optimize/peephole/LoadStoreRemover.java
deleted file mode 100644
index e1d5d69..0000000
--- a/src/proguard/optimize/peephole/LoadStoreRemover.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.peephole;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttribute;
-import proguard.classfile.editor.CodeAttributeEditor;
-import proguard.classfile.instruction.*;
-import proguard.classfile.instruction.visitor.InstructionVisitor;
-import proguard.classfile.util.SimplifiedVisitor;
-
-/**
- * This InstructionVisitor deletes load/store instruction pairs.
- *
- * @author Eric Lafortune
- */
-public class LoadStoreRemover
-extends      SimplifiedVisitor
-implements   InstructionVisitor
-{
-    private final BranchTargetFinder  branchTargetFinder;
-    private final CodeAttributeEditor codeAttributeEditor;
-    private final InstructionVisitor  extraInstructionVisitor;
-
-
-    /**
-     * Creates a new LoadStoreRemover.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttributeEditor     a code editor that can be used for
-     *                                accumulating changes to the code.
-     */
-    public LoadStoreRemover(BranchTargetFinder  branchTargetFinder,
-                            CodeAttributeEditor codeAttributeEditor)
-    {
-        this(branchTargetFinder, codeAttributeEditor, null);
-    }
-
-
-    /**
-     * Creates a new LoadStoreRemover.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttributeEditor     a code editor that can be used for
-     *                                accumulating changes to the code.
-     * @param extraInstructionVisitor an optional extra visitor for all deleted
-     *                                load instructions.
-     */
-    public LoadStoreRemover(BranchTargetFinder  branchTargetFinder,
-                            CodeAttributeEditor codeAttributeEditor,
-                            InstructionVisitor  extraInstructionVisitor)
-    {
-        this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttributeEditor     = codeAttributeEditor;
-        this.extraInstructionVisitor = extraInstructionVisitor;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
-
-
-    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
-    {
-        // Is this instruction a load instruction?
-        if (variableInstruction.isLoad() &&
-            variableInstruction.opcode != InstructionConstants.OP_RET)
-        {
-            int variableIndex = variableInstruction.variableIndex;
-
-            int nextOffset = offset + variableInstruction.length(offset);
-
-            if (!codeAttributeEditor.isModified(offset)     &&
-                !codeAttributeEditor.isModified(nextOffset) &&
-                !branchTargetFinder.isTarget(nextOffset))
-            {
-                // Is the next instruction a corresponding store instruction?
-                Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
-                                                                        nextOffset);
-
-                if (nextInstruction instanceof VariableInstruction)
-                {
-                    variableInstruction = (VariableInstruction)nextInstruction;
-                    if (!variableInstruction.isLoad()                              &&
-                        variableInstruction.opcode != InstructionConstants.OP_IINC &&
-                        variableInstruction.variableIndex == variableIndex)
-                    {
-                        // Delete both instructions.
-                        codeAttributeEditor.deleteInstruction(offset);
-                        codeAttributeEditor.deleteInstruction(nextOffset);
-
-                        // Visit the instruction, if required.
-                        if (extraInstructionVisitor != null)
-                        {
-                            extraInstructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/src/proguard/optimize/peephole/MemberPrivatizer.java b/src/proguard/optimize/peephole/MemberPrivatizer.java
index ac2185c..55b2f31 100644
--- a/src/proguard/optimize/peephole/MemberPrivatizer.java
+++ b/src/proguard/optimize/peephole/MemberPrivatizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -27,9 +27,9 @@ import proguard.classfile.visitor.MemberVisitor;
 import proguard.optimize.info.NonPrivateMemberMarker;
 
 /**
- * This MemberVisitor makes all class members that it visits private, unless they
- * have been marked by a NonPrivateMemberMarker. The invocations of the
- * privatized method still have to be fixed.
+ * This MemberVisitor makes all class members that it visits private, unless
+ * they have been marked by a NonPrivateMemberMarker. The invocations of
+ * privatized methods still have to be fixed.
  *
  * @see NonPrivateMemberMarker
  * @see MethodInvocationFixer
@@ -39,8 +39,7 @@ public class MemberPrivatizer
 extends      SimplifiedVisitor
 implements   MemberVisitor
 {
-    private final MemberVisitor extraFieldVisitor;
-    private final MemberVisitor extraMethodVisitor;
+    private final MemberVisitor extraMemberVisitor;
 
 
     /**
@@ -48,22 +47,18 @@ implements   MemberVisitor
      */
     public MemberPrivatizer()
     {
-        this(null, null);
+        this(null);
     }
 
 
     /**
      * Creates a new MemberPrivatizer.
-     * @param extraFieldVisitor  an optional extra visitor for all privatized
-     *                           fields.
-     * @param extraMethodVisitor an optional extra visitor for all privatized
-     *                           methods.
+     * @param extraMemberVisitor an optional extra visitor for all privatized
+     *                           class members.
      */
-    public MemberPrivatizer(MemberVisitor extraFieldVisitor,
-                            MemberVisitor extraMethodVisitor)
+    public MemberPrivatizer(MemberVisitor extraMemberVisitor)
     {
-        this.extraFieldVisitor  = extraFieldVisitor;
-        this.extraMethodVisitor = extraMethodVisitor;
+        this.extraMemberVisitor = extraMemberVisitor;
     }
 
 
@@ -80,9 +75,9 @@ implements   MemberVisitor
                                               ClassConstants.INTERNAL_ACC_PRIVATE);
 
             // Visit the field, if required.
-            if (extraFieldVisitor != null)
+            if (extraMemberVisitor != null)
             {
-                extraFieldVisitor.visitProgramField(programClass, programField);
+                extraMemberVisitor.visitProgramField(programClass, programField);
             }
         }
     }
@@ -99,9 +94,9 @@ implements   MemberVisitor
                                               ClassConstants.INTERNAL_ACC_PRIVATE);
 
             // Visit the method, if required.
-            if (extraMethodVisitor != null)
+            if (extraMemberVisitor != null)
             {
-                extraMethodVisitor.visitProgramMethod(programClass, programMethod);
+                extraMemberVisitor.visitProgramMethod(programClass, programMethod);
             }
         }
     }
diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/src/proguard/optimize/peephole/MethodFinalizer.java
similarity index 63%
copy from src/proguard/optimize/peephole/ClassFinalizer.java
copy to src/proguard/optimize/peephole/MethodFinalizer.java
index 00654ee..af1811b 100644
--- a/src/proguard/optimize/peephole/ClassFinalizer.java
+++ b/src/proguard/optimize/peephole/MethodFinalizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -26,17 +26,15 @@ import proguard.classfile.visitor.*;
 import proguard.optimize.KeepMarker;
 
 /**
- * This <code>ClassVisitor</code> and <code>MemberVisitor</code>
- * makes the classes it visits, and their methods, final, if possible.
+ * This <code>MemberVisitor</code> makes the program methods that it visits
+ * final, if possible.
  *
  * @author Eric Lafortune
  */
-public class ClassFinalizer
+public class MethodFinalizer
 extends      SimplifiedVisitor
-implements   ClassVisitor,
-             MemberVisitor
+implements   MemberVisitor
 {
-    private final ClassVisitor  extraClassVisitor;
     private final MemberVisitor extraMemberVisitor;
 
     private final MemberFinder memberFinder = new MemberFinder();
@@ -45,57 +43,25 @@ implements   ClassVisitor,
     /**
      * Creates a new ClassFinalizer.
      */
-    public ClassFinalizer()
+    public MethodFinalizer()
     {
-        this(null, null);
+        this(null);
     }
 
 
     /**
      * Creates a new ClassFinalizer.
-     * @param extraClassVisitor  an optional extra visitor for all finalized
-     *                           classes.
      * @param extraMemberVisitor an optional extra visitor for all finalized
      *                           methods.
      */
-    public ClassFinalizer(ClassVisitor  extraClassVisitor,
-                          MemberVisitor extraMemberVisitor)
+    public MethodFinalizer(MemberVisitor extraMemberVisitor)
     {
-        this.extraClassVisitor  = extraClassVisitor;
         this.extraMemberVisitor = extraMemberVisitor;
     }
 
 
-    // Implementations for ClassVisitor.
-
-    public void visitProgramClass(ProgramClass programClass)
-    {
-        // If the class is not final/interface/abstract,
-        // and it is not being kept,
-        // and it doesn't have any subclasses,
-        // then make it final.
-        if ((programClass.u2accessFlags & (ClassConstants.INTERNAL_ACC_FINAL     |
-                                           ClassConstants.INTERNAL_ACC_INTERFACE |
-                                           ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 &&
-            !KeepMarker.isKept(programClass)                                           &&
-            programClass.subClasses == null)
-        {
-            programClass.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
-
-            // Visit the class, if required.
-            if (extraClassVisitor != null)
-            {
-                extraClassVisitor.visitProgramClass(programClass);
-            }
-        }
-
-        // Check all methods.
-        programClass.methodsAccept(this);
-    }
-
-
     // Implementations for MemberVisitor.
-
+    
     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
         String name = programMethod.getName(programClass);
@@ -124,4 +90,4 @@ implements   ClassVisitor,
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java
index ebb588c..55f9ccb 100644
--- a/src/proguard/optimize/peephole/MethodInliner.java
+++ b/src/proguard/optimize/peephole/MethodInliner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -240,7 +240,7 @@ implements   AttributeVisitor,
             }
         }
 
-        codeAttributeComposer.beginCodeFragment((parameterOffset + parameterCount) * 4);
+        codeAttributeComposer.beginCodeFragment(parameterSize+1);
 
         // Go over the parameter types backward, storing the stack entries
         // in their corresponding variables.
@@ -300,8 +300,7 @@ implements   AttributeVisitor,
     {
         // The code may expand, due to expanding constant and variable
         // instructions.
-        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength * MAXIMUM_CODE_EXPANSION +
-                                                MAXIMUM_EXTRA_CODE_LENGTH);
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
 
         // Copy the instructions.
         codeAttribute.instructionsAccept(clazz, method, this);
@@ -549,6 +548,14 @@ implements   AttributeVisitor,
             // Inline the method body.
             programMethod.attributesAccept(programClass, this);
 
+            // Update the optimization information of the target method.
+            MethodOptimizationInfo info =
+                MethodOptimizationInfo.getMethodOptimizationInfo(targetMethod);
+            if (info != null)
+            {
+                info.merge(MethodOptimizationInfo.getMethodOptimizationInfo(programMethod));
+            }
+
             inlining = oldInlining;
             inliningMethods.pop();
         }
diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java
index 7d5b12d..69adb30 100644
--- a/src/proguard/optimize/peephole/NopRemover.java
+++ b/src/proguard/optimize/peephole/NopRemover.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/PeepholeOptimizer.java b/src/proguard/optimize/peephole/PeepholeOptimizer.java
index 9580862..98f8e8d 100644
--- a/src/proguard/optimize/peephole/PeepholeOptimizer.java
+++ b/src/proguard/optimize/peephole/PeepholeOptimizer.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/PushPopRemover.java b/src/proguard/optimize/peephole/PushPopRemover.java
deleted file mode 100644
index b3d322a..0000000
--- a/src/proguard/optimize/peephole/PushPopRemover.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.peephole;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttribute;
-import proguard.classfile.editor.CodeAttributeEditor;
-import proguard.classfile.instruction.*;
-import proguard.classfile.instruction.visitor.InstructionVisitor;
-import proguard.classfile.util.SimplifiedVisitor;
-
-/**
- * This InstructionVisitor deletes all push/pop instruction pairs. In this
- * context, push instructions are instructions that push values onto the stack,
- * like dup and load instructions.
- *
- * @author Eric Lafortune
- */
-public class PushPopRemover
-extends      SimplifiedVisitor
-implements   InstructionVisitor
-{
-    private final BranchTargetFinder  branchTargetFinder;
-    private final CodeAttributeEditor codeAttributeEditor;
-    private final InstructionVisitor  extraInstructionVisitor;
-
-
-    /**
-     * Creates a new PushPopRemover.
-     * @param branchTargetFinder    a branch target finder that has been
-     *                              initialized to indicate branch targets
-     *                              in the visited code.
-     * @param codeAttributeEditor   a code editor that can be used for
-     *                              accumulating changes to the code.
-     */
-    public PushPopRemover(BranchTargetFinder  branchTargetFinder,
-                          CodeAttributeEditor codeAttributeEditor)
-    {
-        this(branchTargetFinder, codeAttributeEditor, null);
-    }
-
-
-    /**
-     * Creates a new PushPopRemover.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttributeEditor     a code editor that can be used for
-     *                                accumulating changes to the code.
-     * @param extraInstructionVisitor an optional extra visitor for all deleted
-     *                                push instructions.
-     */
-    public PushPopRemover(BranchTargetFinder  branchTargetFinder,
-                          CodeAttributeEditor codeAttributeEditor,
-                          InstructionVisitor  extraInstructionVisitor)
-    {
-        this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttributeEditor     = codeAttributeEditor;
-        this.extraInstructionVisitor = extraInstructionVisitor;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
-
-
-    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
-    {
-        switch (simpleInstruction.opcode)
-        {
-            case InstructionConstants.OP_ICONST_M1:
-            case InstructionConstants.OP_ICONST_0:
-            case InstructionConstants.OP_ICONST_1:
-            case InstructionConstants.OP_ICONST_2:
-            case InstructionConstants.OP_ICONST_3:
-            case InstructionConstants.OP_ICONST_4:
-            case InstructionConstants.OP_ICONST_5:
-            case InstructionConstants.OP_LCONST_0:
-            case InstructionConstants.OP_LCONST_1:
-            case InstructionConstants.OP_FCONST_0:
-            case InstructionConstants.OP_FCONST_1:
-            case InstructionConstants.OP_FCONST_2:
-            case InstructionConstants.OP_DCONST_0:
-            case InstructionConstants.OP_DCONST_1:
-
-            case InstructionConstants.OP_DUP:
-            case InstructionConstants.OP_DUP2:
-            case InstructionConstants.OP_BIPUSH:
-            case InstructionConstants.OP_SIPUSH:
-            case InstructionConstants.OP_LDC:
-            case InstructionConstants.OP_LDC_W:
-            case InstructionConstants.OP_LDC2_W:
-                // All these simple instructions are pushing instructions.
-                deleteWithSubsequentPop(clazz, method, codeAttribute, offset, simpleInstruction);
-                break;
-        }
-    }
-
-    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
-    {
-        if (variableInstruction.isLoad() &&
-            variableInstruction.opcode != InstructionConstants.OP_RET)
-        {
-            // All load instructions are pushing instructions.
-            deleteWithSubsequentPop(clazz, method, codeAttribute, offset, variableInstruction);
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Deletes the given instruction and its subsequent compatible pop instruction,
-     * if any, and if the latter is not a branch target.
-     */
-    private void deleteWithSubsequentPop(Clazz         clazz,
-                                         Method        method,
-                                         CodeAttribute codeAttribute,
-                                         int           offset,
-                                         Instruction   instruction)
-    {
-        boolean isCategory2 = instruction.isCategory2();
-
-        int nextOffset = offset + instruction.length(offset);
-
-        if (!codeAttributeEditor.isModified(offset)     &&
-            !codeAttributeEditor.isModified(nextOffset) &&
-            !branchTargetFinder.isTarget(nextOffset))
-        {
-            Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
-                                                                    nextOffset);
-            int nextOpcode = nextInstruction.opcode;
-            if ((nextOpcode == InstructionConstants.OP_POP ||
-                 nextOpcode == InstructionConstants.OP_POP2) &&
-                                                             nextInstruction.isCategory2() == isCategory2)
-            {
-                // Delete the pushing instruction and the pop instruction.
-                codeAttributeEditor.deleteInstruction(offset);
-                codeAttributeEditor.deleteInstruction(nextOffset);
-
-                // Visit the instruction, if required.
-                if (extraInstructionVisitor != null)
-                {
-                    instruction.accept(clazz, method, codeAttribute, offset, extraInstructionVisitor);
-                }
-            }
-        }
-    }
-}
diff --git a/src/proguard/optimize/peephole/ReachableCodeMarker.java b/src/proguard/optimize/peephole/ReachableCodeMarker.java
index d9d9048..d9dbf2d 100644
--- a/src/proguard/optimize/peephole/ReachableCodeMarker.java
+++ b/src/proguard/optimize/peephole/ReachableCodeMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java
index efb9551..6707a12 100644
--- a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java
+++ b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/StoreLoadReplacer.java b/src/proguard/optimize/peephole/StoreLoadReplacer.java
deleted file mode 100644
index 9cf58d6..0000000
--- a/src/proguard/optimize/peephole/StoreLoadReplacer.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.peephole;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttribute;
-import proguard.classfile.editor.CodeAttributeEditor;
-import proguard.classfile.instruction.*;
-import proguard.classfile.instruction.visitor.InstructionVisitor;
-import proguard.classfile.util.SimplifiedVisitor;
-
-/**
- * This InstructionVisitor replaces store/load instruction pairs by equivalent
- * dup/store instruction pairs.
- *
- * @author Eric Lafortune
- */
-public class StoreLoadReplacer
-extends      SimplifiedVisitor
-implements   InstructionVisitor
-{
-    // Some Instruction objects that can be reused.
-    private final Instruction dupInstruction  = new SimpleInstruction(InstructionConstants.OP_DUP);
-    private final Instruction dup2Instruction = new SimpleInstruction(InstructionConstants.OP_DUP2);
-
-    private final BranchTargetFinder  branchTargetFinder;
-    private final CodeAttributeEditor codeAttributeEditor;
-    private final InstructionVisitor  extraInstructionVisitor;
-
-
-    /**
-     * Creates a new StoreLoadReplacer.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttributeEditor     a code editor that can be used for
-     *                                accumulating changes to the code.
-     */
-    public StoreLoadReplacer(BranchTargetFinder  branchTargetFinder,
-                             CodeAttributeEditor codeAttributeEditor)
-    {
-        this(branchTargetFinder, codeAttributeEditor, null);
-    }
-
-
-    /**
-     * Creates a new StoreLoadReplacer.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttributeEditor     a code editor that can be used for
-     *                                accumulating changes to the code.
-     * @param extraInstructionVisitor an optional extra visitor for all replaced
-     *                                store instructions.
-     */
-    public StoreLoadReplacer(BranchTargetFinder  branchTargetFinder,
-                             CodeAttributeEditor codeAttributeEditor,
-                             InstructionVisitor  extraInstructionVisitor)
-    {
-        this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttributeEditor     = codeAttributeEditor;
-        this.extraInstructionVisitor = extraInstructionVisitor;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
-
-
-    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
-    {
-        // Is this instruction a regular store instruction?
-        if (!variableInstruction.isLoad() &&
-            variableInstruction.opcode != InstructionConstants.OP_IINC)
-        {
-            byte opcode        = variableInstruction.opcode;
-            int  variableIndex = variableInstruction.variableIndex;
-
-            int nextOffset = offset + variableInstruction.length(offset);
-
-            if (!codeAttributeEditor.isModified(offset)     &&
-                !codeAttributeEditor.isModified(nextOffset) &&
-                !branchTargetFinder.isTarget(nextOffset))
-            {
-                // Is the next instruction a corresponding load instruction?
-                Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
-                                                                        nextOffset);
-
-                if (nextInstruction instanceof VariableInstruction)
-                {
-                    variableInstruction = (VariableInstruction)nextInstruction;
-                    if (variableInstruction.isLoad()                              &&
-                        variableInstruction.opcode != InstructionConstants.OP_RET &&
-                        variableInstruction.variableIndex == variableIndex)
-                    {
-                        // Replace the store instruction by a matching dup instruction.
-                        Instruction matchingDupInstruction = variableInstruction.isCategory2() ?
-                                                             dup2Instruction :
-                                                             dupInstruction;
-
-                        codeAttributeEditor.replaceInstruction(offset,
-                                                               matchingDupInstruction);
-
-                        // Replace the load instruction by the store instruction.
-                        Instruction storeInstruction =
-                             new VariableInstruction(opcode,
-                                                     variableInstruction.variableIndex).shrink();
-
-                        codeAttributeEditor.replaceInstruction(nextOffset,
-                                                               storeInstruction);
-
-                        // Visit the instruction, if required.
-                        if (extraInstructionVisitor != null)
-                        {
-                            extraInstructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java
index 7d590c5..22fd83d 100644
--- a/src/proguard/optimize/peephole/TargetClassChanger.java
+++ b/src/proguard/optimize/peephole/TargetClassChanger.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/src/proguard/optimize/peephole/UnreachableCodeRemover.java
index 72ff728..e8a99ab 100644
--- a/src/proguard/optimize/peephole/UnreachableCodeRemover.java
+++ b/src/proguard/optimize/peephole/UnreachableCodeRemover.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java
index 8cf2576..048f5e3 100644
--- a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java
+++ b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/optimize/peephole/VariableShrinker.java b/src/proguard/optimize/peephole/VariableShrinker.java
index 8ec1998..45b694f 100644
--- a/src/proguard/optimize/peephole/VariableShrinker.java
+++ b/src/proguard/optimize/peephole/VariableShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -101,7 +101,7 @@ implements   AttributeVisitor
             // Delete unused local variables from the local variable frame.
             variableEditor.reset(maxLocals);
 
-            for (int variableIndex = parameterSize+1; variableIndex < maxLocals; variableIndex++)
+            for (int variableIndex = parameterSize; variableIndex < maxLocals; variableIndex++)
             {
                 // Is the variable not required?
                 if (!variableUsageMarker.isVariableUsed(variableIndex))
diff --git a/src/proguard/optimize/peephole/VerticalClassMerger.java b/src/proguard/optimize/peephole/VerticalClassMerger.java
index b0054f8..29ed6ff 100644
--- a/src/proguard/optimize/peephole/VerticalClassMerger.java
+++ b/src/proguard/optimize/peephole/VerticalClassMerger.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/preverify/CodePreverifier.java b/src/proguard/preverify/CodePreverifier.java
index b6ca1ac..fa60b9a 100644
--- a/src/proguard/preverify/CodePreverifier.java
+++ b/src/proguard/preverify/CodePreverifier.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -301,6 +301,7 @@ implements   MemberVisitor,
                                                      programMethod,
                                                      codeAttribute,
                                                      offset,
+                                                     index == 0,
                                                      value,
                                                      producerValue);
 
@@ -368,6 +369,7 @@ implements   MemberVisitor,
                                               programMethod,
                                               codeAttribute,
                                               offset,
+                                              false,
                                               value,
                                               producerValue);
 
@@ -391,6 +393,7 @@ implements   MemberVisitor,
                                                            ProgramMethod programMethod,
                                                            CodeAttribute codeAttribute,
                                                            int           offset,
+                                                           boolean       isVariable0,
                                                            Value         value,
                                                            Value         producerValue)
     {
@@ -433,32 +436,25 @@ implements   MemberVisitor,
                             producerOffset = producers.instructionOffset(0);
                         }
 
-                        // Special case: in an instance initialization method,
-                        // before the super initialization, loading "this"
-                        // produces an uninitialized stack entry.
+                        // Are we in an instance initialization method,
+                        // before the super initialization, loading "this"?
                         if (partialEvaluator.isInitializer()                       &&
                             offset <= partialEvaluator.superInitializationOffset() &&
-                            producerOffset > PartialEvaluator.AT_METHOD_ENTRY      &&
-                            codeAttribute.code[producerOffset] == InstructionConstants.OP_ALOAD_0)
+                            (isVariable0 ||
+                             producerOffset > PartialEvaluator.AT_METHOD_ENTRY &&
+                             codeAttribute.code[producerOffset] == InstructionConstants.OP_ALOAD_0))
                         {
-                            producerOffset = PartialEvaluator.AT_METHOD_ENTRY;
+                            // It's an UninitializedThis type.
+                            return VerificationTypeFactory.createUninitializedThisType();
                         }
 
-                        // Is the reference type newly created?
-                        int initializationOffset = producerOffset == PartialEvaluator.AT_METHOD_ENTRY ?
-                            partialEvaluator.superInitializationOffset() :
-                            partialEvaluator.initializationOffset(producerOffset);
-
-                        if (initializationOffset != PartialEvaluator.NONE)
+                        // Is the reference type newly created and still
+                        // uninitialized?
+                        if (producerOffset > PartialEvaluator.AT_METHOD_ENTRY &&
+                            offset <= partialEvaluator.initializationOffset(producerOffset))
                         {
-                            // Is the reference type still uninitialized?
-                            if (offset <= initializationOffset)
-                            {
-                                // It's an UninitializedThis or Uninitialized type.
-                                return producerOffset == PartialEvaluator.AT_METHOD_ENTRY ?
-                                    (VerificationType)VerificationTypeFactory.createUninitializedThisType() :
-                                    (VerificationType)VerificationTypeFactory.createUninitializedType(producerOffset);
-                            }
+                            // It's an Uninitialized type.
+                            return VerificationTypeFactory.createUninitializedType(producerOffset);
                         }
                     }
                 }
diff --git a/src/proguard/preverify/CodeSubroutineInliner.java b/src/proguard/preverify/CodeSubroutineInliner.java
index cfc2c25..603eb75 100644
--- a/src/proguard/preverify/CodeSubroutineInliner.java
+++ b/src/proguard/preverify/CodeSubroutineInliner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -68,11 +68,32 @@ implements   AttributeVisitor,
 //            clazz.getName().equals("abc/Def") &&
 //            method.getName(clazz).equals("abc");
 
-        if (DEBUG)
+        // TODO: Remove this when the subroutine inliner has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
         {
-            method.accept(clazz, new ClassPrinter());
+            System.err.println("Unexpected error while inlining subroutines:");
+            System.err.println("  Class       = ["+clazz.getName()+"]");
+            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;
         }
+    }
+
 
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
         branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
 
         // Don't bother if there aren't any subroutines anyway.
@@ -136,11 +157,6 @@ implements   AttributeVisitor,
         // End and update the code attribute.
         codeAttributeComposer.endCodeFragment();
         codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
-
-        if (DEBUG)
-        {
-            method.accept(clazz, new ClassPrinter());
-        }
     }
 
 
@@ -368,7 +384,15 @@ implements   AttributeVisitor,
 
         if (DEBUG)
         {
-            System.out.println("  Appending exception ["+startPC+" -> "+endPC+"] -> "+handlerPC);
+            if (startPC == exceptionInfo.u2startPC &&
+                endPC   == exceptionInfo.u2endPC)
+            {
+                System.out.println("  Appending exception ["+startPC+" -> "+endPC+"] -> "+handlerPC);
+            }
+            else
+            {
+                System.out.println("  Appending clipped exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+"] ~> ["+startPC+" -> "+endPC+"] -> "+handlerPC);
+            }
         }
 
         // Append the exception. Note that exceptions with empty try blocks
diff --git a/src/proguard/preverify/Preverifier.java b/src/proguard/preverify/Preverifier.java
index 55e005f..e071c5c 100644
--- a/src/proguard/preverify/Preverifier.java
+++ b/src/proguard/preverify/Preverifier.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/preverify/SubroutineInliner.java b/src/proguard/preverify/SubroutineInliner.java
index 65d5803..e28512f 100644
--- a/src/proguard/preverify/SubroutineInliner.java
+++ b/src/proguard/preverify/SubroutineInliner.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/retrace/ReTrace.java b/src/proguard/retrace/ReTrace.java
index 81b0832..97ab27b 100644
--- a/src/proguard/retrace/ReTrace.java
+++ b/src/proguard/retrace/ReTrace.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -20,9 +20,12 @@
  */
 package proguard.retrace;
 
-import proguard.obfuscate.MappingReader;
+import proguard.classfile.util.ClassUtil;
+import proguard.obfuscate.*;
 
 import java.io.*;
+import java.util.*;
+import java.util.regex.*;
 
 
 /**
@@ -32,46 +35,71 @@ import java.io.*;
  * @author Eric Lafortune
  */
 public class ReTrace
+implements   MappingProcessor
 {
+    private static final String REGEX_OPTION   = "-regex";
     private static final String VERBOSE_OPTION = "-verbose";
 
 
+    public static final String STACK_TRACE_EXPRESSION = "(?:\\s*%c:.*)|(?:\\s*at\\s+%c.%m\\s*\\(.*?(?::%l)?\\)\\s*)";
+
+    private static final String REGEX_CLASS       = "\\b(?:[A-Za-z0-9_$]+\\.)*[A-Za-z0-9_$]+\\b";
+    private static final String REGEX_CLASS_SLASH = "\\b(?:[A-Za-z0-9_$]+/)*[A-Za-z0-9_$]+\\b";
+    private static final String REGEX_LINE_NUMBER = "\\b[0-9]+\\b";
+    private static final String REGEX_TYPE        = REGEX_CLASS + "(?:\\[\\])*";
+    private static final String REGEX_MEMBER      = "\\b[A-Za-z0-9_$]+\\b";
+    private static final String REGEX_ARGUMENTS   = "(?:" + REGEX_TYPE + "(?:\\s*,\\s*" + REGEX_TYPE + ")*)?";
+
     // The class settings.
+    private final String  regularExpression;
     private final boolean verbose;
     private final File    mappingFile;
     private final File    stackTraceFile;
 
+    private Map classMap       = new HashMap();
+    private Map classFieldMap  = new HashMap();
+    private Map classMethodMap = new HashMap();
+
 
     /**
      * Creates a new ReTrace object to process stack traces on the standard
      * input, based on the given mapping file name.
-     * @param verbose     specifies whether the de-obfuscated stack trace
-     *                    should be verbose.
-     * @param mappingFile the mapping file that was written out by ProGuard.
+     * @param regularExpression the regular expression for parsing the lines in
+     *                          the stack trace.
+     * @param verbose           specifies whether the de-obfuscated stack trace
+     *                          should be verbose.
+     * @param mappingFile       the mapping file that was written out by
+     *                          ProGuard.
      */
-    public ReTrace(boolean verbose,
+    public ReTrace(String  regularExpression,
+                   boolean verbose,
                    File    mappingFile)
     {
-        this(verbose, mappingFile, null);
+        this(regularExpression, verbose, mappingFile, null);
     }
 
 
     /**
      * Creates a new ReTrace object to process a stack trace from the given file,
      * based on the given mapping file name.
-     * @param verbose        specifies whether the de-obfuscated stack trace
-     *                       should be verbose.
-     * @param mappingFile    the mapping file that was written out by ProGuard.
-     * @param stackTraceFile the optional name of the file that contains the
-     *                       stack trace.
+     * @param regularExpression the regular expression for parsing the lines in
+     *                          the stack trace.
+     * @param verbose           specifies whether the de-obfuscated stack trace
+     *                          should be verbose.
+     * @param mappingFile       the mapping file that was written out by
+     *                          ProGuard.
+     * @param stackTraceFile    the optional name of the file that contains the
+     *                          stack trace.
      */
-    public ReTrace(boolean verbose,
+    public ReTrace(String  regularExpression,
+                   boolean verbose,
                    File    mappingFile,
                    File    stackTraceFile)
     {
-        this.verbose        = verbose;
-        this.mappingFile    = mappingFile;
-        this.stackTraceFile = stackTraceFile;
+        this.regularExpression = regularExpression;
+        this.verbose           = verbose;
+        this.mappingFile       = mappingFile;
+        this.stackTraceFile    = stackTraceFile;
     }
 
 
@@ -80,17 +108,571 @@ public class ReTrace
      */
     public void execute() throws IOException
     {
-        StackTrace stackTrace = new StackTrace(verbose);
-        MappingReader reader = new MappingReader(mappingFile);
+        // Read the mapping file.
+        MappingReader mappingReader = new MappingReader(mappingFile);
+        mappingReader.pump(this);
+
+
+        StringBuffer expressionBuffer    = new StringBuffer(regularExpression.length() + 32);
+        char[]       expressionTypes     = new char[32];
+        int          expressionTypeCount = 0;
+        int index = 0;
+        while (true)
+        {
+            int nextIndex = regularExpression.indexOf('%', index);
+            if (nextIndex < 0                             ||
+                nextIndex == regularExpression.length()-1 ||
+                expressionTypeCount == expressionTypes.length)
+            {
+                break;
+            }
+
+            expressionBuffer.append(regularExpression.substring(index, nextIndex));
+            expressionBuffer.append('(');
+
+            char expressionType = regularExpression.charAt(nextIndex + 1);
+            switch(expressionType)
+            {
+                case 'c':
+                    expressionBuffer.append(REGEX_CLASS);
+                    break;
+
+                case 'C':
+                    expressionBuffer.append(REGEX_CLASS_SLASH);
+                    break;
+
+                case 'l':
+                    expressionBuffer.append(REGEX_LINE_NUMBER);
+                    break;
+
+                case 't':
+                    expressionBuffer.append(REGEX_TYPE);
+                    break;
+
+                case 'f':
+                    expressionBuffer.append(REGEX_MEMBER);
+                    break;
+
+                case 'm':
+                    expressionBuffer.append(REGEX_MEMBER);
+                    break;
+
+                case 'a':
+                    expressionBuffer.append(REGEX_ARGUMENTS);
+                    break;
+            }
+
+            expressionBuffer.append(')');
+
+            expressionTypes[expressionTypeCount++] = expressionType;
+
+            index = nextIndex + 2;
+        }
+
+        expressionBuffer.append(regularExpression.substring(index));
+
+        Pattern pattern = Pattern.compile(expressionBuffer.toString());
+
+        // Read the stack trace file.
+        LineNumberReader reader =
+            new LineNumberReader(stackTraceFile == null ?
+                (Reader)new InputStreamReader(System.in) :
+                (Reader)new BufferedReader(new FileReader(stackTraceFile)));
+
+
+        try
+        {
+            StringBuffer outLine = new StringBuffer(256);
+            List         extraOutLines  = new ArrayList();
+
+            String className = null;
+
+            // Read the line in the stack trace.
+            while (true)
+            {
+                String line = reader.readLine();
+                if (line == null)
+                {
+                    break;
+                }
+
+                Matcher matcher = pattern.matcher(line);
+
+                if (matcher.matches())
+                {
+                    int    lineNumber = 0;
+                    String type       = null;
+                    String arguments  = null;
+
+                    // Figure out a class name, line number, type, and
+                    // arguments beforehand.
+                    for (int expressionTypeIndex = 0; expressionTypeIndex < expressionTypeCount; expressionTypeIndex++)
+                    {
+                        int startIndex = matcher.start(expressionTypeIndex + 1);
+                        if (startIndex >= 0)
+                        {
+                            String match = matcher.group(expressionTypeIndex + 1);
+
+                            char expressionType = expressionTypes[expressionTypeIndex];
+                            switch (expressionType)
+                            {
+                                case 'c':
+                                    className = originalClassName(match);
+                                    break;
+
+                                case 'C':
+                                    className = originalClassName(ClassUtil.externalClassName(match));
+                                    break;
+
+                                case 'l':
+                                    lineNumber = Integer.parseInt(match);
+                                    break;
+
+                                case 't':
+                                    type = originalType(match);
+                                    break;
+
+                                case 'a':
+                                    arguments = originalArguments(match);
+                                    break;
+                            }
+                        }
+                    }
+
+                    // Actually construct the output line.
+                    int lineIndex = 0;
+
+                    outLine.setLength(0);
+                    extraOutLines.clear();
+
+                    for (int expressionTypeIndex = 0; expressionTypeIndex < expressionTypeCount; expressionTypeIndex++)
+                    {
+                        int startIndex = matcher.start(expressionTypeIndex + 1);
+                        if (startIndex >= 0)
+                        {
+                            int    endIndex = matcher.end(expressionTypeIndex + 1);
+                            String match    = matcher.group(expressionTypeIndex + 1);
+
+                            // Copy a literal piece of input line.
+                            outLine.append(line.substring(lineIndex, startIndex));
+
+                            char expressionType = expressionTypes[expressionTypeIndex];
+                            switch (expressionType)
+                            {
+                                case 'c':
+                                    className = originalClassName(match);
+                                    outLine.append(className);
+                                    break;
+
+                                case 'C':
+                                    className = originalClassName(ClassUtil.externalClassName(match));
+                                    outLine.append(ClassUtil.internalClassName(className));
+                                    break;
+
+                                case 'l':
+                                    lineNumber = Integer.parseInt(match);
+                                    outLine.append(match);
+                                    break;
+
+                                case 't':
+                                    type = originalType(match);
+                                    outLine.append(type);
+                                    break;
+
+                                case 'f':
+                                    originalFieldName(className,
+                                                      match,
+                                                      type,
+                                                      outLine,
+                                                      extraOutLines);
+                                    break;
 
-        // Read the obfuscated stack trace.
-        stackTrace.read(stackTraceFile);
+                                case 'm':
+                                    originalMethodName(className,
+                                                       match,
+                                                       lineNumber,
+                                                       type,
+                                                       arguments,
+                                                       outLine,
+                                                       extraOutLines);
+                                    break;
 
-        // Resolve the obfuscated stack trace by means of the mapping file.
-        reader.pump(stackTrace);
+                                case 'a':
+                                    arguments = originalArguments(match);
+                                    outLine.append(arguments);
+                                    break;
+                            }
 
-        // Print out the resolved stack trace.
-        stackTrace.print();
+                            // Skip the original element whose processed version
+                            // has just been appended.
+                            lineIndex = endIndex;
+                        }
+                    }
+
+                    // Copy the last literal piece of input line.
+                    outLine.append(line.substring(lineIndex));
+
+                    // Print out the main line.
+                    System.out.println(outLine);
+
+                    // Print out any additional lines.
+                    for (int extraLineIndex = 0; extraLineIndex < extraOutLines.size(); extraLineIndex++)
+                    {
+                        System.out.println(extraOutLines.get(extraLineIndex));
+                    }
+                }
+                else
+                {
+                    // Print out the original line.
+                    System.out.println(line);
+                }
+            }
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Can't read stack trace (" + ex.getMessage() + ")");
+        }
+        finally
+        {
+            if (stackTraceFile != null)
+            {
+                try
+                {
+                    reader.close();
+                }
+                catch (IOException ex)
+                {
+                    // This shouldn't happen.
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Finds the original field name(s), appending the first one to the out
+     * line, and any additional alternatives to the extra lines.
+     */
+    private void originalFieldName(String       className,
+                                   String       obfuscatedFieldName,
+                                   String       type,
+                                   StringBuffer outLine,
+                                   List         extraOutLines)
+    {
+        int extraIndent = -1;
+
+        // Class name -> obfuscated field names.
+        Map fieldMap = (Map)classFieldMap.get(className);
+        if (fieldMap != null)
+        {
+            // Obfuscated field names -> fields.
+            Set fieldSet = (Set)fieldMap.get(obfuscatedFieldName);
+            if (fieldSet != null)
+            {
+                // Find all matching fields.
+                Iterator fieldInfoIterator = fieldSet.iterator();
+                while (fieldInfoIterator.hasNext())
+                {
+                    FieldInfo fieldInfo = (FieldInfo)fieldInfoIterator.next();
+                    if (fieldInfo.matches(type))
+                    {
+                        // Is this the first matching field?
+                        if (extraIndent < 0)
+                        {
+                            extraIndent = outLine.length();
+
+                            // Append the first original name.
+                            if (verbose)
+                            {
+                                outLine.append(fieldInfo.type).append(' ');
+                            }
+                            outLine.append(fieldInfo.originalName);
+                        }
+                        else
+                        {
+                            // Create an additional line with the proper
+                            // indentation.
+                            StringBuffer extraBuffer = new StringBuffer();
+                            for (int counter = 0; counter < extraIndent; counter++)
+                            {
+                                extraBuffer.append(' ');
+                            }
+
+                            // Append the alternative name.
+                            if (verbose)
+                            {
+                                extraBuffer.append(fieldInfo.type).append(' ');
+                            }
+                            extraBuffer.append(fieldInfo.originalName);
+
+                            // Store the additional line.
+                            extraOutLines.add(extraBuffer);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Just append the obfuscated name if we haven't found any matching
+        // fields.
+        if (extraIndent < 0)
+        {
+            outLine.append(obfuscatedFieldName);
+        }
+    }
+
+
+    /**
+     * Finds the original method name(s), appending the first one to the out
+     * line, and any additional alternatives to the extra lines.
+     */
+    private void originalMethodName(String       className,
+                                    String       obfuscatedMethodName,
+                                    int          lineNumber,
+                                    String       type,
+                                    String       arguments,
+                                    StringBuffer outLine,
+                                    List         extraOutLines)
+    {
+        int extraIndent = -1;
+
+        // Class name -> obfuscated method names.
+        Map methodMap = (Map)classMethodMap.get(className);
+        if (methodMap != null)
+        {
+            // Obfuscated method names -> methods.
+            Set methodSet = (Set)methodMap.get(obfuscatedMethodName);
+            if (methodSet != null)
+            {
+                // Find all matching methods.
+                Iterator methodInfoIterator = methodSet.iterator();
+                while (methodInfoIterator.hasNext())
+                {
+                    MethodInfo methodInfo = (MethodInfo)methodInfoIterator.next();
+                    if (methodInfo.matches(lineNumber, type, arguments))
+                    {
+                        // Is this the first matching method?
+                        if (extraIndent < 0)
+                        {
+                            extraIndent = outLine.length();
+
+                            // Append the first original name.
+                            if (verbose)
+                            {
+                                outLine.append(methodInfo.type).append(' ');
+                            }
+                            outLine.append(methodInfo.originalName);
+                            if (verbose)
+                            {
+                                outLine.append('(').append(methodInfo.arguments).append(')');
+                            }
+                        }
+                        else
+                        {
+                            // Create an additional line with the proper
+                            // indentation.
+                            StringBuffer extraBuffer = new StringBuffer();
+                            for (int counter = 0; counter < extraIndent; counter++)
+                            {
+                                extraBuffer.append(' ');
+                            }
+
+                            // Append the alternative name.
+                            if (verbose)
+                            {
+                                extraBuffer.append(methodInfo.type).append(' ');
+                            }
+                            extraBuffer.append(methodInfo.originalName);
+                            if (verbose)
+                            {
+                                extraBuffer.append('(').append(methodInfo.arguments).append(')');
+                            }
+
+                            // Store the additional line.
+                            extraOutLines.add(extraBuffer);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Just append the obfuscated name if we haven't found any matching
+        // methods.
+        if (extraIndent < 0)
+        {
+            outLine.append(obfuscatedMethodName);
+        }
+    }
+
+
+    /**
+     * Returns the original argument types.
+     */
+    private String originalArguments(String obfuscatedArguments)
+    {
+        StringBuffer originalArguments = new StringBuffer();
+
+        int startIndex = 0;
+        while (true)
+        {
+            int endIndex = obfuscatedArguments.indexOf(',', startIndex);
+            if (endIndex < 0)
+            {
+                break;
+            }
+
+            originalArguments.append(originalType(obfuscatedArguments.substring(startIndex, endIndex).trim())).append(',');
+
+            startIndex = endIndex + 1;
+        }
+
+        originalArguments.append(originalType(obfuscatedArguments.substring(startIndex).trim()));
+
+        return originalArguments.toString();
+    }
+
+
+    /**
+     * Returns the original type.
+     */
+    private String originalType(String obfuscatedType)
+    {
+        int index = obfuscatedType.indexOf('[');
+
+        return index >= 0 ?
+            originalClassName(obfuscatedType.substring(0, index)) + obfuscatedType.substring(index) :
+            originalClassName(obfuscatedType);
+    }
+
+
+    /**
+     * Returns the original class name.
+     */
+    private String originalClassName(String obfuscatedClassName)
+    {
+        String originalClassName = (String)classMap.get(obfuscatedClassName);
+
+        return originalClassName != null ?
+            originalClassName :
+            obfuscatedClassName;
+    }
+
+
+    // Implementations for MappingProcessor.
+
+    public boolean processClassMapping(String className, String newClassName)
+    {
+        // Obfuscated class name -> original class name.
+        classMap.put(newClassName, className);
+
+        return true;
+    }
+
+
+    public void processFieldMapping(String className, String fieldType, String fieldName, String newFieldName)
+    {
+        // Original class name -> obfuscated field names.
+        Map fieldMap = (Map)classFieldMap.get(className);
+        if (fieldMap == null)
+        {
+            fieldMap = new HashMap();
+            classFieldMap.put(className, fieldMap);
+        }
+
+        // Obfuscated field name -> fields.
+        Set fieldSet = (Set)fieldMap.get(newFieldName);
+        if (fieldSet == null)
+        {
+            fieldSet = new LinkedHashSet();
+            fieldMap.put(newFieldName, fieldSet);
+        }
+
+        // Add the field information.
+        fieldSet.add(new FieldInfo(fieldType,
+                                   fieldName));
+    }
+
+
+    public void processMethodMapping(String className, int firstLineNumber, int lastLineNumber, String methodReturnType, String methodName, String methodArguments, String newMethodName)
+    {
+        // Original class name -> obfuscated method names.
+        Map methodMap = (Map)classMethodMap.get(className);
+        if (methodMap == null)
+        {
+            methodMap = new HashMap();
+            classMethodMap.put(className, methodMap);
+        }
+
+        // Obfuscated method name -> methods.
+        Set methodSet = (Set)methodMap.get(newMethodName);
+        if (methodSet == null)
+        {
+            methodSet = new LinkedHashSet();
+            methodMap.put(newMethodName, methodSet);
+        }
+
+        // Add the method information.
+        methodSet.add(new MethodInfo(firstLineNumber,
+                                     lastLineNumber,
+                                     methodReturnType,
+                                     methodArguments,
+                                     methodName));
+    }
+
+
+    /**
+     * A field record.
+     */
+    private static class FieldInfo
+    {
+        private String type;
+        private String originalName;
+
+
+        private FieldInfo(String type, String originalName)
+        {
+            this.type         = type;
+            this.originalName = originalName;
+        }
+
+
+        private boolean matches(String type)
+        {
+            return
+                type == null || type.equals(this.type);
+        }
+    }
+
+
+    /**
+     * A method record.
+     */
+    private static class MethodInfo
+    {
+        private int    firstLineNumber;
+        private int    lastLineNumber;
+        private String type;
+        private String arguments;
+        private String originalName;
+
+
+        private MethodInfo(int firstLineNumber, int lastLineNumber, String type, String arguments, String originalName)
+        {
+            this.firstLineNumber = firstLineNumber;
+            this.lastLineNumber  = lastLineNumber;
+            this.type            = type;
+            this.arguments       = arguments;
+            this.originalName    = originalName;
+        }
+
+
+        private boolean matches(int lineNumber, String type, String arguments)
+        {
+            return
+                (lineNumber == 0    || (firstLineNumber <= lineNumber && lineNumber <= lastLineNumber) || lastLineNumber == 0) &&
+                (type       == null || type.equals(this.type))                                                                 &&
+                (arguments  == null || arguments.equals(this.arguments));
+        }
     }
 
 
@@ -105,19 +687,33 @@ public class ReTrace
             System.exit(-1);
         }
 
-        int argumentIndex = 0;
+        String  regularExpresssion = STACK_TRACE_EXPRESSION;
+        boolean verbose            = false;
 
-        boolean verbose = false;
-        if (args[argumentIndex].equals(VERBOSE_OPTION))
+        int argumentIndex = 0;
+        while (argumentIndex < args.length)
         {
-            verbose = true;
-            argumentIndex++;
-
-            if (args.length < 2)
+            String arg = args[argumentIndex];
+            if (arg.equals(REGEX_OPTION))
+            {
+                regularExpresssion = args[++argumentIndex];
+            }
+            else if (arg.equals(VERBOSE_OPTION))
+            {
+                verbose = true;
+            }
+            else
             {
-                System.err.println("Usage: java proguard.ReTrace [-verbose] <mapping_file> [<stacktrace_file>]");
-                System.exit(-1);
+                break;
             }
+
+            argumentIndex++;
+        }
+
+        if (argumentIndex >= args.length)
+        {
+            System.err.println("Usage: java proguard.ReTrace [-regex <regex>] [-verbose] <mapping_file> [<stacktrace_file>]");
+            System.exit(-1);
         }
 
         File mappingFile    = new File(args[argumentIndex++]);
@@ -125,7 +721,7 @@ public class ReTrace
             new File(args[argumentIndex]) :
             null;
 
-        ReTrace reTrace = new ReTrace(verbose, mappingFile, stackTraceFile);
+        ReTrace reTrace = new ReTrace(regularExpresssion, verbose, mappingFile, stackTraceFile);
 
         try
         {
diff --git a/src/proguard/retrace/StackTrace.java b/src/proguard/retrace/StackTrace.java
deleted file mode 100644
index 791f21a..0000000
--- a/src/proguard/retrace/StackTrace.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 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.retrace;
-
-import proguard.obfuscate.MappingProcessor;
-
-import java.io.*;
-import java.util.*;
-
-
-/**
- * This class represents an obfuscated stack trace. It can read, de-obfuscate,
- * and then write its contents.
- *
- * @author Eric Lafortune
- */
-final class StackTrace implements MappingProcessor
-{
-    // The stack trace settings.
-    private final boolean verbose;
-
-    // The stack trace items.
-    private final List stackTraceItems = new ArrayList();
-
-
-    /**
-     * Creates a new StackTrace.
-     * @param verbose specifies whether the de-obfuscated stack trace should
-     *                be verbose.
-     */
-    public StackTrace(boolean verbose)
-    {
-        this.verbose = verbose;
-    }
-
-
-    /**
-     * Reads the stack trace file.
-     */
-    public void read(File stackTraceFile) throws IOException
-    {
-        LineNumberReader lineNumberReader = null;
-
-        try
-        {
-            Reader reader = stackTraceFile == null ?
-                (Reader)new InputStreamReader(System.in) :
-                (Reader)new BufferedReader(new FileReader(stackTraceFile));
-
-            lineNumberReader = new LineNumberReader(reader);
-
-            // Read the line in the stack trace.
-            while (true)
-            {
-                String line = lineNumberReader.readLine();
-                if (line == null)
-                {
-                    break;
-                }
-
-                line = line.trim();
-                if (line.length() == 0)
-                {
-                    continue;
-                }
-
-                // Put the line in a stack trace item.
-                StackTraceItem item = new StackTraceItem(verbose);
-
-                item.parse(line);
-
-                stackTraceItems.add(item);
-            }
-        }
-        catch (IOException ex)
-        {
-            throw new IOException("Can't read stack trace (" + ex.getMessage() + ")");
-        }
-        finally
-        {
-            if (stackTraceFile != null &&
-                lineNumberReader != null)
-            {
-                try
-                {
-                    lineNumberReader.close();
-                }
-                catch (IOException ex)
-                {
-                    // This shouldn't happen.
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Prints out the de-obfuscated stack trace.
-     */
-    public void print()
-    {
-        // Delegate to each of the stack trace items.
-        for (int index = 0; index < stackTraceItems.size(); index++)
-        {
-            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
-
-            item.print();
-        }
-    }
-
-
-    // Implementations for MappingProcessor.
-
-    public boolean processClassMapping(String className,
-                                       String newClassName)
-    {
-        // Delegate to each of the stack trace items.
-        boolean present = false;
-        for (int index = 0; index < stackTraceItems.size(); index++)
-        {
-            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
-
-            present |= item.processClassMapping(className,
-                                                newClassName);
-        }
-
-        return present;
-    }
-
-
-    public void processFieldMapping(String className,
-                                    String fieldType,
-                                    String fieldName,
-                                    String newFieldName)
-    {
-        // A stack trace never contains any fields.
-    }
-
-
-    public void processMethodMapping(String className,
-                                     int    firstLineNumber,
-                                     int    lastLineNumber,
-                                     String methodReturnType,
-                                     String methodNameAndArguments,
-                                     String newMethodName)
-    {
-        // Delegate to each of the stack trace items.
-        for (int index = 0; index < stackTraceItems.size(); index++)
-        {
-            StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
-
-            item.processMethodMapping(className,
-                                      firstLineNumber,
-                                      lastLineNumber,
-                                      methodReturnType,
-                                      methodNameAndArguments,
-                                      newMethodName);
-        }
-    }
-}
diff --git a/src/proguard/retrace/StackTraceItem.java b/src/proguard/retrace/StackTraceItem.java
deleted file mode 100644
index 16d13ae..0000000
--- a/src/proguard/retrace/StackTraceItem.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- *             of Java bytecode.
- *
- * Copyright (c) 2002-2008 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.retrace;
-
-import proguard.obfuscate.MappingProcessor;
-
-import java.io.IOException;
-import java.util.*;
-
-
-/**
- * This class represents an obfuscated stack trace item. It can read, de-obfuscate,
- * and then write its contents.
- *
- * @author Eric Lafortune
- */
-final class StackTraceItem implements MappingProcessor
-{
-    // The stack trace settings.
-    private final boolean verbose;
-
-    public String prefix;
-    public String obfuscatedClassName;
-    public String obfuscatedMethodName;
-    public String sourceFile;
-    public int    lineNumber;
-    public String suffix;
-
-    public String originalClassName;
-    public List   originalMethodNames;
-
-    /**
-     * Creates a new StackTraceItem.
-     * @param verbose specifies whether the de-obfuscated stack trace should
-     *                be verbose.
-     */
-    public StackTraceItem(boolean verbose)
-    {
-        this.verbose = verbose;
-    }
-
-
-    /**
-     * Parses the stack trace
-     */
-    public void parse(String line) throws IOException
-    {
-        if (!parseAtLine(line) &&
-            !parseExceptionInThreadLine(line))
-        {
-            parseAnyLine(line);
-        }
-    }
-
-
-    /**
-     * Tries to parse "at ___.___(___:___)", containing the class name,
-     * the method name, the source file, and the optional line number.
-     */
-    private boolean parseAtLine(String line)
-    {
-        if (!line.startsWith("at "))
-        {
-            return false;
-        }
-
-        int openParenthesisIndex = line.indexOf('(', 3);
-        if (openParenthesisIndex < 0)
-        {
-            return false;
-        }
-
-        int colonIndex = line.indexOf(':', openParenthesisIndex + 1);
-
-        int closeParenthesisIndex = line.indexOf(')', Math.max(openParenthesisIndex, colonIndex) + 1);
-        if (closeParenthesisIndex < 0)
-        {
-            return false;
-        }
-
-        int periodIndex = line.lastIndexOf('.', openParenthesisIndex - 1);
-        if (periodIndex < 0)
-        {
-            return false;
-        }
-
-        prefix               = "        at ";
-        obfuscatedClassName  = line.substring(3, periodIndex).trim();
-        obfuscatedMethodName = line.substring(periodIndex + 1, openParenthesisIndex).trim();
-        sourceFile           = line.substring(openParenthesisIndex + 1, colonIndex < 0 ? closeParenthesisIndex : colonIndex).trim();
-        lineNumber           = colonIndex < 0 ? 0 : Integer.parseInt(line.substring(colonIndex + 1, closeParenthesisIndex).trim());
-
-        return true;
-    }
-
-
-    /**
-     * Tries to parse "Exception in thread "___" ___:___" or just "___:___",
-     * containing the optional thread name, the exception class name and the
-     * exception message.
-     */
-    private boolean parseExceptionInThreadLine(String line)
-    {
-        // Trim away the thread message part, if any.
-        if (line.startsWith("Exception in thread \""))
-        {
-            int quote_index = line.indexOf('"', 21);
-            if (quote_index < 0)
-            {
-                return false;
-            }
-
-            prefix = line.substring(0, quote_index+1) + " ";
-            line   = line.substring(quote_index+1).trim();
-        }
-
-        int colonIndex = line.indexOf(':');
-        if (colonIndex < 0)
-        {
-            return false;
-        }
-
-        int spaceIndex = line.lastIndexOf(' ', colonIndex);
-
-        prefix              = line.substring(0, spaceIndex+1);
-        obfuscatedClassName = line.substring(spaceIndex+1, colonIndex).trim();
-        suffix              = line.substring(colonIndex);
-
-        return true;
-    }
-
-
-    /**
-     * Parses any line.
-     */
-    private void parseAnyLine(String line)
-    {
-        prefix = line;
-    }
-
-
-    /**
-     * Prints out the de-obfuscated stack trace.
-     */
-    public void print()
-    {
-        // Get the original class name, if we found it.
-        String className = originalClassName != null ?
-            originalClassName :
-            obfuscatedClassName;
-
-        // Get the first original method name, if we found it.
-        String methodName = originalMethodNames != null ?
-            (String)originalMethodNames.get(0) :
-            obfuscatedMethodName;
-
-        // Compose the source file with the line number, if any.
-        String source = lineNumber != 0 ?
-            sourceFile + ":" + lineNumber :
-            sourceFile;
-
-        // Print out the resolved stack trace.
-        System.out.print(prefix);
-
-        if (className != null)
-        {
-            System.out.print(className);
-
-            if (methodName != null)
-            {
-                System.out.print("." + methodName + "(" + source + ")");
-
-                // Print out alternatives, if any.
-                if (originalMethodNames != null)
-                {
-                    for (int otherMethodNameIndex = 1; otherMethodNameIndex < originalMethodNames.size(); otherMethodNameIndex++) {
-                        String otherMethodName = (String)originalMethodNames.get(otherMethodNameIndex);
-                        System.out.println();
-                        printSpaces(className.length()+12);
-                        System.out.print(otherMethodName);
-                    }
-                }
-            }
-
-            if (suffix != null)
-            {
-                System.out.print(suffix);
-            }
-        }
-
-        System.out.println();
-    }
-
-
-    /**
-     * Prints the given number of spaces.
-     */
-    private void printSpaces(int aCount)
-    {
-        for (int counter = 0; counter < aCount; counter++)
-          System.out.print(' ');
-    }
-
-
-    // Implementations for MappingProcessor.
-
-    public boolean processClassMapping(String className,
-                                       String newClassName)
-    {
-        boolean present = false;
-
-        if (newClassName.equals(obfuscatedClassName))
-        {
-            originalClassName = className;
-            present = true;
-        }
-
-        return present;
-    }
-
-
-    public void processFieldMapping(String className,
-                                    String fieldType,
-                                    String fieldName,
-                                    String newFieldName)
-    {
-        // A stack trace item never contains any fields.
-    }
-
-
-    public void processMethodMapping(String className,
-                                     int    firstLineNumber,
-                                     int    lastLineNumber,
-                                     String methodReturnType,
-                                     String methodNameAndArguments,
-                                     String newMethodName)
-    {
-        if (className.equals(originalClassName) &&
-            newMethodName.equals(obfuscatedMethodName) &&
-            (lineNumber == 0 ||
-             firstLineNumber == 0 ||
-             lastLineNumber  == 0 ||
-             (firstLineNumber <= lineNumber &&
-              lastLineNumber  >= lineNumber)))
-        {
-            // Create a List for storing solutions for this
-            // method name.
-            if (originalMethodNames == null)
-            {
-                originalMethodNames = new ArrayList();
-            }
-
-            // Does the method have line numbers?
-            if (firstLineNumber != 0 &&
-                lastLineNumber  != 0 &&
-                lineNumber      != 0)
-            {
-                // Then it will be the one and only solution.
-                obfuscatedMethodName = null;
-                originalMethodNames.clear();
-            }
-
-            // Include return type and arguments in the method name if
-            // we're in verbose mode, otherwise strip the arguments.
-            String originalMethodName = verbose ?
-                (methodReturnType + " " + methodNameAndArguments) :
-                methodNameAndArguments.substring(0, methodNameAndArguments.indexOf('('));
-
-            // Add this method name solution to the list.
-            originalMethodNames.add(originalMethodName);
-        }
-    }
-}
diff --git a/src/proguard/shrink/AnnotationUsageMarker.java b/src/proguard/shrink/AnnotationUsageMarker.java
index e4222d2..9aaae34 100644
--- a/src/proguard/shrink/AnnotationUsageMarker.java
+++ b/src/proguard/shrink/AnnotationUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/ClassShrinker.java b/src/proguard/shrink/ClassShrinker.java
index 024a091..0b5c5b7 100644
--- a/src/proguard/shrink/ClassShrinker.java
+++ b/src/proguard/shrink/ClassShrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/InnerUsageMarker.java b/src/proguard/shrink/InnerUsageMarker.java
index 2448ad3..b8ca801 100644
--- a/src/proguard/shrink/InnerUsageMarker.java
+++ b/src/proguard/shrink/InnerUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/InterfaceUsageMarker.java b/src/proguard/shrink/InterfaceUsageMarker.java
index 921e974..7599898 100644
--- a/src/proguard/shrink/InterfaceUsageMarker.java
+++ b/src/proguard/shrink/InterfaceUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/ShortestUsageMark.java b/src/proguard/shrink/ShortestUsageMark.java
index bbac60b..757c713 100644
--- a/src/proguard/shrink/ShortestUsageMark.java
+++ b/src/proguard/shrink/ShortestUsageMark.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/ShortestUsageMarker.java b/src/proguard/shrink/ShortestUsageMarker.java
index f1855ff..da8fad3 100644
--- a/src/proguard/shrink/ShortestUsageMarker.java
+++ b/src/proguard/shrink/ShortestUsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/ShortestUsagePrinter.java b/src/proguard/shrink/ShortestUsagePrinter.java
index 4045f12..db42fe1 100644
--- a/src/proguard/shrink/ShortestUsagePrinter.java
+++ b/src/proguard/shrink/ShortestUsagePrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/Shrinker.java b/src/proguard/shrink/Shrinker.java
index 2d35059..edbc27f 100644
--- a/src/proguard/shrink/Shrinker.java
+++ b/src/proguard/shrink/Shrinker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/UsageMarker.java b/src/proguard/shrink/UsageMarker.java
index b2bed5f..e913046 100644
--- a/src/proguard/shrink/UsageMarker.java
+++ b/src/proguard/shrink/UsageMarker.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/UsagePrinter.java b/src/proguard/shrink/UsagePrinter.java
index 35cd1df..294b9e1 100644
--- a/src/proguard/shrink/UsagePrinter.java
+++ b/src/proguard/shrink/UsagePrinter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/UsedClassFilter.java b/src/proguard/shrink/UsedClassFilter.java
index 563eecc..ec180bd 100644
--- a/src/proguard/shrink/UsedClassFilter.java
+++ b/src/proguard/shrink/UsedClassFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/shrink/UsedMemberFilter.java b/src/proguard/shrink/UsedMemberFilter.java
index 9738415..755cfd1 100644
--- a/src/proguard/shrink/UsedMemberFilter.java
+++ b/src/proguard/shrink/UsedMemberFilter.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/AndMatcher.java b/src/proguard/util/AndMatcher.java
index 6f645fa..94a37e5 100644
--- a/src/proguard/util/AndMatcher.java
+++ b/src/proguard/util/AndMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/ClassNameParser.java b/src/proguard/util/ClassNameParser.java
index 4ae3f1b..ee972f0 100644
--- a/src/proguard/util/ClassNameParser.java
+++ b/src/proguard/util/ClassNameParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/NotMatcher.java b/src/proguard/util/ConstantMatcher.java
similarity index 71%
copy from src/proguard/util/NotMatcher.java
copy to src/proguard/util/ConstantMatcher.java
index 4c11c99..1764caa 100644
--- a/src/proguard/util/NotMatcher.java
+++ b/src/proguard/util/ConstantMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -21,19 +21,21 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings does not match the given
- * StringMatcher.
+ * This StringMatcher matches any string or no string at all.
  *
  * @author Eric Lafortune
  */
-public class NotMatcher implements StringMatcher
+public class ConstantMatcher implements StringMatcher
 {
-    private final StringMatcher matcher;
+    private boolean matches;
 
 
-    public NotMatcher(StringMatcher matcher)
+    /**
+     * Creates a new ConstantMatcher that always returns the given result.
+     */
+    public ConstantMatcher(boolean matches)
     {
-        this.matcher = matcher;
+        this.matches = matches;
     }
 
 
@@ -41,6 +43,6 @@ public class NotMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return !matcher.matches(string);
+        return matches;
     }
-}
+}
\ No newline at end of file
diff --git a/src/proguard/util/EmptyStringMatcher.java b/src/proguard/util/EmptyStringMatcher.java
index 3ff5dfc..543f446 100644
--- a/src/proguard/util/EmptyStringMatcher.java
+++ b/src/proguard/util/EmptyStringMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/ExtensionMatcher.java
index e12da2b..5a9f658 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/ExtensionMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/FileNameParser.java b/src/proguard/util/FileNameParser.java
index 2c34fa0..913f22d 100644
--- a/src/proguard/util/FileNameParser.java
+++ b/src/proguard/util/FileNameParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/FixedStringMatcher.java b/src/proguard/util/FixedStringMatcher.java
index de94763..c1eb3f4 100644
--- a/src/proguard/util/FixedStringMatcher.java
+++ b/src/proguard/util/FixedStringMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/ListMatcher.java b/src/proguard/util/ListMatcher.java
index c573fe3..b2559c8 100644
--- a/src/proguard/util/ListMatcher.java
+++ b/src/proguard/util/ListMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/ListParser.java b/src/proguard/util/ListParser.java
index f411319..cec803b 100644
--- a/src/proguard/util/ListParser.java
+++ b/src/proguard/util/ListParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -37,6 +37,10 @@ public class ListParser implements StringParser
     private final StringParser stringParser;
 
 
+    /**
+     * Creates a new ListParser that parses individual elements in the
+     * comma-separated list with the given StringParser.
+     */
     public ListParser(StringParser stringParser)
     {
         this.stringParser = stringParser;
@@ -52,11 +56,11 @@ public class ListParser implements StringParser
     }
 
 
-    // Small utility methods.
-
     /**
      * Creates a StringMatcher for the given regular expression, which can
      * be a list of optionally negated simple entries.
+     * <p>
+     * An empty list results in a StringMatcher that matches any string.
      */
     public StringMatcher parse(List regularExpressions)
     {
@@ -79,10 +83,12 @@ public class ListParser implements StringParser
                     (StringMatcher)new OrMatcher(entryMatcher, listMatcher);
         }
 
-        return listMatcher;
+        return listMatcher != null ? listMatcher : new ConstantMatcher(true);
     }
 
 
+    // Small utility methods.
+
     /**
      * Creates a StringMatcher for the given regular expression, which is a
      * an optionally negated simple expression.
diff --git a/src/proguard/util/ListUtil.java b/src/proguard/util/ListUtil.java
index 37aae61..570dbe8 100644
--- a/src/proguard/util/ListUtil.java
+++ b/src/proguard/util/ListUtil.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
@@ -50,7 +50,7 @@ public class ListUtil
                 buffer.append(',');
             }
 
-            buffer.append(list.get(index));
+            buffer.append(quotedString((String)list.get(index)));
         }
 
         return buffer.toString();
@@ -69,19 +69,105 @@ public class ListUtil
 
         List list = new ArrayList();
         int index = 0;
-        while (index < string.length())
+        while ((index = skipWhitespace(string, index)) < string.length())
         {
-            int nextIndex = string.indexOf(',', index);
-            if (nextIndex < 0)
+            int nextIndex;
+
+            // Do we have an opening quote?
+            if (string.charAt(index) == '\'')
             {
-                nextIndex = string.length();
+                // Parse a quoted string.
+                nextIndex = string.indexOf('\'', index + 1);
+                if (nextIndex < 0)
+                {
+                    nextIndex = string.length();
+                }
+
+                list.add(string.substring(index + 1, nextIndex));
             }
+            else
+            {
+                // Parse a non-quoted string.
+                nextIndex = string.indexOf(',', index);
+                if (nextIndex < 0)
+                {
+                    nextIndex = string.length();
+                }
 
-            list.add(string.substring(index, nextIndex).trim());
+                String substring = string.substring(index, nextIndex).trim();
+                if (substring.length() > 0)
+                {
+                    list.add(substring);
+                }
+            }
 
             index = nextIndex + 1;
         }
 
         return list;
     }
+
+
+    /**
+     * Skips any whitespace characters.
+     */
+    private static int skipWhitespace(String string, int index)
+    {
+        while (index < string.length() &&
+               Character.isWhitespace(string.charAt(index)))
+        {
+            index++;
+        }
+        return index;
+    }
+
+
+    /**
+     * Returns a quoted version of the given string, if necessary.
+     */
+    private static String quotedString(String string)
+    {
+        return string.length()     == 0 ||
+               string.indexOf(' ') >= 0 ||
+               string.indexOf('@') >= 0 ||
+               string.indexOf('{') >= 0 ||
+               string.indexOf('}') >= 0 ||
+               string.indexOf('(') >= 0 ||
+               string.indexOf(')') >= 0 ||
+               string.indexOf(':') >= 0 ||
+               string.indexOf(';') >= 0 ||
+               string.indexOf(',') >= 0  ? ("'" + string + "'") :
+                                           (      string      );
+    }
+
+
+    public static void main(String[] args)
+    {
+        if (args.length == 1)
+        {
+            System.out.println("Input string: ["+args[0]+"]");
+
+            List list = commaSeparatedList(args[0]);
+
+            System.out.println("Resulting list:");
+            for (int index = 0; index < list.size(); index++)
+            {
+                System.out.println("["+list.get(index)+"]");
+            }
+        }
+        else
+        {
+            List list = Arrays.asList(args);
+
+            System.out.println("Input list:");
+            for (int index = 0; index < list.size(); index++)
+            {
+                System.out.println("["+list.get(index)+"]");
+            }
+
+            String string = commaSeparatedString(list);
+
+            System.out.println("Resulting string: ["+string+"]");
+        }
+    }
 }
diff --git a/src/proguard/util/NameParser.java b/src/proguard/util/NameParser.java
index beda426..e311fbf 100644
--- a/src/proguard/util/NameParser.java
+++ b/src/proguard/util/NameParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/NotMatcher.java b/src/proguard/util/NotMatcher.java
index 4c11c99..f2a9a51 100644
--- a/src/proguard/util/NotMatcher.java
+++ b/src/proguard/util/NotMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/OrMatcher.java b/src/proguard/util/OrMatcher.java
index 2a8e625..097c6c6 100644
--- a/src/proguard/util/OrMatcher.java
+++ b/src/proguard/util/OrMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/SettableMatcher.java b/src/proguard/util/SettableMatcher.java
index c7ab5c2..9557f62 100644
--- a/src/proguard/util/SettableMatcher.java
+++ b/src/proguard/util/SettableMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/StringMatcher.java b/src/proguard/util/StringMatcher.java
index 57a9f10..bd66dcc 100644
--- a/src/proguard/util/StringMatcher.java
+++ b/src/proguard/util/StringMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/StringParser.java b/src/proguard/util/StringParser.java
index 6d635ba..29f4f16 100644
--- a/src/proguard/util/StringParser.java
+++ b/src/proguard/util/StringParser.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/util/VariableStringMatcher.java b/src/proguard/util/VariableStringMatcher.java
index 526ab4f..1e41323 100644
--- a/src/proguard/util/VariableStringMatcher.java
+++ b/src/proguard/util/VariableStringMatcher.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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
diff --git a/src/proguard/wtk/ProGuardObfuscator.java b/src/proguard/wtk/ProGuardObfuscator.java
index e8665b5..4618437 100644
--- a/src/proguard/wtk/ProGuardObfuscator.java
+++ b/src/proguard/wtk/ProGuardObfuscator.java
@@ -2,7 +2,7 @@
  * ProGuard -- shrinking, optimization, obfuscation, and preverification
  *             of Java bytecode.
  *
- * Copyright (c) 2002-2008 Eric Lafortune (eric at graphics.cornell.edu)
+ * Copyright (c) 2002-2009 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

-- 
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