[proguard] 01/02: Imported Upstream version 4.1

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


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

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

commit b983ca15da351bbb3e19edcc9cfe0fceaf2683ce
Author: Sam Clegg <samo at debian.org>
Date:   Thu Apr 10 10:41:57 2014 +0200

    Imported Upstream version 4.1
---
 README                                             |    2 +-
 docs/FAQ.html                                      |  126 +-
 docs/GPL_exception.html                            |    6 +-
 docs/acknowledgements.html                         |   16 +-
 docs/alternatives.html                             |  109 +-
 docs/downloads.html                                |   49 +-
 docs/feedback.html                                 |   59 +-
 docs/index.html                                    |    4 +-
 docs/license.html                                  |   18 +-
 docs/main.html                                     |   40 +-
 docs/manual/ant.html                               |  208 +-
 docs/manual/examples.html                          |  526 +++-
 docs/manual/gui.html                               |   84 +-
 docs/manual/index.html                             |    2 +-
 docs/manual/introduction.html                      |  120 +-
 docs/manual/limitations.html                       |   49 +-
 docs/manual/refcard.html                           |  121 +-
 docs/manual/retrace/introduction.html              |   12 +-
 docs/manual/sections.html                          |    6 +-
 docs/manual/troubleshooting.html                   |  176 +-
 docs/manual/usage.html                             |  341 ++-
 docs/manual/wtk.html                               |   10 +-
 docs/quality.html                                  |    2 +-
 docs/results.html                                  |  137 +-
 docs/sanawarelogo.png                              |  Bin 0 -> 8552 bytes
 docs/screenshot_console.gif                        |  Bin 13199 -> 18930 bytes
 docs/screenshot_console_small.gif                  |  Bin 87201 -> 19730 bytes
 docs/screenshot_gui1.gif                           |  Bin 38674 -> 42755 bytes
 docs/screenshot_gui2.gif                           |  Bin 30691 -> 36887 bytes
 docs/screenshot_gui3.gif                           |  Bin 29364 -> 37080 bytes
 docs/screenshot_gui4.gif                           |  Bin 30409 -> 40769 bytes
 docs/screenshot_gui5.gif                           |  Bin 28754 -> 35909 bytes
 docs/screenshot_gui6.gif                           |  Bin 23939 -> 37123 bytes
 docs/screenshot_gui7.gif                           |  Bin 29404 -> 41922 bytes
 docs/screenshot_gui8.gif                           |  Bin 26259 -> 31454 bytes
 docs/screenshots_gui_small.gif                     |  Bin 90544 -> 161022 bytes
 docs/sections.html                                 |    3 +
 docs/testimonials.html                             |   17 +-
 docs/title.html                                    |    2 +-
 .../{applications.pro => annotations/examples.pro} |   38 +-
 examples/annotations/examples/Applet.java          |   20 +
 examples/annotations/examples/Application.java     |   18 +
 examples/annotations/examples/Bean.java            |   54 +
 examples/annotations/examples/NativeCallBack.java  |   42 +
 examples/annotations/lib/annotations.jar           |  Bin 0 -> 6123 bytes
 examples/annotations/lib/annotations.pro           |  118 +
 .../annotations/src/proguard/annotation/Keep.java  |   18 +
 .../src/proguard/annotation/KeepApplication.java   |   18 +
 .../proguard/annotation/KeepClassMemberNames.java  |   18 +
 .../src/proguard/annotation/KeepClassMembers.java  |   18 +
 .../proguard/annotation/KeepGettersSetters.java    |   18 +
 .../proguard/annotation/KeepImplementations.java   |   18 +
 .../src/proguard/annotation/KeepName.java          |   18 +
 .../annotation/KeepPublicClassMemberNames.java     |   18 +
 .../annotation/KeepPublicClassMembers.java         |   18 +
 .../annotation/KeepPublicGettersSetters.java       |   18 +
 .../annotation/KeepPublicImplementations.java      |   18 +
 .../KeepPublicProtectedClassMemberNames.java       |   18 +
 .../KeepPublicProtectedClassMembers.java           |   19 +
 examples/ant/applets.xml                           |    7 +-
 examples/ant/applications2.xml                     |    1 +
 examples/ant/applications3.xml                     |    7 +-
 examples/ant/library.xml                           |    5 +-
 examples/ant/midlets.xml                           |    5 +-
 examples/ant/proguard.xml                          |    9 +-
 examples/ant/servlets.xml                          |    7 +-
 examples/applets.pro                               |    1 +
 examples/applications.pro                          |    1 +
 examples/dictionaries/compact.txt                  |    1 -
 examples/dictionaries/shakespeare.txt              |    0
 examples/library.pro                               |    4 +-
 examples/midlets.pro                               |    6 +-
 examples/proguard.pro                              |    9 +-
 examples/proguardall.pro                           |   20 +-
 examples/proguardgui.pro                           |   13 +-
 examples/retrace.pro                               |    2 +-
 examples/servlets.pro                              |    1 +
 lib/proguard.jar                                   |  Bin 429304 -> 0 bytes
 lib/proguardgui.jar                                |  Bin 125680 -> 0 bytes
 lib/retrace.jar                                    |  Bin 5110 -> 0 bytes
 src/proguard/ArgumentWordReader.java               |    8 +-
 src/proguard/ClassPath.java                        |    8 +-
 src/proguard/ClassPathEntry.java                   |    8 +-
 src/proguard/ClassSpecification.java               |  211 +-
 src/proguard/ClassSpecificationVisitorFactory.java |  494 ++--
 src/proguard/Configuration.java                    |  112 +-
 src/proguard/ConfigurationConstants.java           |  115 +-
 src/proguard/ConfigurationParser.java              |  455 +++-
 src/proguard/ConfigurationWriter.java              |  317 ++-
 src/proguard/DataEntryReaderFactory.java           |   37 +-
 src/proguard/DataEntryWriterFactory.java           |   38 +-
 src/proguard/DescriptorKeepChecker.java            |  161 ++
 ...FilePrinter.java => DuplicateClassPrinter.java} |   28 +-
 src/proguard/FileWordReader.java                   |   12 +-
 src/proguard/FullyQualifiedClassNameChecker.java   |  188 ++
 src/proguard/GPL.java                              |    8 +-
 src/proguard/Initializer.java                      |  288 +-
 src/proguard/InputReader.java                      |   54 +-
 src/proguard/KeepSpecification.java                |  138 +
 ...Specification.java => MemberSpecification.java} |   55 +-
 src/proguard/OutputWriter.java                     |   53 +-
 src/proguard/ParseException.java                   |   10 +-
 src/proguard/ProGuard.java                         |  308 ++-
 src/proguard/SubclassedClassFileFilter.java        |   63 -
 ...FileVisitor.java => SubclassedClassFilter.java} |   41 +-
 src/proguard/Targeter.java                         |   88 +
 src/proguard/UpToDateChecker.java                  |  153 ++
 src/proguard/WordReader.java                       |    7 +-
 src/proguard/ant/ClassPathElement.java             |   33 +-
 src/proguard/ant/ClassSpecificationElement.java    |  102 +-
 src/proguard/ant/ConfigurationElement.java         |   18 +-
 src/proguard/ant/ConfigurationTask.java            |  182 +-
 ...eepAttributeElement.java => FilterElement.java} |   45 +-
 src/proguard/ant/KeepAttributeElement.java         |   10 +-
 src/proguard/ant/KeepSpecificationElement.java     |   87 +
 ...lement.java => MemberSpecificationElement.java} |   66 +-
 src/proguard/ant/ProGuardTask.java                 |   72 +-
 src/proguard/classfile/ClassConstants.java         |  147 +-
 src/proguard/classfile/ClassCpInfo.java            |  123 -
 src/proguard/classfile/ClassPool.java              |   73 +-
 .../classfile/{ClassFile.java => Clazz.java}       |  150 +-
 src/proguard/classfile/CpInfo.java                 |  166 --
 src/proguard/classfile/DoubleCpInfo.java           |   97 -
 .../classfile/{FieldInfo.java => Field.java}       |   11 +-
 src/proguard/classfile/FieldrefCpInfo.java         |   69 -
 src/proguard/classfile/FloatCpInfo.java            |   92 -
 src/proguard/classfile/IntegerCpInfo.java          |   92 -
 .../classfile/InterfaceMethodrefCpInfo.java        |   69 -
 src/proguard/classfile/LibraryClass.java           |  553 ++++
 src/proguard/classfile/LibraryClassFile.java       |  743 -----
 src/proguard/classfile/LibraryField.java           |   66 +
 src/proguard/classfile/LibraryFieldInfo.java       |   80 -
 src/proguard/classfile/LibraryMember.java          |   90 +
 src/proguard/classfile/LibraryMemberInfo.java      |  129 -
 src/proguard/classfile/LibraryMethod.java          |   72 +
 src/proguard/classfile/LibraryMethodInfo.java      |   86 -
 src/proguard/classfile/LongCpInfo.java             |   96 -
 .../classfile/{MemberInfo.java => Member.java}     |   24 +-
 .../classfile/{MethodInfo.java => Method.java}     |   11 +-
 src/proguard/classfile/MethodrefCpInfo.java        |   69 -
 src/proguard/classfile/ProgramClass.java           |  587 ++++
 src/proguard/classfile/ProgramClassFile.java       |  706 -----
 src/proguard/classfile/ProgramField.java           |   76 +
 src/proguard/classfile/ProgramFieldInfo.java       |   90 -
 src/proguard/classfile/ProgramMember.java          |  146 +
 src/proguard/classfile/ProgramMemberInfo.java      |  184 --
 src/proguard/classfile/ProgramMethod.java          |   82 +
 src/proguard/classfile/ProgramMethodInfo.java      |   96 -
 src/proguard/classfile/StringCpInfo.java           |  108 -
 src/proguard/classfile/VisitorAccepter.java        |    7 +-
 .../classfile/attribute/AllAttrInfoVisitor.java    |  127 -
 src/proguard/classfile/attribute/AttrInfo.java     |  184 --
 .../classfile/attribute/AttrInfoVisitor.java       |   58 -
 src/proguard/classfile/attribute/Attribute.java    |  125 +
 src/proguard/classfile/attribute/CodeAttrInfo.java |  188 --
 .../classfile/attribute/CodeAttribute.java         |  176 ++
 .../classfile/attribute/ConstantValueAttrInfo.java |   70 -
 .../ConstantValueAttribute.java}                   |   33 +-
 .../classfile/attribute/DeprecatedAttrInfo.java    |   65 -
 .../classfile/attribute/DeprecatedAttribute.java   |   57 +
 .../attribute/EnclosingMethodAttrInfo.java         |  135 -
 .../attribute/EnclosingMethodAttribute.java        |  118 +
 .../classfile/attribute/ExceptionInfo.java         |   75 +-
 .../classfile/attribute/ExceptionsAttrInfo.java    |   99 -
 .../classfile/attribute/ExceptionsAttribute.java   |   66 +
 .../classfile/attribute/InnerClassesAttrInfo.java  |  104 -
 .../classfile/attribute/InnerClassesAttribute.java |   65 +
 .../classfile/attribute/InnerClassesInfo.java      |   53 +-
 .../classfile/attribute/LibraryAttrInfo.java       |   45 -
 .../classfile/attribute/LineNumberInfo.java        |   42 +-
 .../attribute/LineNumberTableAttrInfo.java         |  116 -
 .../attribute/LineNumberTableAttribute.java        |   86 +
 .../classfile/attribute/LocalVariableInfo.java     |   94 +-
 .../attribute/LocalVariableTableAttrInfo.java      |  102 -
 .../attribute/LocalVariableTableAttribute.java     |   65 +
 .../classfile/attribute/LocalVariableTypeInfo.java |  101 +-
 .../attribute/LocalVariableTypeTableAttrInfo.java  |  102 -
 .../attribute/LocalVariableTypeTableAttribute.java |   65 +
 .../classfile/attribute/MultiAttrInfoVisitor.java  |  234 --
 .../classfile/attribute/SignatureAttrInfo.java     |   95 -
 .../classfile/attribute/SignatureAttribute.java    |   88 +
 .../classfile/attribute/SourceDirAttrInfo.java     |   69 -
 .../SourceDirAttribute.java}                       |   33 +-
 .../classfile/attribute/SourceFileAttrInfo.java    |   70 -
 .../SourceFileAttribute.java}                      |   33 +-
 .../classfile/attribute/SyntheticAttrInfo.java     |   65 -
 .../classfile/attribute/SyntheticAttribute.java    |   57 +
 .../classfile/attribute/UnknownAttrInfo.java       |   70 -
 .../classfile/attribute/UnknownAttribute.java      |   68 +
 .../classfile/attribute/annotation/Annotation.java |  113 +-
 .../annotation/AnnotationDefaultAttrInfo.java      |   77 -
 .../annotation/AnnotationDefaultAttribute.java     |   61 +
 .../annotation/AnnotationElementValue.java         |   48 +-
 .../attribute/annotation/AnnotationsAttribute.java |   83 +
 .../attribute/annotation/ArrayElementValue.java    |   69 +-
 .../attribute/annotation/ClassElementValue.java    |   64 +-
 .../attribute/annotation/ConstantElementValue.java |   48 +-
 .../attribute/annotation/ElementValue.java         |  122 +-
 .../attribute/annotation/ElementValueVisitor.java  |   40 -
 .../annotation/EnumConstantElementValue.java       |   71 +-
 .../annotation/ParameterAnnotationsAttribute.java  |   64 +
 .../annotation/RuntimeAnnotationsAttrInfo.java     |   92 -
 .../RuntimeInvisibleAnnotationsAttrInfo.java       |   44 -
 .../RuntimeInvisibleAnnotationsAttribute.java      |   59 +
 ...ntimeInvisibleParameterAnnotationsAttrInfo.java |   44 -
 ...imeInvisibleParameterAnnotationsAttribute.java} |   37 +-
 .../RuntimeParameterAnnotationsAttrInfo.java       |  129 -
 .../RuntimeVisibleAnnotationsAttrInfo.java         |   44 -
 .../RuntimeVisibleAnnotationsAttribute.java        |   59 +
 ...RuntimeVisibleParameterAnnotationsAttrInfo.java |   44 -
 ...ntimeVisibleParameterAnnotationsAttribute.java} |   30 +-
 .../annotation/visitor/AllAnnotationVisitor.java   |  100 +
 .../annotation/visitor/AnnotatedClassVisitor.java  |   62 +
 .../visitor/AnnotationToMemberVisitor.java         |   62 +
 .../annotation/visitor/AnnotationTypeFilter.java   |  102 +
 .../{ => visitor}/AnnotationVisitor.java           |   18 +-
 .../annotation/visitor/ElementValueVisitor.java    |   51 +
 .../attribute/annotation/visitor/package.html      |    3 +
 .../attribute/preverification/DoubleType.java      |   66 +
 .../attribute/preverification/FloatType.java       |   66 +
 .../attribute/preverification/FullFrame.java       |  202 ++
 .../attribute/preverification/IntegerType.java     |   66 +
 .../attribute/preverification/LessZeroFrame.java   |  103 +
 .../attribute/preverification/LongType.java        |   66 +
 .../attribute/preverification/MoreZeroFrame.java   |  161 ++
 .../attribute/preverification/NullType.java        |   66 +
 .../attribute/preverification/ObjectType.java      |  107 +
 .../attribute/preverification/SameOneFrame.java    |  115 +
 .../attribute/preverification/SameZeroFrame.java   |   74 +
 .../preverification/StackMapAttribute.java         |   91 +
 .../attribute/preverification/StackMapFrame.java   |  117 +
 .../preverification/StackMapTableAttribute.java    |   93 +
 .../attribute/preverification/TopType.java         |   66 +
 .../preverification/UninitializedThisType.java     |   66 +
 .../preverification/UninitializedType.java         |  106 +
 .../preverification/VerificationType.java          |  103 +
 .../preverification/VerificationTypeFactory.java   |  112 +
 .../visitor/StackMapFrameVisitor.java              |   40 +
 .../visitor/VerificationTypeVisitor.java           |   65 +
 .../attribute/visitor/AllAttributeVisitor.java     |  117 +
 .../attribute/visitor/AttributeNameFilter.java     |  345 +++
 .../attribute/visitor/AttributeVisitor.java        |   89 +
 .../{ => visitor}/ExceptionInfoVisitor.java        |   11 +-
 .../{ => visitor}/InnerClassesInfoVisitor.java     |   14 +-
 .../visitor/LineNumberInfoVisitor.java             |   10 +-
 .../{ => visitor}/LocalVariableInfoVisitor.java    |   10 +-
 .../LocalVariableTypeInfoVisitor.java              |   10 +-
 .../attribute/visitor/MultiAttributeVisitor.java   |  356 +++
 .../attribute/visitor/RequiredAttributeFilter.java |  351 +++
 .../attribute/visitor/StackSizeComputer.java       |  376 +++
 .../classfile/attribute/visitor/package.html       |    3 +
 src/proguard/classfile/constant/ClassConstant.java |  105 +
 src/proguard/classfile/constant/Constant.java      |   68 +
 .../classfile/constant/DoubleConstant.java         |   82 +
 .../classfile/constant/FieldrefConstant.java       |   71 +
 src/proguard/classfile/constant/FloatConstant.java |   82 +
 .../classfile/constant/IntegerConstant.java        |   82 +
 .../constant/InterfaceMethodrefConstant.java       |   71 +
 src/proguard/classfile/constant/LongConstant.java  |   82 +
 .../classfile/constant/MethodrefConstant.java      |   71 +
 .../NameAndTypeConstant.java}                      |   56 +-
 .../{RefCpInfo.java => constant/RefConstant.java}  |   76 +-
 .../classfile/constant/StringConstant.java         |  135 +
 .../Utf8Constant.java}                             |  100 +-
 .../visitor/AllConstantVisitor.java}               |   36 +-
 .../constant/visitor/ConstantVisitor.java          |   46 +
 .../classfile/constant/visitor/package.html        |    3 +
 src/proguard/classfile/editor/AccessFixer.java     |  138 +
 src/proguard/classfile/editor/AttributeAdder.java  |  293 ++
 src/proguard/classfile/editor/AttributeSorter.java |   89 +
 .../classfile/editor/AttributesEditor.java         |  270 ++
 .../classfile/editor/ClassElementSorter.java       |   52 +
 .../classfile/editor/ClassFileReferenceFixer.java  |  542 ----
 .../classfile/editor/ClassMemberSorter.java        |   69 +
 .../classfile/editor/ClassReferenceFixer.java      |  548 ++++
 .../editor/CodeAttrInfoEditorResetter.java         |   76 -
 .../classfile/editor/CodeAttributeComposer.java    |  729 +++++
 ...ttrInfoEditor.java => CodeAttributeEditor.java} |  530 ++--
 .../editor/CodeAttributeEditorResetter.java        |   60 +
 .../classfile/editor/ComparableConstant.java       |  182 ++
 .../classfile/editor/ComparableCpInfo.java         |  177 --
 src/proguard/classfile/editor/ConstantAdder.java   |  190 ++
 .../classfile/editor/ConstantPoolEditor.java       |  796 +++---
 .../classfile/editor/ConstantPoolRemapper.java     |  546 ++--
 .../classfile/editor/ConstantPoolSorter.java       |   90 +-
 src/proguard/classfile/editor/ExceptionAdder.java  |   67 +
 .../classfile/editor/ExceptionsEditor.java         |   55 +
 .../classfile/editor/InstructionWriter.java        |  139 +-
 .../InterfaceSorter.java}                          |   28 +-
 src/proguard/classfile/editor/MemberAdder.java     |  141 +
 .../classfile/editor/MemberReferenceFixer.java     |  393 ++-
 src/proguard/classfile/editor/MembersEditor.java   |   92 +
 .../classfile/editor/MethodInvocationFixer.java    |  254 +-
 .../classfile/editor/StackSizeUpdater.java         |  345 +--
 src/proguard/classfile/editor/VariableEditor.java  |   84 +-
 .../classfile/editor/VariableRemapper.java         |   98 +-
 .../classfile/editor/VariableSizeUpdater.java      |  102 +
 .../instruction/AllInstructionVisitor.java         |   71 -
 .../classfile/instruction/BranchInstruction.java   |   34 +-
 ...CpInstruction.java => ConstantInstruction.java} |  160 +-
 .../classfile/instruction/Instruction.java         |   87 +-
 .../instruction/InstructionConstants.java          |  424 +--
 .../classfile/instruction/InstructionCounter.java  |  106 -
 .../classfile/instruction/InstructionFactory.java  |   31 +-
 .../classfile/instruction/InstructionUtil.java     |   67 +
 .../classfile/instruction/InstructionVisitor.java  |   41 -
 .../instruction/LookUpSwitchInstruction.java       |   79 +-
 .../classfile/instruction/SimpleInstruction.java   |  167 +-
 .../classfile/instruction/SwitchInstruction.java   |   83 +
 .../instruction/TableSwitchInstruction.java        |   76 +-
 .../classfile/instruction/VariableInstruction.java |  127 +-
 .../instruction/visitor/AllInstructionVisitor.java |   56 +
 .../visitor/InstructionCounter.java}               |   34 +-
 .../instruction/visitor/InstructionVisitor.java    |   42 +
 .../{ => visitor}/MultiInstructionVisitor.java     |   55 +-
 .../classfile/instruction/visitor/package.html     |    3 +
 src/proguard/classfile/io/LibraryClassReader.java  |  362 +++
 src/proguard/classfile/io/ProgramClassReader.java  |  850 ++++++
 src/proguard/classfile/io/ProgramClassWriter.java  |  692 +++++
 src/proguard/classfile/io/RuntimeDataInput.java    |  223 ++
 src/proguard/classfile/io/RuntimeDataOutput.java   |  224 ++
 src/proguard/classfile/io/package.html             |    3 +
 src/proguard/classfile/util/AccessUtil.java        |   17 +-
 .../ClassFileClassForNameReferenceInitializer.java |  304 ---
 .../util/ClassFileHierarchyInitializer.java        |  256 --
 .../util/ClassFileReferenceInitializer.java        |  519 ----
 .../classfile/util/ClassForNameChecker.java        |  227 --
 .../classfile/util/ClassNewInstanceChecker.java    |   97 -
 .../classfile/util/ClassReferenceInitializer.java  |  494 ++++
 .../util/ClassSubHierarchyInitializer.java         |   77 +
 .../util/ClassSuperHierarchyInitializer.java       |  159 ++
 src/proguard/classfile/util/ClassUtil.java         |  288 +-
 .../classfile/util/DescriptorClassEnumeration.java |   12 +-
 .../util/DynamicClassReferenceInitializer.java     |  440 +++
 .../util/DynamicMemberReferenceInitializer.java    |  590 ++++
 .../classfile/util/ExternalTypeEnumeration.java    |    8 +-
 .../classfile/util/InstructionSequenceMatcher.java |  624 +++++
 .../classfile/util/InternalTypeEnumeration.java    |   12 +-
 src/proguard/classfile/util/MemberFinder.java      |  174 +-
 src/proguard/classfile/util/MethodInfoLinker.java  |  197 --
 src/proguard/classfile/util/MethodLinker.java      |  154 ++
 src/proguard/classfile/util/SimplifiedVisitor.java |  810 ++++++
 src/proguard/classfile/util/StringSharer.java      |  155 ++
 src/proguard/classfile/util/WarningPrinter.java    |   13 +-
 ...lClassFileVisitor.java => AllClassVisitor.java} |   20 +-
 .../classfile/visitor/AllFieldVisitor.java         |   28 +-
 .../classfile/visitor/AllMemberInfoVisitor.java    |   57 -
 .../{ClassCounter.java => AllMemberVisitor.java}   |   34 +-
 .../classfile/visitor/AllMethodVisitor.java        |   28 +-
 ...ClassFileFilter.java => BottomClassFilter.java} |   36 +-
 ...ileAccessFilter.java => ClassAccessFilter.java} |   44 +-
 src/proguard/classfile/visitor/ClassCleaner.java   |  275 ++
 .../ClassCollector.java}                           |   39 +-
 src/proguard/classfile/visitor/ClassCounter.java   |   17 +-
 .../classfile/visitor/ClassFileCleaner.java        |  368 ---
 .../visitor/ClassFileHierarchyTraveler.java        |   91 -
 .../visitor/ClassFileMemberInfoVisitor.java        |   88 -
 .../classfile/visitor/ClassFileNameFilter.java     |   81 -
 .../classfile/visitor/ClassFilePresenceFilter.java |   95 -
 .../classfile/visitor/ClassFilePrinter.java        |  680 -----
 .../classfile/visitor/ClassFileVersionCounter.java |   79 -
 .../visitor/ClassForNameClassFileVisitor.java      |   73 -
 .../visitor/ClassForNameClassVisitor.java          |   66 +
 .../classfile/visitor/ClassHierarchyTraveler.java  |   91 +
 .../classfile/visitor/ClassMemberVisitor.java      |   88 +
 .../classfile/visitor/ClassNameFilter.java         |   80 +
 .../classfile/visitor/ClassPoolFiller.java         |   28 +-
 .../classfile/visitor/ClassPoolVisitor.java        |    8 +-
 .../classfile/visitor/ClassPresenceFilter.java     |   93 +
 src/proguard/classfile/visitor/ClassPrinter.java   |  956 +++++++
 .../classfile/visitor/ClassVersionFilter.java      |   72 +
 .../classfile/visitor/ClassVersionSetter.java      |   83 +
 .../{ClassFileVisitor.java => ClassVisitor.java}   |   14 +-
 ...raveler.java => ConcreteClassDownTraveler.java} |   52 +-
 src/proguard/classfile/visitor/CpInfoVisitor.java  |   45 -
 .../visitor/DotClassClassFileVisitor.java          |   99 -
 .../classfile/visitor/DotClassClassVisitor.java    |   91 +
 ...AllCpInfoVisitor.java => ExceptionCounter.java} |   32 +-
 .../visitor/ExceptionExcludedOffsetFilter.java     |   64 +
 .../classfile/visitor/ExceptionOffsetFilter.java   |   64 +
 .../classfile/visitor/ExceptionRangeFilter.java    |   68 +
 .../classfile/visitor/LibraryClassFileFilter.java  |   60 -
 ...lassPoolFiller.java => LibraryClassFilter.java} |   33 +-
 .../classfile/visitor/LibraryMemberFilter.java     |   73 +
 .../classfile/visitor/LibraryMemberInfoFilter.java |   73 -
 ...foAccessFilter.java => MemberAccessFilter.java} |   62 +-
 .../classfile/visitor/MemberClassAccessFilter.java |  106 +
 src/proguard/classfile/visitor/MemberCounter.java  |   29 +-
 .../classfile/visitor/MemberDescriptorFilter.java  |   99 +
 .../visitor/MemberInfoClassFileAccessFilter.java   |  106 -
 .../visitor/MemberInfoDescriptorFilter.java        |   99 -
 .../classfile/visitor/MemberInfoNameFilter.java    |   99 -
 .../classfile/visitor/MemberInfoVisitor.java       |   40 -
 .../classfile/visitor/MemberNameFilter.java        |   99 +
 .../classfile/visitor/MemberToClassVisitor.java    |   90 +
 .../{ClassFileVisitor.java => MemberVisitor.java}  |   18 +-
 .../visitor/MethodImplementationFilter.java        |   45 +-
 .../visitor/MethodImplementationTraveler.java      |   55 +-
 .../classfile/visitor/MultiClassFileVisitor.java   |   97 -
 .../classfile/visitor/MultiClassPoolVisitor.java   |   12 +-
 .../classfile/visitor/MultiClassVisitor.java       |   97 +
 .../classfile/visitor/MultiMemberInfoVisitor.java  |  113 -
 .../classfile/visitor/MultiMemberVisitor.java      |  113 +
 ...lassFileVisitor.java => NamedClassVisitor.java} |   24 +-
 .../classfile/visitor/NamedFieldVisitor.java       |   36 +-
 .../classfile/visitor/NamedMethodVisitor.java      |   36 +-
 .../classfile/visitor/ProgramClassFileFilter.java  |   60 -
 ...lassPoolFiller.java => ProgramClassFilter.java} |   33 +-
 .../classfile/visitor/ProgramMemberFilter.java     |   73 +
 .../classfile/visitor/ProgramMemberInfoFilter.java |   73 -
 .../visitor/ReferencedClassFileVisitor.java        |  316 ---
 .../classfile/visitor/ReferencedClassVisitor.java  |  243 ++
 .../classfile/visitor/ReferencedMemberVisitor.java |   73 +
 .../classfile/visitor/SimpleClassFilePrinter.java  |  169 --
 .../classfile/visitor/SimpleClassPrinter.java      |  167 ++
 .../visitor/VariableClassFileVisitor.java          |   78 -
 .../classfile/visitor/VariableClassVisitor.java    |   78 +
 .../visitor/VariableMemberInfoVisitor.java         |   96 -
 .../classfile/visitor/VariableMemberVisitor.java   |   96 +
 src/proguard/evaluation/BasicBranchUnit.java       |  126 +
 src/proguard/evaluation/BasicInvocationUnit.java   |  360 +++
 .../{optimize => }/evaluation/BranchUnit.java      |   33 +-
 src/proguard/evaluation/InvocationUnit.java        |   62 +
 src/proguard/evaluation/MutableValue.java          |  149 +
 .../{optimize => }/evaluation/Processor.java       |  462 ++--
 src/proguard/{optimize => }/evaluation/Stack.java  |   54 +-
 .../{optimize => }/evaluation/TracedStack.java     |  133 +-
 src/proguard/evaluation/TracedVariables.java       |  292 ++
 .../{optimize => }/evaluation/Variables.java       |   86 +-
 .../evaluation/value/Category1Value.java           |    8 +-
 .../evaluation/value/Category2Value.java           |    8 +-
 .../evaluation/value/DoubleValue.java              |   45 +-
 .../evaluation/value/FloatValue.java               |   45 +-
 .../evaluation/value/InstructionOffsetValue.java   |   72 +-
 .../evaluation/value/IntegerValue.java             |   45 +-
 .../{optimize => }/evaluation/value/LongValue.java |   68 +-
 src/proguard/evaluation/value/ReferenceValue.java  |  515 ++++
 .../evaluation/value/SpecificDoubleValue.java      |   56 +-
 .../evaluation/value/SpecificFloatValue.java       |   56 +-
 .../evaluation/value/SpecificIntegerValue.java     |   88 +-
 .../evaluation/value/SpecificLongValue.java        |   77 +-
 .../evaluation/value/SpecificValueFactory.java     |   89 +
 .../value/TopValue.java}                           |   54 +-
 .../{optimize => }/evaluation/value/Value.java     |   41 +-
 src/proguard/evaluation/value/ValueFactory.java    |  193 ++
 .../{optimize => }/evaluation/value/package.html   |    0
 .../gui/ClassMemberSpecificationDialog.java        |  442 ---
 src/proguard/gui/ClassPathPanel.java               |   73 +-
 src/proguard/gui/ClassSpecificationDialog.java     |  292 +-
 src/proguard/gui/ClassSpecificationsPanel.java     |   86 +-
 src/proguard/gui/ExtensionFileFilter.java          |   14 +-
 src/proguard/gui/FilterDialog.java                 |   79 +-
 src/proguard/gui/GUIResources.java                 |   12 +-
 src/proguard/gui/GUIResources.properties           |  407 ++-
 src/proguard/gui/KeepSpecificationsPanel.java      |   81 +
 src/proguard/gui/ListPanel.java                    |   54 +-
 src/proguard/gui/MemberSpecificationDialog.java    |  497 ++++
 ...nsPanel.java => MemberSpecificationsPanel.java} |  158 +-
 src/proguard/gui/MessageDialogRunnable.java        |   23 +-
 src/proguard/gui/ProGuardGUI.java                  |  794 +++---
 src/proguard/gui/ProGuardRunnable.java             |   35 +-
 src/proguard/gui/ReTraceRunnable.java              |   31 +-
 src/proguard/gui/SwingUtil.java                    |   10 +-
 src/proguard/gui/TabbedPane.java                   |   17 +-
 src/proguard/gui/TextAreaOutputStream.java         |   15 +-
 src/proguard/gui/boilerplate.pro                   |   91 +-
 src/proguard/gui/default.pro                       |  102 +-
 src/proguard/gui/splash/BufferedSprite.java        |  102 +-
 src/proguard/gui/splash/CircleSprite.java          |   30 +-
 src/proguard/gui/splash/ClipSprite.java            |   14 +-
 .../{CompositeSprite.java => ColorSprite.java}     |   43 +-
 src/proguard/gui/splash/CompositeSprite.java       |   10 +-
 src/proguard/gui/splash/ConstantColor.java         |   10 +-
 src/proguard/gui/splash/ConstantDouble.java        |    8 +-
 src/proguard/gui/splash/ConstantFont.java          |   10 +-
 src/proguard/gui/splash/ConstantInt.java           |    8 +-
 src/proguard/gui/splash/ConstantString.java        |    8 +-
 src/proguard/gui/splash/ConstantTiming.java        |    9 +-
 .../{CompositeSprite.java => FontSprite.java}      |   43 +-
 src/proguard/gui/splash/ImageSprite.java           |   16 +-
 src/proguard/gui/splash/LinearColor.java           |   14 +-
 src/proguard/gui/splash/LinearDouble.java          |   12 +-
 src/proguard/gui/splash/LinearInt.java             |   12 +-
 src/proguard/gui/splash/LinearTiming.java          |   10 +-
 src/proguard/gui/splash/OverrideGraphics2D.java    |   11 +-
 src/proguard/gui/splash/RectangleSprite.java       |   66 +-
 src/proguard/gui/splash/SawToothTiming.java        |   10 +-
 src/proguard/gui/splash/ShadowedSprite.java        |   39 +-
 src/proguard/gui/splash/SineTiming.java            |   10 +-
 src/proguard/gui/splash/SmoothTiming.java          |   10 +-
 src/proguard/gui/splash/SplashPanel.java           |   50 +-
 src/proguard/gui/splash/Sprite.java                |    8 +-
 src/proguard/gui/splash/TextSprite.java            |   33 +-
 src/proguard/gui/splash/TimeSwitchSprite.java      |   30 +-
 src/proguard/gui/splash/Timing.java                |    6 +-
 src/proguard/gui/splash/TypeWriterString.java      |   10 +-
 src/proguard/gui/splash/VariableColor.java         |    8 +-
 src/proguard/gui/splash/VariableDouble.java        |    6 +-
 src/proguard/gui/splash/VariableFont.java          |    6 +-
 src/proguard/gui/splash/VariableInt.java           |    6 +-
 src/proguard/gui/splash/VariableSizeFont.java      |   12 +-
 src/proguard/gui/splash/VariableString.java        |    6 +-
 src/proguard/io/CascadingDataEntryWriter.java      |   10 +-
 src/proguard/io/ClassFileReader.java               |  104 -
 .../io/{ClassFileFilter.java => ClassFilter.java}  |   30 +-
 src/proguard/io/ClassReader.java                   |  114 +
 .../{ClassFileRewriter.java => ClassRewriter.java} |   29 +-
 src/proguard/io/DataEntry.java                     |    6 +-
 src/proguard/io/DataEntryCopier.java               |   13 +-
 src/proguard/io/DataEntryFilter.java               |    6 +-
 src/proguard/io/DataEntryNameFilter.java           |    8 +-
 src/proguard/io/DataEntryParentFilter.java         |    8 +-
 src/proguard/io/DataEntryPump.java                 |   10 +-
 src/proguard/io/DataEntryReader.java               |    9 +-
 src/proguard/io/DataEntryRenamer.java              |   78 +
 src/proguard/io/DataEntryRewriter.java             |  164 ++
 src/proguard/io/DataEntryWriter.java               |    6 +-
 src/proguard/io/DirectoryPump.java                 |    8 +-
 src/proguard/io/DirectoryWriter.java               |   16 +-
 src/proguard/io/FileDataEntry.java                 |   10 +-
 src/proguard/io/FilteredDataEntryReader.java       |   14 +-
 src/proguard/io/FilteredDataEntryWriter.java       |    8 +-
 src/proguard/io/Finisher.java                      |    8 +-
 src/proguard/io/JarReader.java                     |   10 +-
 src/proguard/io/JarWriter.java                     |   32 +-
 src/proguard/io/ParentDataEntryWriter.java         |    7 +-
 src/proguard/io/RenamedDataEntry.java              |   10 +-
 src/proguard/io/ZipDataEntry.java                  |   14 +-
 src/proguard/obfuscate/AttributeShrinker.java      |  105 +-
 src/proguard/obfuscate/AttributeUsageMarker.java   |  358 +--
 src/proguard/obfuscate/ClassFileObfuscator.java    |  179 --
 src/proguard/obfuscate/ClassFileOpener.java        |  117 -
 src/proguard/obfuscate/ClassFileRenamer.java       |  151 -
 src/proguard/obfuscate/ClassObfuscator.java        |  390 +++
 src/proguard/obfuscate/ClassOpener.java            |  127 +
 src/proguard/obfuscate/ClassRenamer.java           |  112 +
 src/proguard/obfuscate/DictionaryNameFactory.java  |    6 +-
 src/proguard/obfuscate/MapCleaner.java             |   27 +-
 src/proguard/obfuscate/MappingKeeper.java          |   45 +-
 src/proguard/obfuscate/MappingPrinter.java         |   67 +-
 src/proguard/obfuscate/MappingProcessor.java       |   12 +-
 src/proguard/obfuscate/MappingReader.java          |   45 +-
 src/proguard/obfuscate/MemberInfoNameCleaner.java  |   63 -
 .../obfuscate/MemberInfoNameConflictFixer.java     |  156 --
 .../obfuscate/MemberInfoSpecialNameFilter.java     |  103 -
 src/proguard/obfuscate/MemberNameCleaner.java      |   60 +
 ...NameCollector.java => MemberNameCollector.java} |   76 +-
 .../obfuscate/MemberNameConflictFixer.java         |  158 ++
 ...erInfoObfuscator.java => MemberObfuscator.java} |  103 +-
 .../obfuscate/MemberSpecialNameFilter.java         |  101 +
 src/proguard/obfuscate/MultiMappingProcessor.java  |   20 +-
 src/proguard/obfuscate/NameAndTypeShrinker.java    |   66 +-
 src/proguard/obfuscate/NameAndTypeUsageMarker.java |  105 +-
 src/proguard/obfuscate/NameFactory.java            |    8 +-
 src/proguard/obfuscate/NameFactoryResetter.java    |   26 +-
 src/proguard/obfuscate/NameMarker.java             |   68 +-
 src/proguard/obfuscate/Obfuscator.java             |  313 +--
 src/proguard/obfuscate/SimpleNameFactory.java      |    8 +-
 src/proguard/obfuscate/SourceFileRenamer.java      |   80 +-
 src/proguard/obfuscate/SpecialNameFactory.java     |    9 +-
 src/proguard/obfuscate/Utf8Shrinker.java           |   63 +-
 src/proguard/obfuscate/Utf8UsageMarker.java        |  287 +-
 src/proguard/optimize/ChangedCodePrinter.java      |  230 +-
 src/proguard/optimize/ConstantMemberFilter.java    |   77 +
 src/proguard/optimize/KeepMarker.java              |   48 +-
 .../optimize/MemberDescriptorSpecializer.java      |  138 +
 .../optimize/MethodDescriptorShrinker.java         |  312 +++
 src/proguard/optimize/MethodOptimizationInfo.java  |  114 -
 .../optimize/MethodOptimizationInfoSetter.java     |   56 -
 src/proguard/optimize/MethodStaticizer.java        |   87 +
 src/proguard/optimize/NonPrivateMethodMarker.java  |  141 -
 .../optimize/OptimizationInfoMemberFilter.java     |   94 +
 src/proguard/optimize/Optimizer.java               |  524 ++--
 src/proguard/optimize/ParameterShrinker.java       |  371 +--
 src/proguard/optimize/ParameterUsageMarker.java    |  138 -
 .../optimize/SideEffectInstructionChecker.java     |  271 --
 src/proguard/optimize/SideEffectMethodMarker.java  |  172 --
 .../optimize/UnusedParameterInvocationUnit.java    |  132 +
 src/proguard/optimize/VariableUsageMarker.java     |   74 -
 src/proguard/optimize/WriteOnlyFieldFilter.java    |   44 +-
 src/proguard/optimize/WriteOnlyFieldMarker.java    |  158 --
 .../optimize/evaluation/EvaluationSimplifier.java  |  994 ++++---
 .../optimize/evaluation/LivenessAnalyzer.java      |  512 ++++
 .../optimize/evaluation/LoadingInvocationUnit.java |  114 +
 .../optimize/evaluation/PartialEvaluator.java      |  803 +++---
 .../optimize/evaluation/StoringInvocationUnit.java |  164 ++
 .../optimize/evaluation/TracedBranchUnit.java      |  137 +-
 .../optimize/evaluation/TracedVariables.java       |  215 --
 .../evaluation/UnusedParameterCleaner.java         |  170 --
 .../optimize/evaluation/VariableOptimizer.java     |  222 ++
 .../evaluation/value/DoubleValueFactory.java       |   53 -
 .../evaluation/value/FloatValueFactory.java        |   55 -
 .../value/InstructionOffsetValueFactory.java       |   59 -
 .../evaluation/value/IntegerValueFactory.java      |   66 -
 .../evaluation/value/LongValueFactory.java         |   53 -
 .../optimize/evaluation/value/ReferenceValue.java  |  190 --
 .../evaluation/value/ReferenceValueFactory.java    |   81 -
 .../value/SpecificArrayReferenceValue.java         |  160 --
 .../evaluation/value/SpecificReferenceValue.java   |  158 --
 .../optimize/evaluation/value/ValueFactory.java    |   63 -
 src/proguard/optimize/info/AccessMethodMarker.java |  187 ++
 .../optimize/info/BackwardBranchMarker.java        |   90 +
 .../optimize/info/CatchExceptionMarker.java        |   69 +
 .../optimize/info/ClassOptimizationInfo.java       |   88 +
 .../optimize/info/ExceptionInstructionChecker.java |  184 ++
 .../optimize/info/FieldOptimizationInfo.java       |  162 ++
 .../info/MemberOptimizationInfoSetter.java         |   59 +
 .../optimize/info/MethodInvocationMarker.java      |  107 +
 .../optimize/info/MethodOptimizationInfo.java      |  273 ++
 .../{ => info}/NoSideEffectMethodMarker.java       |   47 +-
 .../optimize/info/NonPrivateMemberMarker.java      |  177 ++
 .../optimize/info/ParameterUsageMarker.java        |  220 ++
 .../optimize/info/ReadWriteFieldMarker.java        |  163 ++
 .../info/SideEffectInstructionChecker.java         |  220 ++
 .../optimize/info/SideEffectMethodMarker.java      |  175 ++
 .../SingleImplementationMarker.java                |   86 +-
 .../optimize/info/SuperInvocationMarker.java       |   93 +
 .../optimize/info/VariableUsageMarker.java         |  108 +
 src/proguard/optimize/info/package.html            |    4 +
 .../optimize/peephole/BranchTargetFinder.java      |  600 ++--
 .../optimize/peephole/ClassFileFinalizer.java      |  135 -
 src/proguard/optimize/peephole/ClassFinalizer.java |  127 +
 .../optimize/peephole/GetterSetterInliner.java     |  383 ---
 .../optimize/peephole/GotoCommonCodeReplacer.java  |  119 +-
 .../optimize/peephole/GotoGotoReplacer.java        |   59 +-
 .../optimize/peephole/GotoReturnReplacer.java      |   54 +-
 .../peephole/InstructionSequenceConstants.java     | 2870 ++++++++++++++++++++
 .../peephole/InstructionSequenceReplacer.java      |  278 ++
 .../peephole/InstructionSequencesReplacer.java     |  138 +
 .../optimize/peephole/LoadStoreRemover.java        |   60 +-
 .../optimize/peephole/MemberPrivatizer.java        |  108 +
 src/proguard/optimize/peephole/MethodInliner.java  |  554 ++++
 .../optimize/peephole/MethodPrivatizer.java        |   85 -
 src/proguard/optimize/peephole/NopRemover.java     |   51 +-
 .../optimize/peephole/PeepholeOptimizer.java       |  103 +
 src/proguard/optimize/peephole/PushPopRemover.java |   85 +-
 .../optimize/peephole/ReachableCodeMarker.java     |  263 ++
 .../peephole/SingleImplementationFixer.java        |  142 +-
 .../peephole/SingleImplementationInliner.java      |  260 +-
 .../optimize/peephole/StoreLoadReplacer.java       |   72 +-
 .../optimize/peephole/UnreachableCodeRemover.java  |  143 +
 .../peephole/UnreachableExceptionRemover.java      |  163 ++
 .../optimize/peephole/VariableShrinker.java        |  131 +
 src/proguard/preverify/CodePreverifier.java        |  603 ++++
 src/proguard/preverify/CodeSubroutineInliner.java  |  378 +++
 src/proguard/preverify/Preverifier.java            |   74 +
 src/proguard/preverify/SubroutineInliner.java      |   74 +
 src/proguard/retrace/ReTrace.java                  |   18 +-
 src/proguard/retrace/StackTrace.java               |   25 +-
 src/proguard/retrace/StackTraceItem.java           |   59 +-
 src/proguard/shrink/AnnotationUsageMarker.java     |  359 +++
 src/proguard/shrink/ClassFileShrinker.java         |  346 ---
 src/proguard/shrink/ClassShrinker.java             |  375 +++
 src/proguard/shrink/InnerUsageMarker.java          |  225 +-
 src/proguard/shrink/InterfaceUsageMarker.java      |   83 +-
 src/proguard/shrink/ShortestUsageMark.java         |   84 +-
 src/proguard/shrink/ShortestUsageMarker.java       |  101 +-
 src/proguard/shrink/ShortestUsagePrinter.java      |  100 +-
 src/proguard/shrink/Shrinker.java                  |   68 +-
 src/proguard/shrink/UsageMarker.java               |  729 ++---
 src/proguard/shrink/UsagePrinter.java              |   98 +-
 src/proguard/shrink/UsedClassFileFilter.java       |   75 -
 src/proguard/shrink/UsedClassFilter.java           |   74 +
 src/proguard/shrink/UsedMemberFilter.java          |   93 +
 .../{ExtensionMatcher.java => AndMatcher.java}     |   26 +-
 src/proguard/util/BasicListMatcher.java            |  147 -
 src/proguard/util/BasicMatcher.java                |  359 ---
 src/proguard/util/ClassNameListMatcher.java        |   87 -
 src/proguard/util/ClassNameMatcher.java            |   97 -
 src/proguard/util/ClassNameParser.java             |  216 ++
 .../EmptyStringMatcher.java}                       |   24 +-
 src/proguard/util/ExtensionMatcher.java            |   26 +-
 src/proguard/util/FileNameListMatcher.java         |   87 -
 src/proguard/util/FileNameMatcher.java             |   90 -
 src/proguard/util/FileNameParser.java              |  121 +
 ...tensionMatcher.java => FixedStringMatcher.java} |   32 +-
 src/proguard/util/ListMatcher.java                 |   69 +
 src/proguard/util/ListParser.java                  |  131 +
 src/proguard/util/ListUtil.java                    |    6 +-
 src/proguard/util/NameParser.java                  |  106 +
 .../{ExtensionMatcher.java => NotMatcher.java}     |   23 +-
 .../util/{ExtensionMatcher.java => OrMatcher.java} |   26 +-
 ...{ExtensionMatcher.java => SettableMatcher.java} |   23 +-
 src/proguard/util/StringMatcher.java               |    6 +-
 .../VariableFont.java => util/StringParser.java}   |   19 +-
 src/proguard/util/VariableStringMatcher.java       |  126 +
 src/proguard/wtk/ProGuardObfuscator.java           |   12 +-
 src/proguard/wtk/default.pro                       |    3 +-
 688 files changed, 51907 insertions(+), 29177 deletions(-)

diff --git a/README b/README
index db97aa0..d08a410 100644
--- a/README
+++ b/README
@@ -40,7 +40,7 @@ If you want to compile the ProGuard Ant task as well:
     javac -sourcepath src -d classes -classpath lib/ant.jar \
         src/proguard/ant/ProGuardTask.java
 
-If you want to compile the J2ME WTK obfuscator plug-in:
+If you want to compile the JME WTK obfuscator plug-in:
 
     javac -sourcepath src -d classes -classpath wtklib/kenv.zip \
         src/proguard/wtk/ProGuardObfuscator.java
diff --git a/docs/FAQ.html b/docs/FAQ.html
index b39e2c6..032bfa8 100644
--- a/docs/FAQ.html
+++ b/docs/FAQ.html
@@ -15,13 +15,14 @@
 <ol>
 <li><a href="#shrinking">What is shrinking?</a>
 <li><a href="#obfuscation">What is obfuscation?</a>
+<li><a href="#preverification">What is preverification?</a>
 <li><a href="#optimization">What kind of optimizations does <b>ProGuard</b>
     support?</a>
 <li><a href="#commercial">Can I use <b>ProGuard</b> to process my commercial
     application?</a>
-<li><a href="#jdk1.4">Does <b>ProGuard</b> work with JDK1.4? JDK5.0?
-    JDK6.0?</a>
-<li><a href="#j2me">Does <b>ProGuard</b> work with J2ME?</a>
+<li><a href="#jdk1.4">Does <b>ProGuard</b> work with Java 2? Java 5? Java
+    6?</a>
+<li><a href="#j2me">Does <b>ProGuard</b> work with Java Micro Edition?</a>
 <li><a href="#ant">Does <b>ProGuard</b> have support for Ant?</a>
 <li><a href="#gui">Does <b>ProGuard</b> come with a GUI?</a>
 <li><a href="#forname">Does <b>ProGuard</b> handle <code>Class.forName</code>
@@ -39,17 +40,17 @@
 
 <a name="shrinking"> </a>
 <h3>What is shrinking?</h3>
+
 Java source code (.java files) is typically compiled to bytecode (.class
-files). Complete programs or program libraries are usually zipped up and
-distributed as Java archives (.jar files). Bytecode is more compact than Java
-source code, but it may still contain a lot of unused code, especially if it
-uses program libraries. Shrinking programs such as <b>ProGuard</b> can analyze
-bytecode and remove unused classes, fields, and methods. The program remains
-functionally equivalent, including the information given in exception stack
-traces.
+files). Bytecode is more compact than Java source code, but it may still
+contain a lot of unused code, especially if it includes program libraries.
+Shrinking programs such as <b>ProGuard</b> can analyze bytecode and remove
+unused classes, fields, and methods. The program remains functionally
+equivalent, including the information given in exception stack traces.
 
 <a name="obfuscation"> </a>
 <h3>What is obfuscation?</h3>
+
 By default, compiled bytecode still contains a lot of debugging information:
 source file names, line numbers, field names, method names, argument names,
 variable names, etc. This information makes it straightforward to decompile
@@ -60,25 +61,44 @@ it much harder to reverse-engineer the code. It further compacts the code as a
 bonus. The program remains functionally equivalent, except for the class
 names, method names, and line numbers given in exception stack traces.
 
+<a name="preverification"> </a>
+<h3>What is preverification?</h3>
+
+When loading class files, the class loader performs some sophisticated
+verification of the byte code. This analysis makes sure the code can't
+accidentally or intentionally break out of the sandbox of the virtual machine.
+Java Micro Edition and Java 6 introduced split verification. This means that
+the JME preverifier and the Java 6 compiler add preverification information to
+the class files (StackMap and StackMapTable attributes, respectively), in order
+to simplify the actual verification step for the class loader. Class files can
+then be loaded faster and in a more memory-efficient way. <b>ProGuard</b> can
+perform the preverification step too, for instance allowing to retarget older
+class files at Java 6.
+
 <a name="optimization"> </a>
 <h3>What kind of optimizations does <b>ProGuard</b> support?</h3>
+
 Apart from removing unused classes, fields, and methods in the shrinking step,
 <b>ProGuard</b> can also perform optimizations at the bytecode level, inside
-methods:
+methods. Thanks to techniques like control flow analysis, data flow analysis,
+partial evaluation, and liveness analysis, <b>ProGuard</b> can:
+
 <ul>
 <li>Evaluate constant expressions.
-<li>Remove unnecessary field accesses.
-<li>Remove unnecessary method calls.
+<li>Remove unnecessary field accesses and method calls.
 <li>Remove unnecessary branches.
 <li>Remove unnecessary comparisons and instanceof tests.
-<li>Remove unused code.
-<li>Remove write-only fields.
-<li>Remove unused method parameters.
-<li>Various peephole optimizations like push/pop simplification.
-<li>Make classes static and final when possible.
+<li>Remove unused code blocks.
+<li>Merge identical code blocks.
+<li>Reduce variable allocation.
+<li>Remove write-only fields and unused method parameters.
+<li>Inline constant fields, method parameters, and return values.
+<li>Inline methods that are short or only called once.
 <li>Make methods private, static, and final when possible.
-<li>Inline simple getters and setters when possible.
+<li>Make classes static and final when possible.
 <li>Replace interfaces that have single implementations.
+<li>Perform over 200 peephole optimizations, like replacing ...*2 by
+    ...<<1.
 <li>Optionally remove logging code.
 </ul>
 The positive effects of these optimizations will depend on your code and on
@@ -88,48 +108,40 @@ At the very least, your bytecode may become a bit smaller.
 <p>
 Some notable optimizations that aren't supported yet:
 <ul>
-<li>Inlining of constant fields that are not marked as final.
-<li>Inlining of other methods than getters and setters.
 <li>Moving constant expressions out of loops.
 <li>Optimizations that require escape analysis.
 </ul>
 
 <a name="commercial"> </a>
 <h3>Can I use <b>ProGuard</b> to process my commercial application?</h3>
+
 Yes, you can. <b>ProGuard</b> itself is distributed under the GPL, but this
 doesn't affect the programs that you process. Your code remains yours, and
 its license can remain the same.
 
 <a name="jdk1.4"> </a>
-<h3>Does <b>ProGuard</b> work with JDK1.4? JDK5.0? JDK6.0?</h3>
-Yes, <b>ProGuard</b> supports all JDKs up to and including 6.0. Class files
-compiled with JDK1.4 are targeted at JRE1.2 by default. Class files compiled
-with JDK5.0 may have additional attributes for generics and annotations. The
-compiled class files have slightly different structures from older class
-files. <b>ProGuard</b> handles all versions correctly.
-<p>
-The upcoming <b>ProGuard 4.0</b> supports preverification, which improves the
-start-up performance in JDK6.0.
+<h3>Does <b>ProGuard</b> work with Java 2? Java 5? Java 6?</h3>
+
+Yes, <b>ProGuard</b> supports all JDKs from 1.1 up to and including 6.0. Java 2
+introduced some small differences in the class file format. Java 5 added
+attributes for generics and for annotations. Java 6 introduced preverification
+attributes. <b>ProGuard</b> handles all versions correctly.
 
 <a name="j2me"> </a>
-<h3>Does <b>ProGuard</b> work with J2ME?</h3>
-Yes. <b>ProGuard</b> itself runs in J2SE, but you can freely specify the
-run-time environment at which your programs are targeted, including J2ME. For
-instance, you can specify <code>midpapi20.jar</code> and
-<code>cldcapi11.jar</code> as the run-time libraries, instead of J2SE's
-traditional <code>rt.jar</code>. All of <b>ProGuard</b>'s powerful
-configuration options remain available. The <a
-href="manual/examples.html">example section</a> of the <a
-href="manual/index.html">ProGuard User Manual</a> illustrates how to process
-<a href="manual/examples.html#midlet">a single midlet</a> or <a
-href="manual/examples.html#midlets">all midlets</a> in your input jar, for
-instance.
+<h3>Does <b>ProGuard</b> work with Java Micro Edition?</h3>
+
+Yes. <b>ProGuard</b> itself runs in Java Standard Edition, but you can freely
+specify the run-time environment at which your programs are targeted,
+including Java Micro Edition. <b>ProGuard</b> then also performs the required
+preverification, producing more compact results than the traditional external
+preverifier.
 <p>
-In addition, <b>ProGuard</b> provides an obfuscator plug-in for the J2ME
-Wireless Toolkit.
+<b>ProGuard</b> also comes with an obfuscator plug-in for the JME Wireless
+Toolkit.
 
 <a name="ant"> </a>
 <h3>Does <b>ProGuard</b> have support for Ant?</h3>
+
 Yes. <b>ProGuard</b> provides an Ant task, so that it integrates seamlessly
 into your Ant build processes. You can still use configurations in
 <b>ProGuard</b>'s own readable format. Alternatively, if you prefer XML, you
@@ -137,13 +149,15 @@ can specify the equivalent XML configuration.
 
 <a name="gui"> </a>
 <h3>Does <b>ProGuard</b> come with a GUI?</h3>
+
 Yes. First of all, <b>ProGuard</b> is perfectly usable as a command-line tool
 that can easily be integrated into any automatic build process. For casual
-users, there's also a graphical user interface that makes creating, loading,
-editing, executing, and saving ProGuard configurations a breeze.
+users, there's also a graphical user interface that simplifies creating,
+loading, editing, executing, and saving ProGuard configurations.
 
 <a name="forname"> </a>
 <h3>Does <b>ProGuard</b> handle <code>Class.forName</code> calls?</h3>
+
 Yes. <b>ProGuard</b> automatically handles
 <code>Class.forName("SomeClass")</code> and <code>SomeClass.class</code>
 constructs. The referenced classes are preserved in the shrinking phase, and
@@ -159,12 +173,13 @@ accordingly.
 
 <a name="resource"> </a>
 <h3>Does <b>ProGuard</b> handle resource files?</h3>
-Yes, in the sense that <b>ProGuard</b> copies all non-class resource files
-from the given input jars to the output jars. Their names and contents remain
-unchanged.
+
+Yes. <b>ProGuard</b> copies all non-class resource files, optionally adapting
+their names and their contents to the obfuscation that has been applied.
 
 <a name="encrypt"> </a>
 <h3>Does <b>ProGuard</b> encrypt strings constants?</h3>
+
 No. Storing encrypted string constants in program code is fairly futile, since
 the encryption has to be perfectly reversible by definition. Moreover, the
 decryption costs additional memory and computation at run-time. If this feature
@@ -172,21 +187,23 @@ is ever incorporated, I'll provide a tool to decrypt the strings as well.
 
 <a name="flow"> </a>
 <h3>Does <b>ProGuard</b> perform flow obfuscation?</h3>
-No. Control obfuscation injects additional branches into the bytecode, in an
-attempt to fool decompilers. This operation may confuse JIT compilers as well,
-adversely affecting performance. It may be added as an option in the future,
-but it doesn't have the highest priority. I will focus mostly on the
-optimization step, which may already restructure the code to the point where
+
+Not explicitly. Control flow obfuscation injects additional branches into the
+bytecode, in an attempt to fool decompilers. <b>ProGuard</b> does not do this,
+in order to avoid any negative effects on performance and size. However, the
+optimization step often already restructures the code to the point where most
 decompilers get confused.
 
 <a name="incremental"> </a>
 <h3>Does <b>ProGuard</b> support incremental obfuscation?</h3>
+
 Yes. This feature allows you to specify a previous obfuscation mapping file in
 a new obfuscation step, in order to produce add-ons or patches for obfuscated
 code.
 
 <a name="keywords"> </a>
 <h3>Can <b>ProGuard</b> obfuscate using reserved keywords?</h3>
+
 Yes. You can specify your own obfuscation dictionary, such as a list of
 reserved key words, identifiers with foreign characters, random source files,
 or a text by Shakespeare. Note that this hardly improves the obfuscation.
@@ -195,6 +212,7 @@ can be undone fairly easily, by obfuscating again with simpler names.
 
 <a name="stacktrace"> </a>
 <h3>Can <b>ProGuard</b> reconstruct obfuscated stack traces?</h3>
+
 Yes. <b>ProGuard</b> comes with a companion tool, <b>ReTrace</b>, that can
 'de-obfuscate' stack traces produced by obfuscated applications. The
 reconstruction is based on the mapping file that <b>ProGuard</b> can write
diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html
index 48d4015..ab26e86 100644
--- a/docs/GPL_exception.html
+++ b/docs/GPL_exception.html
@@ -35,9 +35,9 @@ the code of this program with the following stand-alone applications:
 <ul>
 <li>Apache Ant,
 <li>Apache Maven,
-<li>the Eclipse IDE,
-<li>the Sun NetBeans IDE,
-<li>the Sun J2ME Wireless Toolkit, and
+<li>the Eclipse Java IDE,
+<li>the Sun NetBeans Java IDE,
+<li>the Sun JME Wireless Toolkit, and
 <li>the Javaground Tools,
 </ul>
 and distribute linked combinations including the two. You must obey the GNU
diff --git a/docs/acknowledgements.html b/docs/acknowledgements.html
index df13b6f..07084fc 100644
--- a/docs/acknowledgements.html
+++ b/docs/acknowledgements.html
@@ -1,4 +1,3 @@
-
 <!doctype html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 <html>
 <head>
@@ -11,12 +10,13 @@
 
 <h2>Acknowledgements</h2>
 
-<b>ProGuard</b> grew out of <b>RetroGuard</b>, which its author Mark Welsh has
-kindly made available under the GNU Lesser General Public License.
-<b>RetroGuard</b> is a very nice piece of code, but it only performed
+The first versions of <b>ProGuard</b> grew out of <b>RetroGuard</b>, which its
+author Mark Welsh kindly made available under the GNU Lesser General Public
+License. <b>RetroGuard</b> is a very nice piece of code, but it only performed
 obfuscation. I started from the class file parsing code and wrote my own
-shrinker, optimizer, and obfuscator. At this point, both programs have little
-code in common.
+shrinker, optimizer, obfuscator, and preverifier. As of version 4.0, all of the
+original code has been rewritten, so the most obvious remaining similarity are
+the program names.
 <p>
 
 Dirk Schnelle has contributed and maintained the first versions of the Ant
@@ -32,7 +32,7 @@ Sherington, David Sitsky, James Manning, Ptolemy Oberin, Frank-Michael Moser,
 QZ Shines, Thomas Singer, Michele Puccini, Roman Bednarek, Natalia Pujol,
 Daniel Sjöblom, Jan Filipsky, Charles Smith, Gerrit Telkamp, Noel
 Grandin, Torbjörn Söderstedt, Clemens Eisserer, Clark Bassett,
-Eduard Welch,
+Eduard Welch, Dawid Weiss,
 and many others. Thanks! Your feedback has been invaluable.
 <p>
 
@@ -41,7 +41,7 @@ employer <a href="http://www.luciad.com/" target="other">Luciad</a> is kindly
 allowing me to use.
 <p>
 
-<a href="http://sourceforge.net/forum/projects/proguard/"
+<a href="http://sourceforge.net/projects/proguard/"
 target="other">SourceForge</a> is generously providing the resources for
 hosting this project and many other projects.
 <p>
diff --git a/docs/alternatives.html b/docs/alternatives.html
index 7014e6c..20d5564 100644
--- a/docs/alternatives.html
+++ b/docs/alternatives.html
@@ -10,15 +10,15 @@
 
 <h2>Alternatives</h2>
 
-There are quite a few Java class file shrinkers, optimizers, and obfuscators
-out there. Users of <b>ProGuard</b> tell me it easily compares with the best
-of them. However, you may want to check that out yourself.
+There are quite a few Java class file shrinkers, optimizers, obfuscators, and
+preverifiers out there. Users of <b>ProGuard</b> tell me it easily compares
+with the best of them. However, you may want to check that out yourself.
 <p>
 This is a list of the programs of which I'm aware. Obviously, I've never
 personally tested all of them. Many programs, even commercial ones, have been
 abandoned. Please drop me a note if you know of any other shrinkers,
-optimizers, or obfuscators, or if some information provided below is
-incorrect.
+optimizers, obfuscators, or preverifiers, or if some information provided
+below is incorrect.
 <p>
 
 <table>
@@ -26,9 +26,10 @@ incorrect.
 <tr>
 <th>Author/Company</th>
 <th>Program</th>
-<th>Shrinking</th>
-<th>Optimization</th>
-<th>Obfuscation</th>
+<th>Shrink.</th>
+<th>Optim.</th>
+<th>Obfusc.</th>
+<th>Preverif.</th>
 <th>License</th>
 </tr>
 
@@ -38,6 +39,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td>Free (GPL)</td>
 </tr>
 
@@ -47,6 +49,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free (GPL)</td>
 </tr>
 
@@ -56,6 +59,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free (no source)</td>
 </tr>
 
@@ -65,6 +69,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free</td>
 </tr>
 
@@ -74,6 +79,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free (BSD)</td>
 </tr>
 
@@ -83,6 +89,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free</td>
 </tr>
 
@@ -92,6 +99,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free (no source)</td>
 </tr>
 
@@ -101,6 +109,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free (GPL)</td>
 </tr>
 
@@ -110,6 +119,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free (Apache)</td>
 </tr>
 
@@ -119,6 +129,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free (Apache)</td>
 </tr>
 
@@ -128,6 +139,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free (LGPL)</td>
 </tr>
 
@@ -137,6 +149,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Free (LGPL)</td>
 </tr>
 
@@ -146,6 +159,7 @@ incorrect.
 <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 align="center"><br></td>
 <td>Free (LGPL)</td>
 </tr>
 
@@ -155,6 +169,7 @@ incorrect.
 <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 align="center"><br></td>
 <td>Free</td>
 </tr>
 
@@ -164,6 +179,7 @@ incorrect.
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Free</td>
 </tr>
 
@@ -173,6 +189,7 @@ incorrect.
 <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>
 
@@ -182,6 +199,7 @@ incorrect.
 <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 (GPL)</td>
 </tr>
 
@@ -191,6 +209,7 @@ incorrect.
 <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</td>
 </tr>
 
@@ -200,6 +219,7 @@ incorrect.
 <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 (no source)</td>
 </tr>
 
@@ -209,6 +229,7 @@ incorrect.
 <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 (no source)</td>
 </tr>
 
@@ -218,6 +239,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -227,6 +249,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -236,6 +259,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -245,6 +269,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -254,6 +279,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -263,6 +289,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -272,6 +299,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -281,6 +309,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -290,6 +319,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -299,6 +329,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -308,6 +339,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -317,6 +349,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td>Commercial</td>
 </tr>
 
@@ -326,6 +359,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -335,6 +369,7 @@ incorrect.
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -344,6 +379,7 @@ incorrect.
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial</td>
 </tr>
 
@@ -353,15 +389,7 @@ incorrect.
 <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>Commercial</td>
-</tr>
-
-<tr>
-<td><a target="other" rel="nofollow" href="http://www.software4j.com/">Software4j</a></td>
-<td><a target="other" href="http://www.software4j.com/obfuscate4j/">Obfuscate4j</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>Commercial</td>
 </tr>
 
@@ -371,6 +399,7 @@ incorrect.
 <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>Commercial</td>
 </tr>
 
@@ -380,6 +409,7 @@ incorrect.
 <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>Commercial</td>
 </tr>
 
@@ -389,6 +419,7 @@ incorrect.
 <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>Commercial</td>
 </tr>
 
@@ -398,6 +429,17 @@ incorrect.
 <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>Commercial</td>
+</tr>
+
+<tr>
+<td><a target="other" rel="nofollow" href="http://www.chainkey.com/">ChainKey</a></td>
+<td><a target="other" href="http://www.chainkey.com/en/jcp/">Java Code Protector</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>Commercial</td>
 </tr>
 
@@ -407,6 +449,7 @@ incorrect.
 <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 (BSD)</td>
 </tr>
 
@@ -416,6 +459,7 @@ incorrect.
 <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</td>
 </tr>
 
@@ -425,6 +469,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>(discontinued)</td>
 </tr>
 
@@ -434,6 +479,7 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>(disappeared?)</td>
 </tr>
 
@@ -443,6 +489,7 @@ incorrect.
 <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>(disappeared?)</td>
 </tr>
 
@@ -452,6 +499,7 @@ incorrect.
 <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>(disappeared?)</td>
 </tr>
 
@@ -461,6 +509,7 @@ incorrect.
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>(disappeared?)</td>
 </tr>
 
@@ -470,24 +519,37 @@ incorrect.
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial (discontinued)</td>
 </tr>
 
 <tr class="disappeared">
-<td><a target="other" rel="nofollow" href="http://www.jammconsulting.com/">JAMM Consulting</a></td>
-<td><a target="other" rel="nofollow" href="http://www.jammconsulting.com/jamm/servlet/com.jammconsulting.servlet.JAMMServlet?pageId=ObfuscateProPage">ObfuscatePro</a></td>
-<td align="center"><br></td>
+<td><a target="other" rel="nofollow" href="http://www.4thpass.com/">4th Pass</a></td>
+<td><a target="other" rel="nofollow" href="http://www.4thpass.com/">SourceGuard</a></td>
+<td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
 <td align="center"><br></td>
 <td align="center"><img src="checkmark.gif" width="11" height="11" alt="x"></td>
+<td align="center"><br></td>
 <td>Commercial (discontinued?)</td>
 </tr>
 
 <tr class="disappeared">
-<td><a target="other" rel="nofollow" href="http://www.4thpass.com/">4th Pass</a></td>
-<td><a target="other" rel="nofollow" href="http://www.4thpass.com/">SourceGuard</a></td>
+<td><a target="other" rel="nofollow" href="http://www.software4j.com/">Software4j</a></td>
+<td><a target="other" rel="nofollow" href="http://www.software4j.com/obfuscate4j/">Obfuscate4j</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>Commercial (discontinued?)</td>
+</tr>
+
+<tr class="disappeared">
+<td><a target="other" rel="nofollow" href="http://www.jammconsulting.com/">JAMM Consulting</a></td>
+<td><a target="other" rel="nofollow" href="http://www.jammconsulting.com/jamm/servlet/com.jammconsulting.servlet.JAMMServlet?pageId=ObfuscateProPage">ObfuscatePro</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>Commercial (discontinued?)</td>
 </tr>
 
@@ -497,6 +559,7 @@ incorrect.
 <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>Commercial (discontinued?)</td>
 </tr>
 
@@ -506,6 +569,7 @@ incorrect.
 <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>Commercial (discontinued?)</td>
 </tr>
 
@@ -515,6 +579,7 @@ incorrect.
 <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>Commercial (disappeared?)</td>
 </tr>
 
@@ -524,6 +589,7 @@ incorrect.
 <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>Commercial (disappeared?)</td>
 </tr>
 
@@ -533,6 +599,7 @@ incorrect.
 <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>Commercial (disappeared?)</td>
 </tr>
 
diff --git a/docs/downloads.html b/docs/downloads.html
index e089a0b..53b647a 100644
--- a/docs/downloads.html
+++ b/docs/downloads.html
@@ -42,7 +42,26 @@ interest too, because they typically contain any less urgent bug fixes
 collected since the previous release.
 <p>
 
-<h3><div>In beta</div> Version 4.0</h3>
+<h3><div>Dec 2007</div> Version 4.1</h3>
+<ul>
+<li>Fixed shrinking of default annotation element values.
+<li>Fixed optimization of invocations of methods in same class that are
+    accessed through extensions.
+<li>Fixed optimization of invocations of synchronized methods without other
+    side-effects.
+<li>Fixed optimization of some non-returning subroutines.
+<li>Fixed handling of local variable debug information when inlining methods.
+<li>Avoiding StackOverflowErrors during optimization of complex methods.
+<li>Fixed obfuscation of potentially ambiguous non-primitive constants in
+    interfaces.
+<li>Fixed preverification of some code constructs involving String, Class, and
+    exception types.
+<li>The Ant task now allows empty <code><injars></code> and
+    <code><libraryjars></code> elements.
+<li>Updated documentation and examples.
+</ul>
+
+<h3><div>Sep 2007</div> Version 4.0</h3>
 <ul>
 <li>Added preverifier for Java 6 and Java Micro Edition, with new options
     <code>-microedition</code> and <code>-dontpreverify</code>.
@@ -51,6 +70,9 @@ collected since the previous release.
 <li>Made <code>-keep</code> options more orthogonal and flexible, with option
     modifiers <code>allowshrinking</code>, <code>allowoptimization</code>, and
     <code>allowobfuscation</code>.
+<li>Added new wildcards for class member descriptors: "<code>***</code>",
+    matching any type, and "<code>...</code>", matching any number of
+    arguments.
 <li>Added support for configuration by means of annotations.
 <li>Improved shrinking of unused annotations.
 <li>Added check on modification times of input and output, to avoid unnecessary
@@ -76,6 +98,7 @@ collected since the previous release.
     specified methods.
 <li>Now printing notes for class names that don't seem to be fully qualified.
 <li>Added support for uppercase filename extensions.
+<li>Added tool tips to the GUI.
 <li>Rewritten class file I/O code.
 <li>Updated documentation and examples.
 </ul>
@@ -84,9 +107,9 @@ Upgrade considerations:
 
 <li>Since ProGuard now treats the <code>Exceptions</code> attribute as
     optional, you may have to specify <code>-keepattributes Exceptions</code>,
-    for instance when processing code that is to be used as a library.
+    notably when processing code that is to be used as a library.
 
-<li>ProGuard will now preverify code for Java Micro Edition, if you specify the
+<li>ProGuard now preverifies code for Java Micro Edition, if you specify the
     option <code>-microedition</code>. You then no longer need to process the
     code with an external preverifier.
 
@@ -94,6 +117,26 @@ Upgrade considerations:
     old option name <code>-defaultpackage</code>.
 </ul>
 
+<h3><div>Dec 2007</div> Version 3.11</h3>
+<ul>
+<li>Fixed optimization of invocations of methods in same class that are
+    accessed through extensions.
+<li>Fixed optimization of invocations of synchronized methods without other
+    side-effects.
+<li>Updated documentation and examples.
+</ul>
+
+<h3><div>Aug 2007</div> Version 3.10</h3>
+<ul>
+<li>Now handling mixed-case input class names when
+    <code>-dontusemixedcaseclassnames</code> is specified.
+<li>Fixed optimization of synchronization on classes, as compiled by Eclipse
+    and Jikes.
+<li>Fixed optimization of switch statements with unreachable cases.
+<li>Avoiding merging subsequent identically named files.
+<li>Updated documentation and examples.
+</ul>
+
 <h3><div>Jun 2007</div> Version 3.9</h3>
 <ul>
 <li>Fixed processing of .class constructs in Java 6.
diff --git a/docs/feedback.html b/docs/feedback.html
index 01880ae..6e10a3f 100644
--- a/docs/feedback.html
+++ b/docs/feedback.html
@@ -12,50 +12,45 @@
 <h2>Feedback</h2>
 
 By now, I've invested a fair amount of time in <b>ProGuard</b>. You can help
-by providing feedback! If you have good experiences, bad experiences,
-problems, bugs, bug fixes, ideas, encouragements, etc. to share, please go
-ahead:
+by providing feedback! If you have problems, bugs, bug fixes, ideas,
+encouragements, etc., please let me know:
 <p>
 <ul>
-<li>If you need help with <b>ProGuard</b>, please go to the <a
-    href="http://sourceforge.net/forum/forum.php?forum_id=182456"
-    target="other">help forum</a> (at <a
-    href="http://sourceforge.net/projects/proguard/"
-    target="other">SourceForge</a>).
+<li>The <a href="http://sourceforge.net/forum/forum.php?forum_id=182456"
+    target="other">help forum</a> (at SourceForge) is the right place to ask
+    questions about any problems you might have configuring and running
+    <b>ProGuard</b>.
     <p>
 
-<li>A good place to share your thoughts is the <a
-    href="http://sourceforge.net/forum/forum.php?forum_id=182455"
-    target="other">open discussion forum</a> (at <a
-    href="http://sourceforge.net/projects/proguard/"
-    target="other">SourceForge</a>).
+<li>The <a href="http://sourceforge.net/forum/forum.php?forum_id=182455"
+    target="other">open discussion forum</a> (at SourceForge) offers a place
+    to share your thoughts and discuss new ideas.
     <p>
 
-<li>You can submit and consult bug reports on the <a
+<li>The <a
 
     href="http://sourceforge.net/tracker/?atid=474704&group_id=54750&func=browse"
-    target="other">bug tracking page</a> (at <a
-    href="http://sourceforge.net/projects/proguard/"
-    target="other">SourceForge</a>).
+    target="other">bug tracking page</a> (at SourceForge) allows you to submit
+    and consult bug reports. Please make sure the reports are complete and
+    concise. If I can't reproduce the problem, I most likely can't fix it
+    either.
     <p>
 
-<li>Similarly, you can submit and consult feature requests on the <a
+<li>The <a
 
     href="http://sourceforge.net/tracker/?atid=474707&group_id=54750&func=browse"
-    target="other">feature request page</a> (at <a
-    href="http://sourceforge.net/projects/proguard/"
-    target="other">SourceForge</a>).
+    target="other">feature request page</a> (at SourceForge) allows you to
+    submit and consult feature requests. I generally have my own road map in
+    mind, but this is the place express your interest in new and existing
+    ideas.
     <p>
 
-<li>If you just want to stay abreast of the latest developments, you can
-    subscribe to the announcements of new releases in the <a
-    href="http://sourceforge.net/project/showfiles.php?group_id=54750"
-    target="other">download section</a> at <a
-    href="http://sourceforge.net/projects/proguard/"
-    target="other">SourceForge</a>, or the
-    <a href="http://software.freshmeat.net/projects/proguard/"
-    target="other">project page</a> at <a
-    href="http://software.freshmeat.net/">FreshMeat</a>.
+<li>The <a href="http://sourceforge.net/project/showfiles.php?group_id=54750"
+    target="other">download section</a> at SourceForge and the <a
+    href="http://software.freshmeat.net/projects/proguard/"
+    target="other">project page</a> at FreshMeat offer the possibility to
+    subscribe to the announcements of new releases. They are the most
+    efficient way to stay abreast of the latest developments.
     <p>
 
 <li>For anything that doesn't fall in the above categories, you can mail me
@@ -98,8 +93,8 @@ like seeing any constructive comments.
 
 <b>ProGuard</b> isn't a typical open source project, in the sense that I am
 <em>not</em> looking for code contributions. Developing on my own allows me to
-do things my way, without the overhead of project management and the
-compromises associated with larger projects.
+do things my way, without the overhead and compromises associated with larger
+projects.
 
 <hr>
 <address>
diff --git a/docs/index.html b/docs/index.html
index eb599d6..ce67e21 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -5,8 +5,8 @@
 <meta http-equiv="content-script-type" content="text/javascript">
 <meta http-equiv="content-style-type" content="text/css">
 <meta name="author" content="Eric Lafortune">
-<meta name="description" content="ProGuard: java shrinker, optimizer, and obfuscator">
-<meta name="keywords" content="java obfuscator, optimizer, shrinker">
+<meta name="description" content="ProGuard: java shrinker, optimizer, obfuscator, and preverifier">
+<meta name="keywords" content="java obfuscator, optimizer, shrinker, preverfier">
 <link rel="stylesheet" type="text/css" href="style.css">
 <link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
 <title>ProGuard</title>
diff --git a/docs/license.html b/docs/license.html
index 26fa84e..d01ca05 100644
--- a/docs/license.html
+++ b/docs/license.html
@@ -10,8 +10,7 @@
 
 <h2>License</h2>
 
-<b>ProGuard</b> is free. I wrote it for the challenge of it. And for eternal
-fame and glory, of course. You can use it freely for processing your
+<b>ProGuard</b> is free. You can use it freely for processing your
 applications, commercial or not. Your code obviously remains yours after
 having been processed, and its license can remain the same.
 <p>
@@ -19,18 +18,19 @@ having been processed, and its license can remain the same.
 <b>ProGuard</b> itself is copyrighted, but its distribution license provides
 you with some rights for modifying and redistributing its code and its
 documentation. More specifically, <b>ProGuard</b> is distributed under the
-terms of the <a href="GPL.html">GNU General Public License</a> (GPL), as
-published by the <a href="http://www.fsf.org/" target="other">Free Software
-Foundation</a> (FSF). In short, this means that you may freely redistribute
-the program, modified or as is, on the condition that you make the complete
-source code available as well. If you develop a program that is linked with
+terms of the <a href="GPL.html">GNU General Public License</a> (GPL), version
+2, as published by the <a href="http://www.fsf.org/" target="other">Free
+Software Foundation</a> (FSF). In short, this means that you may freely
+redistribute the program, modified or as is, on the condition that you make
+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"
 target="other">FSF</a>), for combinations with the following stand-alone
-applications: Apache Ant, Apache Maven, the Eclipse IDE, the Sun NetBeans IDE,
-the Sun J2ME Wireless Toolkit, and the Javaground Tools.
+applications: Apache Ant, Apache Maven, the Eclipse Java 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
diff --git a/docs/main.html b/docs/main.html
index 5b7c0e3..14b1d4c 100644
--- a/docs/main.html
+++ b/docs/main.html
@@ -4,8 +4,8 @@
 <meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
 <meta http-equiv="content-style-type" content="text/css">
 <meta name="author" content="Eric Lafortune">
-<meta name="description" content="ProGuard: java shrinker, optimizer, and obfuscator">
-<meta name="keywords" content="java obfuscator, optimizer, shrinker">
+<meta name="description" content="ProGuard: java shrinker, optimizer, obfuscator, and preverifier">
+<meta name="keywords" content="java obfuscator, optimizer, shrinker, preverfier">
 <link rel="stylesheet" type="text/css" href="style.css">
 <title>ProGuard Main</title>
 </head>
@@ -14,16 +14,27 @@
 <h2>Main</h2>
 
 <p class="intro">
-<b>ProGuard</b> is a free Java class file shrinker, optimizer, and obfuscator.
-It can detect and remove unused classes, fields, methods, and attributes. It
-can then optimize bytecode and remove unused instructions. Finally, it can
-rename the remaining classes, fields, and methods using short meaningless
-names. The resulting jars are smaller and harder to reverse-engineer.
+<b>ProGuard</b> is a free Java class file shrinker, optimizer, obfuscator, and
+preverifier. It detects and removes unused classes, fields, methods, and
+attributes. It optimizes bytecode and removes unused instructions. It renames
+the remaining classes, fields, and methods using short meaningless names.
+Finally, it preverifies the processed code for Java 6 or for Java Micro
+Edition.
 </p>
-<p>
-More compact jar files also means smaller storage requirements, faster
-transfer of applications across networks, faster loading, and smaller memory
-footprints.
+Some uses of <b>ProGuard</b> are:
+<ul>
+
+<li>Creating more compact code, for smaller code archives, faster transfer
+    across networks, faster loading, and smaller memory footprints.
+
+<li>Making programs and libraries harder to reverse-engineer.
+
+<li>Listing dead code, so it can be removed from the source code.
+
+<li>Retargeting and preverifying existing class files for Java 6, to take full
+    advantage of Java 6's faster class loading.
+
+</ul>
 <p>
 <b>ProGuard</b>'s main advantage compared to other Java obfuscators is
 probably its compact template-based configuration. A few intuitive command
@@ -40,13 +51,12 @@ libraries of several megabytes. The results section presents actual figures
 for a number of applications.
 <p>
 <b>ProGuard</b> is a command-line tool with an optional graphical user
-interface. It also comes with plugins for Ant and for the J2ME Wireless
+interface. It also comes with plugins for Ant and for the JME Wireless
 Toolkit.
 <p>
 <p class="intro">
-Version 3.0 introduced bytecode optimization, reading and writing of nested
-jars, and a brand new Ant task. Please report any problems, so they can be
-fixed soon.
+Version 4.0 introduced preverification and more bytecode optimizations. Please
+report any problems, so they can be fixed soon.
 </p>
 The following sections provide more detailed information:
 <ul>
diff --git a/docs/manual/ant.html b/docs/manual/ant.html
index 52a4401..4e9eb7e 100644
--- a/docs/manual/ant.html
+++ b/docs/manual/ant.html
@@ -116,120 +116,190 @@ elements:
 
 <dl>
 
-<dt><a name="configuration"><code><b>configuration</b></code></a> =
-    "<i>filename</i>"</dt>
+<dt><a name="configuration"><code><b>configuration</b></code></a>
+    = "<i>filename</i>"</dt>
 <dd>Read and merge options from the given ProGuard-style configuration
     file.</dd>
 
-<dt><code><b>skipnonpubliclibraryclasses</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#dontskipnonpubliclibraryclasses"><code><b>skipnonpubliclibraryclasses</b></code></a>
+    = "<i>boolean</i>"
     (default = true)</dt>
 <dd>Ignore non-public library classes.</dd>
 
-<dt><code><b>skipnonpubliclibraryclassmembers</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#dontskipnonpubliclibraryclassmembers"><code><b>skipnonpubliclibraryclassmembers</b></code></a>
+    = "<i>boolean</i>"
     (default = true)</dt>
 <dd>Ignore package visible library class members.</dd>
 
-<dt><code><b>printseeds</b></code> = "<i>boolean or filename</i>"
+<dt><a href="usage.html#target"><code><b>target</b></code></a>
+    = "<i>version</i>"
+    (default = none)</dt>
+<dd>Set the given version number in the processed classes.</dd>
+
+<dt><a href="usage.html#forceprocessing"><code><b>forceprocessing</b></code></a>
+    = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Process the input, even if the output seems up to date.</dd>
+
+<dt><a href="usage.html#printseeds"><code><b>printseeds</b></code></a>
+    = "<i>boolean or filename</i>"
     (default = false)</dt>
 <dd>List classes and class members matched by the various <code>keep</code>
     commands, to the standard output or to the given file.</dd>
 
-<dt><code><b>shrink</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dt><a href="usage.html#dontshrink"><code><b>shrink</b></code></a>
+    = "<i>boolean</i>"
+    (default = true)</dt>
 <dd>Shrink the input class files.</dd>
 
-<dt><code><b>printusage</b></code> = "<i>boolean or filename</i>"
+<dt><a href="usage.html#printusage"><code><b>printusage</b></code></a>
+    = "<i>boolean or filename</i>"
     (default = false)</dt>
 <dd>List dead code of the input class files, to the standard output or to the
     given file.</dd>
 
-<dt><code><b>optimize</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dt><a href="usage.html#dontoptimize"><code><b>optimize</b></code></a>
+    = "<i>boolean</i>"
+    (default = true)</dt>
 <dd>Optimize the input class files.</dd>
 
-<dt><code><b>allowaccessmodification</b></code> = "<i>boolean</i>"
+<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.
+
+<dt><a href="usage.html#allowaccessmodification"><code><b>allowaccessmodification</b></code></a>
+    = "<i>boolean</i>"
     (default = false)</dt>
 <dd>Allow the access modifiers of classes and class members to be modified,
     while optimizing.</dd>
 
-<dt><code><b>obfuscate</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dt><a href="usage.html#dontobfuscate"><code><b>obfuscate</b></code></a>
+    = "<i>boolean</i>"
+   (default = true)</dt>
 <dd>Obfuscate the input class files.</dd>
 
-<dt><code><b>printmapping</b></code> = "<i>boolean or filename</i>"
+<dt><a href="usage.html#printmapping"><code><b>printmapping</b></code></a>
+    = "<i>boolean or filename</i>"
     (default = false)</dt>
 <dd>Print the mapping from old names to new names for classes and class members
     that have been renamed, to the standard output or to the given file.</dd>
 
-<dt><code><b>applymapping</b></code> = "<i>filename</i>"</dt>
+<dt><a href="usage.html#applymapping"><code><b>applymapping</b></code></a>
+    = "<i>filename</i>"
+    (default = none)</dt>
 <dd>Reuse the given mapping, for incremental obfuscation.</dd>
 
-<dt><code><b>obfuscationdictionary</b></code> = "<i>filename</i>"</dt>
+<dt><a href="usage.html#obfuscationdictionary"><code><b>obfuscationdictionary</b></code></a>
+    = "<i>filename</i>"
+    (default = none)</dt>
 <dd>Use the words in the given text file as obfuscated method names.</dd>
 
-<dt><code><b>overloadaggressively</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#overloadaggressively"><code><b>overloadaggressively</b></code></a>
+    = "<i>boolean</i>"
     (default = false)</dt>
 <dd>Apply aggressive overloading while obfuscating.</dd>
 
-<dt><code><b>useuniqueclassmembernames</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#useuniqueclassmembernames"><code><b>useuniqueclassmembernames</b></code></a>
+    = "<i>boolean</i>"
     (default = false)</dt>
 <dd>Ensure uniform obfuscated class member names for subsequent incremental
     obfuscation.</dd>
 
-<dt><code><b>defaultpackage</b></code> = "<i>package_name</i>"</dt>
+<dt><a href="usage.html#dontusemixedcaseclassnames"><code><b>usemixedcaseclassnames</b></code></a>
+    = "<i>boolean</i>"
+    (default = true)</dt>
+<dd>Generate mixed-case class names while obfuscating.</dd>
+
+<dt><a href="usage.html#flattenpackagehierarchy"><code><b>flattenpackagehierarchy</b></code></a>
+    = "<i>package_name</i>"
+    (default = none)</dt>
+<dd>Repackage all packages that are renamed into the single given parent
+    package.</dd>
+
+<dt><a href="usage.html#repackageclasses"><code><b>repackageclasses</b></code></a>
+    = "<i>package_name</i>"
+    (default = none)</dt>
 <dd>Repackage all class files that are renamed into the single given
     package.</dd>
 
-<dt><code><b>usemixedcaseclassnames</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#renamesourcefileattribute"><code><b>renamesourcefileattribute</b></code></a>
+    = "<i>string</i>"
+    (default = none)</dt>
+<dd>Put the given constant string in the <code>SourceFile</code>
+    attributes.</dd>
+
+<dt><a href="usage.html#dontpreverify"><code><b>preverify</b></code></a>
+    = "<i>boolean</i>"
     (default = true)</dt>
-<dd>Generate mixed-case class names while obfuscating.</dd>
+<dd>Preverify the processed class files if they are targeted at Java Micro
+    Edition or at Java 6 or higher.</dd>
 
-<dt><code><b>renamesourcefileattribute</b></code> = "<i>string</i>"</dt>
-<dd>Put the given constant string in the <code>SourceFile</code>
-    attributes. The default is to keep the original string.</dd>
+<dt><a href="usage.html#microedition"><code><b>microedition</b></code></a>
+    = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Targets the processed class files at Java Micro Edition.</dd>
 
-<dt><code><b>verbose</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#verbose"><code><b>verbose</b></code></a>
+    = "<i>boolean</i>"
     (default = false)</dt>
 <dd>Write out some more information during processing.</dd>
 
-<dt><code><b>note</b></code> = "<i>boolean</i>" (default = true)</dt>
+<dt><a href="usage.html#dontnote"><code><b>note</b></code></a>
+    = "<i>boolean</i>"
+   (default = true)</dt>
 <dd>Print notes about class casts of variable dynamically created objects.</dd>
 
-<dt><code><b>warn</b></code> = "<i>boolean</i>" (default = true)</dt>
+<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>
 
-<dt><code><b>ignorewarnings</b></code> = "<i>boolean</i>"
+<dt><a href="usage.html#ignorewarnings"><code><b>ignorewarnings</b></code></a>
+    = "<i>boolean</i>"
     (default = false)</dt>
 <dd>Print warnings about unresolved references, but continue processing
-    anyhow. The default is not to ignore warnings. <i>Only use this option if
-    you know what you're doing!</i></dd>
+    anyhow. <i>Only use this option if you know what you're doing!</i></dd>
 
-<dt><code><b>dump</b></code> = "<i>boolean or filename</i>"
+<dt><a href="usage.html#printconfiguration"><code><b>printconfiguration</b></code></a>
+    = "<i>boolean or filename</i>"
+    (default = false)</dt>
+<dd>Write out the entire configuration in traditional ProGuard style, to the
+    standard output or to the given file. Useful to replace unreadable
+    XML configurations.</dd>
+
+<dt><a href="usage.html#dump"><code><b>dump</b></code></a>
+    = "<i>boolean or filename</i>"
     (default = false)</dt>
 <dd>Write out the internal structure of the processed class files, to the
     standard output or to the given file.</dd>
 
-<dt><code><b><injar</b></code>
+<dt><a href="usage.html#injars"><code><b><injar</b></code></a>
     <a href="#classpath"><i>class_path</i></a>
     <code><b>/></b></code></dt>
 <dd>Specifies the program jars (or wars, ears, zips, or directories).</dd>
 
-<dt><code><b><outjar</b></code>
+<dt><a href="usage.html#outjars"><code><b><outjar</b></code></a>
     <a href="#classpath"><i>class_path</i></a>
     <code><b>/></b></code></dt>
 <dd>Specifies the name of the output jars (or wars, ears, zips, or
     directories).</dd>
 
-<dt><code><b><libraryjar</b></code>
+<dt><a href="usage.html#libraryjars"><code><b><libraryjar</b></code></a>
     <a href="#classpath"><i>class_path</i></a>
     <code><b>/></b></code></dt>
 <dd>Specifies the library jars (or wars, ears, zips, or directories).</dd>
 
-<dt><code><b><keep</b></code>
+<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>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
     <code><b></keep></b></code></dt>
 <dd>Preserve the specified classes <i>and</i> class members.</dd>
 
-<dt><code><b><keepclassmembers</b></code>
+<dt><a href="usage.html#keepclassmembers"><code><b><keepclassmembers</b></code></a>
+    <a href="#keepmodifier"><i>modifiers</i></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -237,7 +307,8 @@ elements:
 <dd>Preserve the specified class members, if their classes are preserved as
     well.</dd>
 
-<dt><code><b><keepclasseswithmembers</b></code>
+<dt><a href="usage.html#keepclasseswithmembers"><code><b><keepclasseswithmembers</b></code></a>
+    <a href="#keepmodifier"><i>modifiers</i></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -245,7 +316,7 @@ elements:
 <dd>Preserve the specified classes <i>and</i> class members, if all of the
     specified class members are present.</dd>
 
-<dt><code><b><keepnames</b></code>
+<dt><a href="usage.html#keepnames"><code><b><keepnames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -253,7 +324,7 @@ elements:
 <dd>Preserve the names of the specified classes <i>and</i> class members (if
     they aren't removed in the shrinking step).</dd>
 
-<dt><code><b><keepclassmembernames</b></code>
+<dt><a href="usage.html#keepclassmembernames"><code><b><keepclassmembernames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -261,7 +332,7 @@ elements:
 <dd>Preserve the names of the specified class members (if they aren't removed
     in the shrinking step).</dd>
 
-<dt><code><b><keepclasseswithmembernames</b></code>
+<dt><a href="usage.html#keepclasseswithmembernames"><code><b><keepclasseswithmembernames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -270,7 +341,7 @@ elements:
     all of the specified class members are present (after the shrinking
     step).</dd>
 
-<dt><code><b><whyareyoukeeping</b></code>
+<dt><a href="usage.html#whyareyoukeeping"><code><b><whyareyoukeeping</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -278,7 +349,7 @@ elements:
 <dd>Print details on why the given classes and class members are being kept in
     the shrinking step.</dd>
 
-<dt><code><b><assumenosideeffects</b></code>
+<dt><a href="usage.html#assumenosideeffects"><code><b><assumenosideeffects</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a>
     <code><b>></b></code>
     <a href="#classmemberspecification"><i>class_member_specifications</i></a>
@@ -286,11 +357,23 @@ elements:
 <dd>Assume that the specified methods don't have any side effects, while
     optimizing.</dd>
 
-<dt><code><b><keepattribute name = </b></code>"<i>attribute_name</i>"
+<dt><a href="usage.html#keepattributes"><code><b><keepattribute name = </b></code></a>"<i>attribute_name</i>"
     <code><b>/></b></code></dt>
 <dd>Preserve the given optional Java bytecode attribute, with optional
     wildcards. If no name is given, all attributes are preserved.</dd>
 
+<dt><a href="usage.html#adaptresourcefilenames"><code><b><adaptresourcefilenames</b></code></a>
+    = "<a href="usage.html#filters"><i>filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Rename the specified resource files, based on the obfuscated names of the
+    corresponding class files.</dd>
+
+<dt><a href="usage.html#adaptresourcefilecontents"><code><b><adaptresourcefilecontents</b></code></a>
+    = "<a href="usage.html#filters"><i>filter</i></a>"
+    <code><b>/></b></code></dt>
+<dd>Update the contents of the specified resource files, based on the
+    obfuscated names of the class files.</dd>
+
 <dt><code><b><configuration refid = </b></code>"<i>ref_id</i>"
     <code><b>/></b></code></dt>
 <dd>Includes the configuration specified in the
@@ -325,29 +408,60 @@ nested elements). The most common attributes are:
 
 </dl>
 
-In addition, the jar tags can have ProGuard-style filter attributes (as
-described in the section on <a href="usage.html#filters">Filters</a>):
+In addition, the jar tags can have ProGuard-style filter attributes:
 
 <dl>
 
-<dt><code><b>filter</b></code> = "<i>filter</i>"</dt>
+<dt><code><b>filter</b></code> =
+    "<a href="usage.html#filters"><i>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> = "<i>jar_filter</i>"</dt>
+<dt><code><b>jarfilter</b></code> =
+    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
 <dd>An optional filter for all jar names that are encountered.</dd>
 
-<dt><code><b>warfilter</b></code> = "<i>war_filter</i>"</dt>
+<dt><code><b>warfilter</b></code> =
+    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
 <dd>An optional filter for all war names that are encountered.</dd>
 
-<dt><code><b>earfilter</b></code> = "<i>ear_filter</i>"</dt>
+<dt><code><b>earfilter</b></code> =
+    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
 <dd>An optional filter for all ear names that are encountered.</dd>
 
-<dt><code><b>zipfilter</b></code> = "<i>zip_filter</i>"</dt>
+<dt><code><b>zipfilter</b></code> =
+    "<a href="usage.html#filters"><i>filter</i></a>"</dt>
 <dd>An optional filter for all zip names that are encountered.</dd>
 
 </dl>
 
+<a name="keepmodifier"> </a>
+<h2>Keep Modifier Attributes</h2>
+
+The keep tags can have the following <i>modifier</i> attributes:
+
+<dl>
+
+<dt><a href="usage.html#allowshrinking"><code><b>allowshrinking</b></code></a>
+    = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Specifies whether the entry points specified in the keep tag may be
+    shrunk.</dd>
+
+<dt><a href="usage.html#allowoptimization"><code><b>allowoptimization</b></code></a>
+    = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Specifies whether the entry points specified in the keep tag may be
+    optimized.</dd>
+
+<dt><a href="usage.html#allowobfuscation"><code><b>allowobfuscation</b></code></a>
+    = "<i>boolean</i>"
+    (default = false)</dt>
+<dd>Specifies whether the entry points specified in the keep tag may be
+    obfuscated.</dd>
+
+</dl>
+
 <a name="classspecification"> </a>
 <h2>Class Specification Attributes and Nested Elements</h2>
 
diff --git a/docs/manual/examples.html b/docs/manual/examples.html
index 8ca6f7d..8f4fad9 100644
--- a/docs/manual/examples.html
+++ b/docs/manual/examples.html
@@ -19,22 +19,28 @@ Some typical useful configurations:
 <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="#servlets">All possible servlets in the input jars</a>
-<li><a href="#library">Processing a library</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>
 <li><a href="#serializable">Processing serializable classes</a>
 <li><a href="#beans">Processing bean classes</a>
-<li><a href="#enumerations">Processing enumeration classes</a>
 <li><a href="#annotations">Processing annotations</a>
 <li><a href="#database">Processing database drivers</a>
 <li><a href="#componentui">Processing ComponentUI classes</a>
 <li><a href="#rmi">Processing RMI code</a>
+<li><a href="#resourcefiles">Processing resource files</a>
 <li><a href="#stacktrace">Producing useful obfuscated stack traces</a>
+<li><a href="#repackaging">Obfuscating package names</a>
 <li><a href="#restructuring">Restructuring the output archives</a>
 <li><a href="#filtering">Filtering the input and the output</a>
 <li><a href="#multiple">Processing multiple applications at once</a>
 <li><a href="#incremental">Incremental obfuscation</a>
+<li><a href="#microedition">Preverifying class files for Java Micro Edition</a>
+<li><a href="#upgrade">Upgrading class files to Java 6</a>
 <li><a href="#deadcode">Finding dead code</a>
 <li><a href="#structure">Printing out the internal structure of class files</a>
+<li><a href="#annotated">Using annotations to configure ProGuard</a>
 </ol>
 
 You can find some sample configuration files in the <code>examples</code>
@@ -48,16 +54,12 @@ typically create a configuration file <code>proguard.pro</code> and then type:
 java -jar proguard.jar @proguard.pro
 </pre>
 <p>
-
 The configuration file would contain the following options:
 <pre>
 -injars       proguard.jar
 -outjars      proguard_out.jar
 -libraryjars  <java.home>/lib/rt.jar
 -printmapping proguard.map
--overloadaggressively
--defaultpackage ''
--allowaccessmodification
 
 -keep public class proguard.ProGuard {
     public static void main(java.lang.String[]);
@@ -67,31 +69,35 @@ The configuration file would contain the following options:
 Note the use of the <code><java.home></code> system property; it is
 replaced automatically.
 <p>
-Also note that the type names are fully specified:
+Also note that all type names are fully specified:
 <code>proguard.ProGuard</code> and <code>java.lang.String[]</code>.
 <p>
 The access modifiers <code>public</code> and <code>static</code> are not
 really required in this case, since we know a priori that the specified class
 and method have the proper access flags. It just looks more familiar this way.
 <p>
-We're using the <a
-href="usage.html#overloadaggressively"><code>-overloadaggressively</code></a>,
-<a href="usage.html#defaultpackage"><code>-defaultpackage</code></a>, and <a
-href="usage.html#allowaccessmodification"><code>-allowaccessmodification</code></a>
-options to shave off some extra bytes, but we could leave them out as well.
-The <a href="usage.html#defaultpackage"><code>-defaultpackage</code></a>
-directive moves all classes to the given package, in this case the root
-package. Only <code>proguard.ProGuard</code> keeps its original name.
-<p>
 We're writing out an obfuscation mapping file with <a
 href="usage.html#printmapping"><code>-printmapping</code></a>, for
 de-obfuscating any stack traces later on, or for incremental obfuscation of
 extensions.
 <p>
-In general, you might need a few additional directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>. For processing 'simple' applications like ProGuard, that is not
+We can further improve the results with a few additional options:
+<pre>
+-optimizationpasses 3
+-overloadaggressively
+-repackageclasses ''
+-allowaccessmodification
+</pre>
+These options are not required; they just shave off some extra bytes from the
+output jar, by performing up to 3 optimization passes, and by aggressively
+obfuscating class members and <a href="#repackaging">package names</a>.
+<p>
+In general, you might need a few additional options for processing <a
+href="#native">native methods</a>, <a href="#callback">callback methods</a>,
+<a href="#enumerations">enumerations</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, <a
+href="#annotations">annotations</a>, and <a href="#resourcefiles">resource
+files</a>. For processing 'simple' applications like ProGuard, that is not
 required.
 
 <a name="applet"> </a>
@@ -110,14 +116,16 @@ The typical applet methods will be preserved automatically, since
 <code>mypackage.MyApplet</code> is an extension of the <code>Applet</code>
 class in the library <code>rt.jar</code>.
 <p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+If applicable, you should add options for processing <a href="#native">native
+methods</a>, <a href="#callback">callback methods</a>, <a
+href="#enumerations">enumerations</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, <a
+href="#annotations">annotations</a>, and <a href="#resourcefiles">resource
+files</a>.
 
 <a name="midlet"> </a>
 <h3>A typical midlet</h3>
-These options shrink, optimize, and obfuscate the J2ME midlet
+These options shrink, optimize, obfuscate, and preverify the midlet
 <code>mypackage.MyMIDlet</code>:
 <pre>
 -injars      in.jar
@@ -125,31 +133,35 @@ These options shrink, optimize, and obfuscate the J2ME midlet
 -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
 -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
 -overloadaggressively
--defaultpackage ''
+-repackageclasses ''
 -allowaccessmodification
+-microedition
 
 -keep public class mypackage.MyMIDlet
 </pre>
 <p>
-Note how we're now targeting the J2ME run-time environment of
-<code>midpapi20.jar</code> and <code>cldcapi11.jar</code>, instead of the J2SE
-run-time environment <code>rt.jar</code>. You can target other J2ME
-environments by picking the appropriate jars.
+Note how we're now targeting the Java Micro Edition run-time environment of
+<code>midpapi20.jar</code> and <code>cldcapi11.jar</code>, instead of the Java
+Standard Edition run-time environment <code>rt.jar</code>. You can target
+other JME environments by picking the appropriate jars.
 <p>
 The typical midlet methods will be preserved automatically, since
 <code>mypackage.MyMIDlet</code> is an extension of the <code>MIDlet</code>
 class in the library <code>midpapi20.jar</code>.
 <p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+The <a href="usage.html#microedition"><code>-microedition</code></a> option
+makes sure the class files are preverified for Java Micro Edition, producing
+compact <code>StackMap</code> attributes. It is no longer necessary to run an
+external preverifier.
 <p>
-You must preverify your midlets <i>after</i> having processed them, using
-J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
-jars, you should use ProGuard's <a
+Be careful if you do use the external <code>preverify</code> tool on a platform
+with a case-insensitive filing system, such as Windows. Because this tool
+unpacks your processed jars, you should then use ProGuard's <a
 href="usage.html#dontusemixedcaseclassnames"><code>-dontusemixedcaseclassnames</code></a>
-option on platforms with case-insensitive filing systems, such as Windows.
+option.
+<p>
+If applicable, you should add options for processing <a href="#native">native
+methods</a> and <a href="#resourcefiles">resource files</a>.
 <p>
 Please note the in-depth article <a
 href="http://developers.sun.com/techtopics/mobility/midp/ttips/proguard/">"Obfuscating
@@ -180,10 +192,12 @@ The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
 out which classes exactly will be preserved, so we know for sure we're getting
 what we want.
 <p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+If applicable, you should add options for processing <a href="#native">native
+methods</a>, <a href="#callback">callback methods</a>, <a
+href="#enumerations">enumerations</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, <a
+href="#annotations">annotations</a>, and <a href="#resourcefiles">resource
+files</a>.
 
 <a name="applets"> </a>
 <h3>All possible applets in the input jars</h3>
@@ -203,14 +217,16 @@ We're simply keeping all classes that extend the <code>Applet</code> class.
 Again, the <a href="usage.html#printseeds"><code>-printseeds</code></a> option
 prints out which applets exactly will be preserved.
 <p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+If applicable, you should add options for processing <a href="#native">native
+methods</a>, <a href="#callback">callback methods</a>, <a
+href="#enumerations">enumerations</a>, <a href="#serializable">serializable
+classes</a>, <a href="#beans">bean classes</a>, <a
+href="#annotations">annotations</a>, and <a href="#resourcefiles">resource
+files</a>.
 
 <a name="midlets"> </a>
 <h3>All possible midlets in the input jars</h3>
-These options shrink, optimize, and obfuscate all public J2ME midlets in
+These options shrink, optimize, obfuscate, and preverify all public midlets in
 <code>in.jar</code>:
 <pre>
 -injars      in.jar
@@ -218,8 +234,9 @@ These options shrink, optimize, and obfuscate all public J2ME midlets in
 -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
 -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
 -overloadaggressively
--defaultpackage ''
+-repackageclasses ''
 -allowaccessmodification
+-microedition
 -printseeds
 
 -keep public class * extends javax.microedition.midlet.MIDlet
@@ -227,19 +244,22 @@ These options shrink, optimize, and obfuscate all public J2ME midlets in
 <p>
 We're simply keeping all classes that extend the <code>MIDlet</code> class.
 <p>
-And again, the <a href="usage.html#printseeds"><code>-printseeds</code></a>
-option prints out which midlets exactly will be preserved.
-<p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+The <a href="usage.html#microedition"><code>-microedition</code></a> option
+makes sure the class files are preverified for Java Micro Edition, producing
+compact <code>StackMap</code> attributes. It is no longer necessary to run an
+external preverifier.
 <p>
-You must preverify your midlets <i>after</i> having processed them, using
-J2ME's <code>preverify</code> tool. Because this tool unpacks your processed
-jars, you should use ProGuard's <a
+Be careful if you do use the external <code>preverify</code> tool on a platform
+with a case-insensitive filing system, such as Windows. Because this tool
+unpacks your processed jars, you should then use ProGuard's <a
 href="usage.html#dontusemixedcaseclassnames"><code>-dontusemixedcaseclassnames</code></a>
-option on platforms with case-insensitive filing systems, such as Windows.
+option.
+<p>
+The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
+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>.
 
 <a name="servlets"> </a>
 <h3>All possible servlets in the input jars</h3>
@@ -264,16 +284,18 @@ interface. We're using the <code>implements</code> keyword because it looks
 more familiar in this context, but it is equivalent to <code>extends</code>,
 as far as ProGuard is concerned.
 <p>
-And again, the <a href="usage.html#printseeds"><code>-printseeds</code></a>
-option prints out which servlets exactly will be preserved.
-<p>
-If applicable, you should add directives for processing <a
-href="#native">native methods</a>, <a href="#enumerations">enumerations</a>,
-<a href="#serializable">serializable classes</a>, or <a href="#beans">bean
-classes</a>.
+The <a href="usage.html#printseeds"><code>-printseeds</code></a> option prints
+out which servlets 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>, <a
+href="#enumerations">enumerations</a>, <a href="#serializable">serializable
+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>Processing a library</h3>
+<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:
@@ -284,8 +306,8 @@ serialization code:
 -printmapping out.map
 
 -renamesourcefileattribute SourceFile
--keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated,
-                Signature,*Annotation*,EnclosingMethod
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
+                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
 
 -keep public class * {
     public protected *;
@@ -317,11 +339,11 @@ serialization code:
 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> directives.
+href="usage.html#keep"><code>-keep</code></a> options.
 <p>
 The <a
 href="usage.html#keepclassmembernames"><code>-keepclassmembernames</code></a>
-directive for the <code>class$</code> methods is not strictly necessary. These
+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
@@ -330,20 +352,25 @@ 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>
-We've added some directives for keeping the "Deprecated" attribute, for
-producing <a href="#stacktrace">useful stack traces</a>, and 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.
+The "Exceptions" attribute is 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 as well, 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.
+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.
+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>
@@ -364,13 +391,30 @@ keep the relevant names from being obfuscated.
 <p>
 ProGuard doesn't have your native code, so it can't automatically preserve the
 classes or class members that are invoked by the native code. These are entry
-points, which you'll have to specify explicitly.
+points, which you'll have to specify explicitly. <a href="callback">Callback
+methods</a> are discussed below as a typical example.
+
+<a name="callback"> </a>
+<h3>Processing callback methods</h3>
+If your application, applet, servlet, library, etc., contains callback
+methods, which are called from external code (native code, scripts,...),
+you'll want to preserve them, and probably their classes too. They are just
+entry points to your code, much like, say, the main method of an application.
+If they aren't preserved by other <code>-keep</code> options, something like
+the following option will keep the callback class and method:
+<pre>
+-keep class mypackage.MyCallbackClass {
+    void myCallbackMethod(java.lang.String);
+}
+</pre>
+<p>
+This will preserve the given class and method from being removed or renamed.
 
 <a name="enumerations"> </a>
 <h3>Processing enumeration classes</h3>
 If your application, applet, servlet, library, etc., contains enumeration
 classes, you'll have to preserve some special methods. Enumerations were
-introduced in JDK 5.0. The java compiler translates enumerations into classes
+introduced in Java 5. The java compiler translates enumerations into classes
 with a special structure. Notably, the classes contain implementations of some
 static methods that the run-time environment accesses by introspection (Isn't
 that just grand? Introspection is the self-modifying code of a new
@@ -392,7 +436,7 @@ may require special attention:
 
 <li>Often, serialization is simply a means of transporting data, without
     long-term storage. Classes that are shrunk and obfuscated should then
-    continue to function fine with the following additional directives:
+    continue to function fine with the following additional options:
 
 <pre>
 -keepclassmembers class * implements java.io.Serializable {
@@ -407,7 +451,7 @@ may require special attention:
     The <a
     href="usage.html#keepclassmembers"><code>-keepclassmembers</code></a>
     option makes sure that any serialization methods are kept. By using this
-    directive instead of the basic <code>-keep</code> directive, we're not
+    option instead of the basic <code>-keep</code> option, we're not
     forcing preservation of <i>all</i> serializable classes, just preservation
     of the listed members of classes that are actually used.
     <p>
@@ -416,7 +460,7 @@ may require special attention:
     versions of the serializable classes. One then has to take care the classes
     remain compatible with their unprocessed versions and with future
     processed versions. In such cases, the relevant classes will most likely
-    have <code>serialVersionUID</code> fields. The following directives should
+    have <code>serialVersionUID</code> fields. The following options should
     then be sufficient to ensure compatibility over time:
 
 <pre>
@@ -470,7 +514,7 @@ may require special attention:
 </pre>
 <p>
 
-    The new directives force preservation of the elements involved in the UID
+    The new options force preservation of the elements involved in the UID
     computation. In addition, the user will have to manually specify all
     interfaces of the serializable classes (using something like "<code>-keep
     interface MyInterface</code>"), since these names are also used when
@@ -480,7 +524,7 @@ may require special attention:
 </ul>
 <p>
 
-Note that the above directives may preserve more classes and class members
+Note that the above options may preserve more classes and class members
 than strictly necessary. For instance, a large number of classes may implement
 the <code>Serialization</code> interface, yet only a small number may actually
 ever be serialized. Knowing your application and tuning the configuration
@@ -488,9 +532,9 @@ often produces more compact results.
 
 <a name="beans"> </a>
 <h3>Processing bean classes</h3>
-If your application, applet, servlet, library, etc., uses extensive
+If your application, applet, servlet, library, etc., makes extensive use of
 introspection on bean classes to find bean editor classes, or getter and
-setter methods, then configuration may become laborious. There's not much else
+setter methods, then configuration may become painful. There's not much else
 you can do than making sure the bean class names, or the getter and setter
 names don't change. For instance:
 <pre>
@@ -503,39 +547,34 @@ names don't change. For instance:
 </pre>
 <p>
 If there are too many elements to list explicitly, wildcards in class names
-and method signatures might be helpful. This example encompasses a wide range
-of setters and getters:
+and method signatures might be helpful. This example should encompasses all
+possible setters and getters in classes in the package <code>mybeans</code>:
 <pre>
 -keep class mybeans.** {
-    void set*(%);
-    void set*(**);
-    void set*(%[]);
-    void set*(**[]);
-    void set*(int, %);
-    void set*(int, **);
-
-    %    get*();
-    **   get*();
-    %[]  get*();
-    **[] get*();
-    %    get*(int);
-    **   get*(int);
+    void set*(***);
+    void set*(int, ***);
+
+    boolean is*(); 
+    boolean is*(int);
+
+    *** get*();
+    *** get*(int);
 }
 </pre>
 <p>
-The '<code>%</code>' wildcard matches any primitive type, while the
-'<code>**</code>' wildcard matches any class name. Array types  have to be
-specified explicitly. This results in a short list of alternative method
-signatures.
+The '<code>***</code>' wildcard matches any type (primitive or non-primitive,
+array or non-array). The methods with the '<code>int</code>' arguments matches
+properties that are lists.
 
 <a name="annotations"> </a>
 <h3>Processing annotations</h3>
-As of JDK 5.0, class files can contain annotations. Annotations are
-represented by attributes that have no direct effect on the execution of the
-code. However, their values can be retrieved through introspection, allowing
-developers to adapt the execution behavior accordingly. By default, ProGuard
-treats annotation attributes as optional, and removes them in the obfuscation
-step. If they are required, you'll have to specify this explicitly:
+If your application, applet, servlet, library, etc., uses annotations, you may
+want to preserve them in the processed output. Annotations are represented by
+attributes that have no direct effect on the execution of the code. However,
+their values can be retrieved through introspection, allowing developers to
+adapt the execution behavior accordingly. By default, ProGuard treats
+annotation attributes as optional, and removes them in the obfuscation step.
+If they are required, you'll have to specify this explicitly:
 <pre>
 -keepattributes *Annotation*
 </pre>
@@ -565,7 +604,7 @@ implementations that you are processing as entry points:
 -keep class * implements java.sql.Driver
 </pre>
 <p>
-This directive also gets rid of the note that ProGuard prints out about
+This option also gets rid of the note that ProGuard prints out about
 <code>(java.sql.Driver)Class.forName</code> constructs, if you are
 instantiating a driver in your code (without necessarily implementing any
 drivers yourself).
@@ -583,7 +622,7 @@ point, for instance like this:
 }
 </pre>
 <p>
-This directive also keeps the classes themselves.
+This option also keeps the classes themselves.
 
 <a name="rmi"> </a>
 <h3>Processing RMI code</h3>
@@ -591,6 +630,8 @@ Reportedly, the easiest way to handle RMI code is to process the code with
 ProGuard first and then invoke the <code>rmic</code> tool. If that is not
 possible, you may want to try something like this:
 <pre>
+-keepattributes Exceptions
+
 -keep interface * extends java.rmi.Remote {
     <methods>;
 }
@@ -600,9 +641,35 @@ possible, you may want to try something like this:
 }
 </pre>
 <p>
-The first directive keeps all your Remote interfaces and their methods. The
-second one keeps all the implementations, along with their particular RMI
-constructors, if any.
+The first <code>-keep</code> option keeps all your Remote interfaces and their
+methods. The second one keeps all the implementations, along with their
+particular RMI constructors, if any.
+<p>
+The <code>Exceptions</code> attribute has to be kept too, because the RMI
+handling code performs introspection to check whether the method signatures
+are compatible.
+
+<a name="resourcefiles"> </a>
+<h3>Processing resource files</h3>
+If your application, applet, servlet, library, etc., contains resource files,
+it may be necessary to adapt their names and/or their contents when the
+application is obfuscated. The following two options can achieve this
+automatically:
+<pre>
+-adaptresourcefilenames    **.properties,**.gif,**.jpg
+-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF
+</pre>
+<p>
+The <a href="usage.html#adaptresourcefilenames">-adaptresourcefilenames</a>
+option in this case renames properties files and image files in the processed
+output, based on the obfuscated names of their corresponding class files (if
+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.
 
 <a name="stacktrace"> </a>
 <h3>Producing useful obfuscated stack traces</h3>
@@ -630,6 +697,113 @@ their original names, so we're saving the mapping to a file
 <code>out.map</code>. The information can then be used by the <a
 href="retrace/index.html">ReTrace</a> tool to restore the original stack trace.
 
+<a name="repackaging"> </a>
+<h3>Obfuscating package names</h3>
+Package names can be obfuscated in various ways, with increasing levels of
+obfuscation and compactness. For example, consider the following classes:
+<pre>
+mycompany.myapplication.MyMain
+mycompany.myapplication.Foo
+mycompany.myapplication.Bar
+mycompany.myapplication.extra.FirstExtra
+mycompany.myapplication.extra.SecondExtra
+mycompany.util.FirstUtil
+mycompany.util.SecondUtil
+</pre>
+<p>
+Let's assume the class name <code>mycompany.myapplication.MyMain</code> is the
+main application class that is kept by the configuration. All other class names
+can be obfuscated.
+<p>
+By default, packages that contain classes that can't be renamed aren't renamed
+either, and the package hierarchy is preserved. This results in obfuscated
+class names like these:
+<pre>
+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+mycompany.myapplication.a.a
+mycompany.myapplication.a.b
+mycompany.a.a
+mycompany.a.b
+</pre>
+<p>
+The <a
+href="usage.html#flattenpackagehierarchy"><code>-flattenpackagehierarchy</code></a>
+option obfuscates the package names further, by flattening the package
+hierarchy of obfuscated packages:
+<pre>
+-flattenpackagehierarchy 'myobfuscated'
+</pre>
+<p>
+The obfuscated class names then look as follows:
+<pre>
+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+myobfuscated.a.a
+myobfuscated.a.b
+myobfuscated.b.a
+myobfuscated.b.b
+</pre>
+<p>
+Alternatively, the <a
+href="usage.html#repackageclasses"><code>-repackageclasses</code></a> option
+obfuscates the entire packaging, by combining obfuscated classes into a single
+package:
+<pre>
+-repackageclasses 'myobfuscated'
+</pre>
+The obfuscated class names then look as follows:
+<pre>
+mycompany.myapplication.MyMain
+mycompany.myapplication.a
+mycompany.myapplication.b
+myobfuscated.a
+myobfuscated.b
+myobfuscated.c
+myobfuscated.d
+</pre>
+<p>
+Additionally specifying the <a
+href="usage.html#allowaccessmodification"><code>-allowaccessmodification</code></a>
+option allows access permissions of classes and class members to
+be broadened, opening up the opportunity to repackage all obfuscated classes:
+<pre>
+-repackageclasses 'myobfuscated'
+-allowaccessmodification
+</pre>
+The obfuscated class names then look as follows:
+<pre>
+mycompany.myapplication.MyMain
+myobfuscated.a
+myobfuscated.b
+myobfuscated.c
+myobfuscated.d
+myobfuscated.e
+myobfuscated.f
+</pre>
+<p>
+The specified target package can always be the root package. For instance:
+<pre>
+-repackageclasses ''
+-allowaccessmodification
+</pre>
+The obfuscated class names are then the shortest possible names:
+<pre>
+mycompany.myapplication.MyMain
+a
+b
+c
+d
+e
+f
+</pre>
+<p>
+Note that not all levels of obfuscation of package names may be acceptable for
+all code. Notably, you may have to take into account that your application may
+contain <a href="#resourcefiles">resource files</a> that have to be adapted.
+
 <a name="restructuring"> </a>
 <h3>Restructuring the output archives</h3>
 In simple applications, all output classes and resources files are merged into
@@ -828,6 +1002,55 @@ jar as a library jar:
 }
 </pre>
 
+<a name="microedition"> </a>
+<h3>Preverifying class files for Java Micro Edition</h3>
+Even if you're not interested in shrinking, optimizing, and obfuscating your
+midlets, as shown in the <a href="#midlets">midlets example</a>, you can still
+use ProGuard to preverify the class files for Java Micro Edition. ProGuard
+produces slightly more compact results compared to the traditional external
+preverifier.
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
+-libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
+
+-dontshrink
+-dontoptimize
+-dontobfuscate
+
+-microedition
+</pre>
+<p>
+We're not processing the input, just making sure the class files are
+preverified by targeting them at Java Micro Edition with the <a
+href="usage.html#microedition"><code>-microedition</code></a> option. Note
+that we don't need any <code>-keep</code> options to specify entry points; all
+class files are simply preverified.
+
+<a name="upgrade"> </a>
+<h3>Upgrading class files to Java 6</h3>
+The following options upgrade class files to Java 6, by updating their
+internal version numbers and preverifying them. The class files can then be
+loaded more efficiently by the Java 6 Virtual Machine.
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-dontshrink
+-dontoptimize
+-dontobfuscate
+
+-target 1.6
+</pre>
+<p>
+We're not processing the input, just retargeting the class files with the <a
+href="usage.html#target"><code>-target</code></a> option. They will
+automatically be preverified for Java 6 as a result. Note that we don't need
+any <code>-keep</code> options to specify entry points; all class files are
+simply updated and preverified.
+
 <a name="deadcode"> </a>
 <h3>Finding dead code</h3>
 These options list unused classes, fields, and methods in the application
@@ -835,8 +1058,10 @@ These options list unused classes, fields, and methods in the application
 <pre>
 -injars      in.jar
 -libraryjars <java.home>/lib/rt.jar
+
 -dontoptimize
 -dontobfuscate
+-dontpreverify
 -printusage
 
 -keep public class mypackage.MyApplication {
@@ -845,18 +1070,18 @@ These options list unused classes, fields, and methods in the application
 </pre>
 <p>
 We're not specifying an output jar, just printing out some results. We're
-saving some processing time by skipping the optimization and obfuscation
-steps.
+saving some processing time by skipping the other processing steps.
 <p>
-The java compiler inlines primitive constants (primitive <code>static
-final</code> fields). ProGuard would therefore list such fields as not being
-used in the class files that it analyzes, even if they <i>are</i> used in the
-source files. We can add a <a
+The java compiler inlines primitive constants and String constants
+(<code>static final</code> fields). ProGuard would therefore list such fields
+as not being used in the class files that it analyzes, even if they <i>are</i>
+used in the source files. We can add a <a
 href="usage.html#keepclassmembers"><code>-keepclassmembers</code></a> option
-that keeps those fields a priori, in order to avoid listing them:
+that keeps those fields a priori, in order to avoid having them listed:
 <pre>
 -keepclassmembers class * {
-    static final % *;
+    static final %                *;
+    static final java.lang.String *;
 }
 </pre>
 
@@ -866,14 +1091,59 @@ These options print out the internal structure of all class files in the input
 jar:
 <pre>
 -injars in.jar
+
 -dontshrink
 -dontoptimize
 -dontobfuscate
+-dontpreverify
+
 -dump
 </pre>
 <p>
 Note how we don't need to specify the Java run-time jar, because we're not
 processing the input jar at all.
+
+<a name="annotated"> </a>
+<h3>Using annotations to configure ProGuard</h3>
+
+The traditional ProGuard configuration allows to keep a clean separation
+between the code and the configuration for shrinking, optimization, and
+obfuscation. However, it is also possible to define specific annotations,
+and then annotate the code to configure the processing.
+<p>
+You can find a set of such predefined annotations in the directory
+<code>examples/annotations/lib</code> in the ProGuard distribution.
+The annotation classes are defined in <code>annotations.jar</code>. The
+corresponding ProGuard configuration (or meta-configuration, if you prefer)
+is specified in <code>annotations.pro</code>. With these files, you can start
+annotating your code. For instance, a java source file
+<code>Application.java</code> can be annotated as follows:
+<pre>
+ at KeepApplication
+public class Application {
+  ....
+}
+</pre>
+<p>
+The ProGuard configuration file for the application can then be simplified by
+leveraging off these annotations:
+<pre>
+-injars      in.jar
+-outjars     out.jar
+-libraryjars <java.home>/lib/rt.jar
+
+-include lib/annotations.pro
+</pre>
+<p>
+The annotations are effectively replacing the application-dependent
+<code>-keep</code> options. You may still wish to add traditional
+<code>-keep</code> options 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>.
+<p>
+The directory <code>examples/annotations</code> contains more examples that
+illustrate some of the possibilities.
 <p>
 
 <hr>
diff --git a/docs/manual/gui.html b/docs/manual/gui.html
index 688612d..4c2585c 100644
--- a/docs/manual/gui.html
+++ b/docs/manual/gui.html
@@ -48,9 +48,13 @@ In addition, there is a tab to execute ReTrace interactively:
 </table>
 <p>
 
-You can freely toggle between the tabs by means of the buttons on the left-hand
-side of the window, or by means of the <b>Previous</b> and <b>Next</b> buttons
-at the bottom of the tabs.
+You can freely toggle between the tabs by means of the buttons on the
+left-hand side of the window, or by means of the <b>Previous</b> and
+<b>Next</b> buttons at the bottom of the tabs. Tool tips briefly explain the
+purpose of the numerous options and text fields, although a basic
+understanding of the shrinking/optimization/obfuscation/preverification
+process is assumed. Please refer to the <a
+href="introduction.html">Introduction</a> of this manual.
 <p>
 
 <a name="proguard"> </a>
@@ -167,8 +171,8 @@ corresponding type are considered.
 <p>
 
 For example, checking the <b>Applications</b> entry and filling in
-"myapplications.**" after it would mean: keep all classes that have a main
-method in the "myapplications" package and any of its subpackages.
+"myapplications.**" after it would mean: keep all classes that have main
+methods in the "myapplications" package and all of its subpackages.
 <p>
 
 The variable list at the bottom allows to define additional entries
@@ -213,7 +217,9 @@ overview of the three dialogs provided by the GUI.
 
 The <i>keep class</i> dialog appears when adding or editing new special keep
 entries. It has text fields and selections for specifying and constraining
-classes and class members to keep.
+classes and class members to keep. The <b>Advanced options</b> / <b>Basic
+options</b> button at the bottom of the dialog allows to toggle showing the
+advanced options.
 
 <ul>
 <li>The <b>Comments</b> text field allows to add optional comments to this
@@ -227,12 +233,26 @@ classes and class members to keep.
     Note that class members will only be protected if they are explicitly
     specified, even if only by means of a wildcard.
 
+<li>The <b>Allow</b> selection allows to specify whether you want to allow the
+    the specified classes and their specified class members to be shrunk,
+    optimized and/or obfuscated.
+
 <li>The <b>Access</b> selections allows to specify constraints on the class or
     classes, based on their access modifiers.
 
+<li>The <b>Annotation</b> text field takes the fully-qualified name of an
+    annotation that is required for matching classes. The annotation name can
+    contain wildcards. This is an advanced option for defining <i>keep</i>
+    annotations.
+
 <li>The <b>Class</b> text field takes the fully-qualified name of the class or
     classes. The class name can contain wildcards.
 
+<li>The <b>Annotation</b> text field takes the fully-qualified name of an
+    annotation that is required for the class or interface that the above
+    class must extend. The annotation name can contain wildcards. This is an
+    advanced option for defining <i>keep</i> annotations.
+
 <li>The <b>Extends/implements class</b> text field takes the fully-qualified
     name of the class or interface that the above classes must extend.
 
@@ -243,13 +263,20 @@ classes and class members to keep.
 <p>
 
 The <i>keep field</i> dialog appears when adding or editing fields within the
-above dialog. It has text fields and selections for specifying and constraining
-fields to keep.
+above dialog. It has text fields and selections for specifying and
+constraining fields to keep. Again, the <b>Advanced options</b> / <b>Basic
+options</b> button at the bottom of the dialog allows to toggle showing the
+advanced options.
 
 <ul>
 <li>The <b>Access</b> selections allows to specify constraints on the field or
     fields, based on their access modifiers.
 
+<li>The <b>Annotation</b> text field takes the fully-qualified name of an
+    annotation that is required for matching fields. The annotation name can
+    contain wildcards. This is an advanced option for defining <i>keep</i>
+    annotations.
+
 <li>The <b>Return type</b> text field takes the fully-qualified type of the
     field or fields. The type can contain wildcards.
 
@@ -258,14 +285,21 @@ fields to keep.
 </ul>
 <p>
 
-Similarly, the <i>keep method</i> dialog appears when adding or editing methods
-within the keep class dialog. It has text fields and selections for specifying
-and constraining methods to keep.
+Similarly, the <i>keep method</i> dialog appears when adding or editing
+methods within the keep class dialog. It has text fields and selections for
+specifying and constraining methods to keep. Again, the <b>Advanced
+options</b> / <b>Basic options</b> button at the bottom of the dialog allows
+to toggle showing the advanced options.
 
 <ul>
 <li>The <b>Access</b> selections allows to specify constraints on the method or
     methods, based on their access modifiers.
 
+<li>The <b>Annotation</b> text field takes the fully-qualified name of an
+    annotation that is required for matching methods. The annotation name can
+    contain wildcards. This is an advanced option for defining <i>keep</i>
+    annotations.
+
 <li>The <b>Return type</b> text field takes the fully-qualified type of the         method or methods. The type can contain wildcards.
 
 <li>The <b>Name</b> text field takes the name of the method or methods. The
@@ -308,10 +342,13 @@ Corresponding configuration options:
 <li>-<a href="usage.html#obfuscationdictionary">obfuscationdictionary</a>
 <li>-<a href="usage.html#overloadaggressively">overloadaggressively</a>
 <li>-<a href="usage.html#useuniqueclassmembernames">useuniqueclassmembernames</a>
-<li>-<a href="usage.html#defaultpackage">defaultpackage</a>
 <li>-<a href="usage.html#dontusemixedcaseclassnames">dontusemixedcaseclassnames</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#adaptresourcefilenames">adaptresourcefilenames</a>
+<li>-<a href="usage.html#adaptresourcefilecontents">adaptresourcefilecontents</a>
 <li>-<a href="usage.html#keepnames">keepnames</a>
 <li>-<a href="usage.html#keepclassmembernames">keepclassmembernames</a>
 <li>-<a href="usage.html#keepclasseswithmembernames">keepclasseswithmembernames</a>
@@ -335,6 +372,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#optimizationpasses">optimizationpasses</a>
 <li>-<a href="usage.html#allowaccessmodification">allowaccessmodification</a>
 <li>-<a href="usage.html#assumenosideeffects">assumenosideeffects</a>
 <li><a href="usage.html#classspecification"><i>class_specification</i></a>
@@ -344,21 +382,27 @@ Corresponding configuration options:
 <a name="information"> </a>
 <h2>The Information Tab</h2>
 
-The <i>Information</i> tab presents a number of options that affect the
-information that ProGuard returns when processing your code. The bottom list
-allows you to query ProGuard about why given classes and class members are
-being kept in the shrinking step.
+The <i>Information</i> tab presents a number of options for preverification
+and targeting, and for the information that ProGuard returns when processing
+your code. The bottom list allows you to query ProGuard about why given
+classes and class members are being kept in the shrinking step.
 <p>
 
 Corresponding configuration options:
 <ul type="none">
-<li>-<a href="usage.html#printseeds">printseeds</a>
+<li>-<a href="usage.html#dontpreverify">dontpreverify</a>
+<li>-<a href="usage.html#microedition">microedition</a>
+<li>-<a href="usage.html#target">target</a>
 <li>-<a href="usage.html#verbose">verbose</a>
 <li>-<a href="usage.html#dontnote">dontnote</a>
 <li>-<a href="usage.html#dontwarn">dontwarn</a>
 <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#forceprocessing">forceprocessing</a>
+<li>-<a href="usage.html#printseeds">printseeds</a>
+<li>-<a href="usage.html#printconfiguration">printconfiguration</a>
+<li>-<a href="usage.html#dump">dump</a>
 <li>-<a href="usage.html#whyareyoukeeping">whyareyoukeeping</a>
 </ul>
 <p>
@@ -390,14 +434,14 @@ the obfuscated stack trace, and an output console to view the de-obfuscated
 stack trace:
 
 <ul>
+<li>The <b>Verbose</b> check box in the settings panel allows to toggle between
+    normal mode and verbose mode.
+
 <li>The <b>Mapping file</b> text field takes the name of the required mapping
     file that ProGuard wrote while processing the original code. The file name
     can be entered manually or by means of the <b>Browse...</b> button that
     opens a file chooser.
 
-<li>The <b>Verbose</b> check box in the settings panel allows to toggle between
-    normal mode and verbose mode.
-
 <li>The <b>Obfuscated stack trace</b> text area allows to enter the stack
     trace, typically by copying and pasting it from elsewhere. Alternatively,
     it can be loaded from a file by means of the load button below.
diff --git a/docs/manual/index.html b/docs/manual/index.html
index f15b2ed..ce88ea0 100644
--- a/docs/manual/index.html
+++ b/docs/manual/index.html
@@ -19,7 +19,7 @@
 <li><a href="refcard.html">Reference Card</a>
 <li><a href="gui.html">Graphical User Interface</a>
 <li><a href="ant.html">Ant Task</a>
-<li><a href="wtk.html">J2ME Wireless Toolkit Integration</a>
+<li><a href="wtk.html">JME Wireless Toolkit Integration</a>
 </ol>
 
 <h2>ReTrace</h2>
diff --git a/docs/manual/introduction.html b/docs/manual/introduction.html
index 2beea44..4fd2bc6 100644
--- a/docs/manual/introduction.html
+++ b/docs/manual/introduction.html
@@ -10,46 +10,52 @@
 
 <h2>Introduction</h2>
 
-<b>ProGuard</b> is a Java class file shrinker, optimizer, and obfuscator. The
-shrinking step detects and removes unused classes, fields, methods, and
-attributes. The optimization step analyzes and optimizes the bytecode of the
-methods. The obfuscation step renames the remaining classes, fields, and
-methods using short meaningless names. The resulting jars are smaller and
-harder to reverse-engineer.
+<b>ProGuard</b> is a Java class file shrinker, optimizer, obfuscator, and
+preverifier. The shrinking step detects and removes unused classes, fields,
+methods, and attributes. The optimization step analyzes and optimizes the
+bytecode of the methods. The obfuscation step renames the remaining classes,
+fields, and methods using short meaningless names. These first steps make the
+code base smaller, more efficient, and harder to reverse-engineer. The final
+preverification step adds preverification information to the classes, which is
+required for Java Micro Edition or which improves the start-up time for Java
+6.
 <p>
-ProGuard can also be used to list unused classes, fields, and methods in an
-application, and to print out the internal structure of class files.
+Each of these steps is optional. For instance, ProGuard can also be used to
+just list dead code in an application, or to preverify class files for
+efficient use in Java 6.
 <p>
 
 <table class="diagram" align="center">
 
 <tr>
 <td rowspan="4" class="lightblock">Input jars</td>
-<td colspan="6" class="transparentblock"></td>
+<td colspan="8" class="transparentblock"></td>
 </tr>
 
 <tr>
 <td rowspan="2" class="transparentblock"></td>
 <td rowspan="3" class="lightblock">Shrunk code</td>
-<td colspan="4" class="transparentblock"></td>
+<td colspan="6" class="transparentblock"></td>
 </tr>
 
 <tr>
 <td             class="transparentblock"></td>
-<td rowspan="2" class="lightblock">Optimized code</td>
-<td colspan="2" class="transparentblock"></td>
+<td rowspan="2" class="lightblock">Optim. code</td>
+<td colspan="3" class="transparentblock"></td>
+<td rowspan="2" class="lightblock">Output jars</td>
 </tr>
 
 <tr>
 <td             class="transparentblock">- shrink →</td>
 <td             class="transparentblock">- optimize →</td>
 <td             class="transparentblock">- obfuscate →</td>
-<td             class="lightblock">Output jars</td>
+<td             class="lightblock">Obfusc. code</td>
+<td             class="transparentblock">- preverify →</td>
 </tr>
 
 <tr>
 <td             class="darkblock">Library jars</td>
-<td colspan="5" class="transparentblock">----------------- (unchanged) -----------------→</td>
+<td colspan="7" class="transparentblock">------------------------------- (unchanged) -------------------------------→</td>
 <td             class="darkblock">Library jars</td>
 </tr>
 
@@ -57,18 +63,19 @@ application, and to print out the internal structure of class files.
 <p>
 
 ProGuard typically reads the <b>input jars</b> (or wars, ears, zips, or
-directories). It then shrinks, optimizes, and obfuscates them. It then writes
-the results to one or more <b>output jars</b> (or wars, ears, zips, or
-directories). The input jars can optionally contain resource files. ProGuard
-copies all non-class resource files from the input jars to the output jars.
-Their names and contents remain unchanged.
+directories). It then shrinks, optimizes, obfuscates, and preverifies them.
+Optionally, multiple optimization passes can be performed, each typically
+followed by another shrinking step. ProGuard writes the processed results to
+one or more <b>output jars</b> (or wars, ears, zips, or directories). The
+input may contain resource files, whose names and contents can optionally be
+updated to reflect the obfuscated class names.
 <p>
 ProGuard requires the <b>library jars</b> (or wars, ears, zips, or
-directories) of the input jars to be specified. It can then reconstruct class
-hierarchies and other class dependencies, which are necessary for proper
-shrinking, optimization, and obfuscation. The library jars themselves always
-remain unchanged. You should still put them in the class path of your final
-application.
+directories) of the input jars to be specified. These are essentially the
+libraries that you would need for compiling the code. ProGuard uses them to
+reconstruct the class dependencies that are necessary for proper processing.
+The library jars themselves always remain unchanged. You should still put them
+in the class path of your final application.
 <p>
 In order to determine which code has to be preserved and which code can be
 discarded or obfuscated, you have to specify one or more entry points to your
@@ -87,26 +94,57 @@ midlets, etc.
 <li>In the <b>obfuscation step</b>, ProGuard renames classes and class members
     that are not entry points. In this entire process, keeping the entry
     points ensures that they can still be accessed by their original names.
+
+<li>The <b>preverification step</b> is the only step that doesn't have to know
+    the entry points.
 </ul>
 <p>
-Any classes or class members of your code that are created or invoked
-dynamically (that is, by name) have to be specified as entry points too. It is
-generally impossible to determine these cases automatically, but ProGuard will
-offer some suggestions if keeping some classes or class members appears
-necessary. For proper results, you should at least be somewhat familiar with
-the code that you are processing.
+The <a href="usage.html">Usage section</a> of this manual describes the
+necessary <a href="usage.html#keepoptions"><code>-keep</code> options</a> and
+the <a href="examples.html">Examples section</a> provides plenty of examples.
+
+<h3>Introspection</h3>
+
+Introspection presents particular problems for any automatic processing of
+code. In ProGuard, classes or class members in your code that are created or
+invoked dynamically (that is, by name) have to be specified as entry points
+too. For example, <code>Class.forName()</code> constructs may refer to any
+class at run-time. It is generally impossible to foresee which classes have to
+be preserved (with their original names), since the class names might be read
+from a configuration file, for instance. You therefore have to specify them in
+your ProGuard configuration, with the same simple <code>-keep</code> options.
+<p>
+However, ProGuard will already detect and handle the following cases for you:
+
+<ul>
+<li><code>Class.forName("SomeClass")</code>
+<li><code>SomeClass.class</code>
+<li><code>SomeClass.class.getField("someField")</code>
+<li><code>SomeClass.class.getDeclaredField("someField")</code>
+<li><code>SomeClass.class.getMethod("someMethod", new Class[] {})</code>
+<li><code>SomeClass.class.getMethod("someMethod", new Class[] { A.class })</code>
+<li><code>SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })</code>
+<li><code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})</code>
+<li><code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })</code>
+<li><code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })</code>
+</ul>
+
+The names of the classes and class members may of course be different, but the
+constructs should be literally the same for ProGuard to recognize them. The
+referenced classes and class members are preserved in the shrinking phase, and
+the string arguments are properly replaced in the obfuscation phase.
+<p>
+Furthermore, ProGuard will offer some suggestions if keeping some classes or
+class members appears necessary. For example, ProGuard will note constructs
+like "<code>(SomeClass)Class.forName(variable).newInstance()</code>". These
+might be an indication that the class or interface <code>SomeClass</code>
+and/or its implementations may need to be preserved. You can then adapt your
+configuration accordingly.
 <p>
-ProGuard does handle <code>Class.forName("SomeClass")</code> and
-<code>SomeClass.class</code> constructs automatically. The referenced classes
-are preserved in the shrinking phase, and the string arguments are properly
-replaced in the obfuscation phase. With variable string arguments, it is
-generally impossible to determine their possible values (they might be read
-from a configuration file, for instance). However, as mentioned, ProGuard will
-note constructs like
-"<code>(SomeClass)Class.forName(variable).newInstance()</code>". These might
-be an indication that the class or interface <code>SomeClass</code> and/or its
-implementations may need to be preserved. You can then adapt your configuration
-accordingly.
+For proper results, you should at least be somewhat familiar with the code
+that you are processing. Obfuscating code that performs a lot of introspection
+may require trial and error, especially without the necessary information
+about the internals of the code.
 <p>
 
 <hr>
diff --git a/docs/manual/limitations.html b/docs/manual/limitations.html
index 165afc4..0867385 100644
--- a/docs/manual/limitations.html
+++ b/docs/manual/limitations.html
@@ -14,21 +14,6 @@ When using ProGuard, you should be aware of a few issues, all of which are
 easily avoided or resolved:
 <p>
 <ul>
-<li>ProGuard handles the essential elements of Java: classes, interfaces, class
-    members, inheritance,... ProGuard itself is not aware of any additional
-    <b>naming conventions or APIs</b>: main methods, native methods, beans,
-    serialization,... You'll have to specify these in your configuration. The
-    configuration syntax is intended to be compact, readable, expressive, and
-    generally applicable. It's usually quite simple. The examples section of
-    this manual provides many typical examples. The graphical user interface
-    has checkboxes for common boilerplate configurations.
-    <p>
-
-<li>ProGuard currently copies <b>manifest files and resource files</b>
-    unchanged. Directory entries, on the other hand, are not copied to the
-    output jars. If your code has any special dependencies on these elements,
-    you should modify or add them manually.
-    <p>
 
 <li>For efficiency, ProGuard always ignores any <b>private or package visible
     library classes</b> while reading library jars. If any of them are
@@ -53,9 +38,11 @@ easily avoided or resolved:
 
 <li>ProGuard's optimization algorithms also remove all <b>empty busy-waiting
     loops</b>, unless they test on fields that are marked as
-    <code>volatile</code>. You should always mark tested fields as
-    <code>volatile</code>, or otherwise, you'll have to switch off
-    optimization using the <code>-dontoptimize</code> option.
+    <code>volatile</code>. The specifications of the Java Virtual Machine
+    require that you always mark fields that are accessed across different
+    threads without further synchronization as <code>volatile</code>.
+    Otherwise, you'll have to switch off optimization using the
+    <code>-dontoptimize</code> option.
     <p>
 
 <li>If an input jar and a library jar contain classes in the <b>same
@@ -66,18 +53,6 @@ easily avoided or resolved:
     across input jars and library jars.
     <p>
 
-<li>ProGuard may not handle <b>obfuscation marker interfaces</b> as expected.
-    If you specify "<code>-keep class * implements MyKeepInterface</code>",
-    and <code>MyKeepInterface</code> is not used in your code, the specified
-    classes are kept, but they are obfuscated. Technically, the interface is
-    removed in the shrinking phase, making the directive void in the
-    obfuscation phase. This behavior may be fixed in the future. For now, you
-    can get around it by explicitly keeping the interface as well:
-    "<code>-keep class MyKeepInterface</code>". In any case, creating a proper
-    configuration file seems a cleaner solution than using such an obfuscation
-    marker interface.
-    <p>
-
 <li>When obfuscating, ProGuard will write out class files named
     "<code>a.class</code>", "<code>b.class</code>", etc. If there is a large
     numbers of classes in the same package, it may also write out
@@ -86,20 +61,6 @@ easily avoided or resolved:
     write the output to a jar, in order to avoid such problems.
     <p>
 
-<li>ProGuard's obfuscation process currently doesn't follow the <b>naming
-    rule</b> specifying that nested classes must be named as
-    <code>EnclosingClass$InnerClass</code>, for instance (cfr. <a href=
-    "http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html"
-    >The Java Language Specification, Second Edition</a>, <a href=
-    "http://java.sun.com/docs/books/jls/second_edition/html/binaryComp.doc.html#59892"
-    >Section 13.1</a>). This should not present a problem in practice, since
-    the rule is mainly intended for transformations at the source code level.
-    Internal-external class relationships are still represented correctly
-    inside the binary class files. Decompilers or others tools that rely on
-    the naming rule may have problems processing obfuscated jars. I'm not
-    aware of any such cases.
-    <p>
-
 </ul>
 <p>
 
diff --git a/docs/manual/refcard.html b/docs/manual/refcard.html
index fd234b7..a983f18 100644
--- a/docs/manual/refcard.html
+++ b/docs/manual/refcard.html
@@ -73,13 +73,26 @@
 </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>
+</tr>
+
+<tr>
+<td valign="top"><a href="usage.html#forceprocessing"><code><b>-forceprocessing</b></code></a></td>
+<td>Process the input, even if the output seems up to date.</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#keep"><code><b>-keep</b></code></a>
+                 [<a href="usage.html#keepoptionmodifiers">,<i>modifier</i></a>,...]
                  <a href="usage.html#classspecification"><i>class_specification</i></a></td>
 <td>Preserve the specified classes <i>and</i> class members.</td>
 
 </tr>
 <tr>
 <td valign="top"><a href="usage.html#keepclassmembers"><code><b>-keepclassmembers</b></code></a>
+                 [<a href="usage.html#keepoptionmodifiers">,<i>modifier</i></a>,...]
                  <a href="usage.html#classspecification"><i>class_specification</i></a></td>
 <td>Preserve the specified class members, if their classes are preserved as
     well.</td>
@@ -87,6 +100,7 @@
 
 <tr>
 <td valign="top"><a href="usage.html#keepclasseswithmembers"><code><b>-keepclasseswithmembers</b></code></a>
+                 [<a href="usage.html#keepoptionmodifiers">,<i>modifier</i></a>,...]
                  <a href="usage.html#classspecification"><i>class_specification</i></a></td>
 <td>Preserve the specified classes <i>and</i> class members, if all of the
     specified class members are present.</td>
@@ -146,6 +160,12 @@
 </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>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#assumenosideeffects"><code><b>-assumenosideeffects</b></code></a>
                  <a href="usage.html#classspecification"><i>class_specification</i></a></td>
 <td>Assume that the specified methods don't have any side effects, while
@@ -193,24 +213,35 @@
     obfuscation.</td> </tr>
 
 <tr>
-<td valign="top"><a href="usage.html#defaultpackage"><code><b>-defaultpackage</b></code></a>
+<td valign="top"><a href="usage.html#dontusemixedcaseclassnames"><code><b>-dontusemixedcaseclassnames</b></code></a></td>
+<td>Don't generate mixed-case class names while obfuscating.</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 class files that are renamed into the single given
+<td>Repackage all packages that are renamed into the single given parent
     package.</td>
 </tr>
 
 <tr>
-<td valign="top"><a href="usage.html#dontusemixedcaseclassnames"><code><b>-dontusemixedcaseclassnames</b></code></a></td>
-<td>Don't generate mixed-case class names while obfuscating.</td>
+<td valign="top"><a href="usage.html#repackageclasses"><code><b>-repackageclasses</b></code></a>
+                 [<i>package_name</i>]</td>
+<td>Repackage all class files that are renamed into the single given
+    package.</td>
 </tr>
 
 <tr>
 <td valign="top"><a href="usage.html#keepattributes"><code><b>-keepattributes</b></code></a>
                  [<i>attribute_name<b>,</b>...</i>]</td>
 <td>Preserve the given optional attributes; typically
-    <code>LineNumberTable</code>, <code>LocalVariableTable</code>,
-    <code>SourceFile</code>, <code>Deprecated</code>, <code>Synthetic</code>,
-    <code>Signature</code>, and <code>InnerClasses</code>.</td>
+    <code>Exceptions</code>, <code>InnerClasses</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>, and
+    <code>*Annotation*</code>.</td>
 </tr>
 
 <tr>
@@ -221,6 +252,30 @@
 </tr>
 
 <tr>
+<td valign="top"><a href="usage.html#adaptresourcefilenames"><code><b>-adaptresourcefilenames</b></code></a>
+                 [<a href="#filters"><i>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>
+<td>Update the contents of the specified resource files, based on the
+    obfuscated names of the class files.</td>
+</tr>
+
+<tr>
+<td valign="top"><a href="usage.html#dontpreverify"><code><b>-dontpreverify</b></code></a></td>
+<td>Don't preverify the processed class files.</td>
+</tr>
+
+<tr>
+<td valign="top"><a href="usage.html#microedition"><code><b>-microedition</b></code></a></td>
+<td>Target the processed class files at Java Micro Edition.</td>
+</tr>
+
+<tr>
 <td valign="top"><a href="usage.html#verbose"><code><b>-verbose</b></code></a></td>
 <td>Write out some more information during processing.</td>
 </tr>
@@ -243,12 +298,19 @@
 </tr>
 
 <tr>
-<td valign="top"><a href="usage.html#dump"><code><b>-dump</b></code></a>
+<td valign="top"><a href="usage.html#printconfiguration"><code><b>-printconfiguration</b></code></a>
                  [<a href="usage.html#filename"><i>filename</i></a>]</td>
 <td>Write out the internal structure of the processed class files, to the
     standard output or to the given file.</td>
 </tr>
 
+<tr>
+<td valign="top"><a href="usage.html#dump"><code><b>-dump</b></code></a>
+                 [<a href="usage.html#filename"><i>filename</i></a>]</td>
+<td>Write out the entire configuration in traditional ProGuard style, to the
+    standard output or to the given file.</td>
+</tr>
+
 </table>
 <p>
 Notes:
@@ -294,19 +356,41 @@ Notes:
 </table>
 <p>
 
+<h2>Keep Option Modifiers</h2>
+
+<table cellspacing="10">
+
+<tr>
+<td valign="top"><a href="usage.html#allowshrinking"><code><b>allowshrinking</b></code></a></td>
+<td>The entry points specified in the keep tag may be shrunk.</td>
+</tr>
+
+<tr>
+<td valign="top"><a href="usage.html#allowoptimization"><code><b>allowoptimization</b></code></a></td>
+<td>The entry points specified in the keep tag may be optimized.</td>
+</tr>
+
+<tr>
+<td valign="top"><a href="usage.html#allowobfuscation"><code><b>allowobfuscation</b></code></a></td>
+<td>The entry points specified in the keep tag may be obfuscated.</td>
+</tr>
+
+</table>
+<p>
+
 <h2>Class Specifications</h2>
 
 <pre>
-[[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] ([<b>!</b>]<b>interface</b>)|<b>class</b> <i>classname</i>
-    [<b>extends</b>|<b>implements</b> <i>classname</i>]
+[<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] [<b>!</b>]<b>interface</b>|<b>class</b> <i>classname</i>
+    [<b>extends</b>|<b>implements</b> [<b>@</b><i>annotationtype</i>] <i>classname</i>]
 [<b>{</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
                                                                       (<i>fieldtype fieldname</i>)<b>;</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
                                                                                            <b><init>(</b><i>argumenttype,...</i><b>)</b> |
                                                                                            <i>classname</i><b>(</b><i>argumenttype,...</i><b>)</b> |
                                                                                            (<i>returntype methodname</i><b>(</b><i>argumenttype,...</i><b>)</b>)<b>;</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
     ...
 <b>}</b>]
 </pre>
@@ -315,11 +399,12 @@ Notes:
 <ul>
 <li>Class names must always be fully qualified, i.e. including their package
     names.
-<li>Types in <i>classname</i>, <i>returntype</i>, and <i>argumenttype</i> can
-    contain wildcards: '<code><b>?</b></code>' for a single character,
-    '<code><b>*</b></code>' for any number of characters (but not the package
-    separator), '<code><b>**</b></code>' for any number of (any)
-    characters, and  '<code><b>%</b></code>' for any primitive type.
+<li>Types in <i>classname</i>, <i>annotationtype</i>, <i>returntype</i>, and
+    <i>argumenttype</i> can contain wildcards: '<code><b>?</b></code>' for a
+    single character, '<code><b>*</b></code>' for any number of characters
+    (but not the package separator), '<code><b>**</b></code>' for any number
+    of (any) characters, '<code><b>%</b></code>' for any primitive type,
+    '<code><b>***</b></code>' for any type, and '<code><b>...</b></code>' for       any number of arguments..
 <li><i>fieldname</i> and <i>methodname</i> can contain wildcards as well:
     '<code><b>?</b></code>' for a single character and '<code><b>*</b></code>'
     for any number of characters.
diff --git a/docs/manual/retrace/introduction.html b/docs/manual/retrace/introduction.html
index 83b49de..b154159 100644
--- a/docs/manual/retrace/introduction.html
+++ b/docs/manual/retrace/introduction.html
@@ -11,11 +11,13 @@
 <h2>Introduction</h2>
 
 <b>ReTrace</b> is a companion tool for <b>ProGuard</b> that 'de-obfuscates'
-stack traces. When an obfuscated program throws an exception, the resulting
-stack trace typically isn't very informative. Class names and method names
-have been replaced by short meaningless strings. Source file names and line
-numbers are missing altogether. While this may be intentional, it can also be
-inconvenient when debugging problems.
+stack traces.
+<p>
+When an obfuscated program throws an exception, the resulting stack trace
+typically isn't very informative. Class names and method names have been
+replaced by short meaningless strings. Source file names and line numbers are
+missing altogether. While this may be intentional, it can also be inconvenient
+when debugging problems.
 <p>
 
 <table class="diagram" align="center">
diff --git a/docs/manual/sections.html b/docs/manual/sections.html
index 51ae384..62df9c3 100644
--- a/docs/manual/sections.html
+++ b/docs/manual/sections.html
@@ -21,7 +21,7 @@
 <li><a target="main" href="refcard.html">Ref Card</a></li>
 <li><a target="main" href="gui.html">GUI</a></li>
 <li><a target="main" href="ant.html">Ant Task</a></li>
-<li><a target="main" href="wtk.html">J2ME WTK</a></li>
+<li><a target="main" href="wtk.html">JME WTK</a></li>
 
 <li class="title">ReTrace Manual</li>
 <li><a target="main" href="retrace/introduction.html">Introduction</a></li>
@@ -54,6 +54,10 @@ document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
 <p>
 <a href="http://www.luciad.com/" target="other">
 <img src="../luciadlogo.png" width="88" height="24" alt="Luciad"></a>
+
+<p>
+<a href="http://www.javadocking.com/" target="other">
+<img src="../sanawarelogo.png" width="88" height="24" alt="Luciad"></a>
 </center>
 
 </body>
diff --git a/docs/manual/troubleshooting.html b/docs/manual/troubleshooting.html
index 1fb1f1a..47e871a 100644
--- a/docs/manual/troubleshooting.html
+++ b/docs/manual/troubleshooting.html
@@ -14,7 +14,9 @@ While preparing a configuration for processing your code, you may bump into a
 few problems. The following sections discuss some common issues and solutions:
 <ul>
 <li><a href="#processing">Problems while processing</a>
-<li><a href="#preverifying">Problems while preverifying for J2ME</a>
+<li><a href="#afterprocessing">Unexpected observations after processing</a>
+<li><a href="#preverifying">Problems while preverifying for Java Micro
+    Edition</a>
 <li><a href="#runtime">Problems at run-time</a>
 </ul>
 
@@ -24,7 +26,7 @@ few problems. The following sections discuss some common issues and solutions:
 ProGuard may print out some notes and non-fatal warnings:
 
 <dl>
-<dt><a name="dynamical"><b>Note: ... calls '(...)Class.forName(variable).newInstance()'</b></a></dt>
+<dt><a name="dynamicalclass"><b>Note: ... calls '(...)Class.forName(variable).newInstance()'</b></a></dt>
 
 <dd>ProGuard lists all class casts of dynamically created class instances,
     like "<code>(MyClass)Class.forName(variable).newInstance()</code>".
@@ -34,6 +36,15 @@ ProGuard may print out some notes and non-fatal warnings:
     MyClass</code>". You can switch off these notes by specifying the
     <code>-dontnote</code> option.</dd>
 
+<dt><a name="dynamicalclassmember"><b>Note: ... accesses a field/method '...' dynamically</b></a></dt>
+
+<dd>ProGuard lists a number of constructs like
+    "<code>.getField("myField")</code>". Depending on your application, you
+    may need to figure out where the mentioned class members are defined and
+    keep them with an option like "<code>-keep class MyClass { MyFieldType
+    myField; }</code>". You can switch off these notes by specifying the
+    <code>-dontnote</code> option.</dd>
+
 <dt><a name="duplicateclass"><b>Note: duplicate definition of program/library class</b></a></dt>
 
 <dd>Your program jars or library jars contain multiple definitions of the
@@ -184,13 +195,11 @@ Should ProGuard crash while processing your application:
 
 <dt><a name="stackoverflowerror"><b>StackOverflowError</b></a></dt>
 
-<dd>This error may occur when optimizing very complex methods on Windows
-    (surprisingly, not so easily on Linux). You can always work around it by
-    using ProGuard's <code>-dontoptimize</code> option. In theory, increasing
-    the stack size of the Java virtual machine (with the usual
-    <code>-Xss</code> option) should help too. In practice however, the
-    <code>-Xss</code> setting doesn't have any effect on the main thread, due
-    to <a
+<dd>This error might occur when processing a large code base on Windows
+    (surprisingly, not so easily on Linux). In theory, increasing the stack
+    size of the Java virtual machine (with the usual <code>-Xss</code> option)
+    should help too. In practice however, the <code>-Xss</code> setting
+    doesn't have any effect on the main thread, due to <a
     href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4362291">Sun Bug
     #4362291</a>. As a result, this solution will only work when running
     ProGuard in a different thread, e.g. from its GUI.</dd>
@@ -204,39 +213,11 @@ Should ProGuard crash while processing your application:
 </dl>
 <p>
 
-<a name="preverifying"> </a>
-<h2>Problems while preverifying for J2ME</h2>
+<a name="afterprocessing"> </a>
+<h2>Unexpected observations after processing</h2>
 
-If ProGuard seems to run fine, but the preverifier subsequently produces
-errors, it's usually for a single reason:
-<dl>
-
-<dt><a name="invalidclassexception1"><b>InvalidClassException</b>,
-    <b>class loading error</b>, or
-    <b>verification error</b></a></dt>
-
-<dd>If you get any such message from the preverifier, you are probably working
-    on a platform with a case-insensitive file system, such as Windows. The
-    <code>preverify</code> tool always unpacks the jars, so class files with
-    similar lower-case and upper-case names overwrite each other. You can use
-    ProGuard's <code>-dontusemixedcaseclassnames</code> option to work around
-    this problem.
-    <p>
-    If the above doesn't help, there is probably a bug in the optimization step
-    of ProGuard. Make sure you are using the latest version. You should be able
-    to work around the problem by using the <code>-dontoptimize</code> option.
-    You can check the bug database to see if it is a known problem (often with
-    a fix). Otherwise, please report it, preferably with the simplest example
-    on which you can find ProGuard to fail.</dd>
-
-</dl>
-<p>
-
-<a name="runtime"> </a>
-<h2>Problems at run-time</h2>
-
-If ProGuard runs fine, but your processed application doesn't work, there
-might be several reasons:
+If ProGuard seems to run fine, but your processed code doesn't look right,
+there might be a couple of reasons:
 
 <dl>
 <dt><a name="disappearingclasses"><b>Disappearing classes</b></a></dt>
@@ -264,7 +245,11 @@ might be several reasons:
     some class is missing, ProGuard won't match the elements that you might be
     expecting. It may help to double-check for typos too. You can use the
     <code>-printseeds</code> option to see which elements are being kept
-    exactly.</dd>
+    exactly.
+    <p>
+    If you are using marker interfaces to keep other classes, the marker
+    interfaces themselves are probably being removed in the shrinking step.
+    You should therefore always explicitly keep any marker interfaces.</dd>
 
 <dt><a name="notobfuscated"><b>Variable names not being obfuscated</b></a></dt>
 
@@ -276,6 +261,47 @@ might be several reasons:
     <code>LocalVariableTable</code> or <code>LocalVariableTypeTable</code>
     attributes.</dd>
 
+</dl>
+
+<a name="preverifying"> </a>
+<h2>Problems while preverifying for Java Micro Edition</h2>
+
+If ProGuard seems to run fine, but the external preverifier subsequently
+produces errors, it's usually for a single reason:
+
+<dl>
+<dt><a name="invalidclassexception1"><b>InvalidClassException</b>,
+    <b>class loading error</b>, or
+    <b>verification error</b></a></dt>
+
+<dd>If you get any such message from the preverifier, you are probably working
+    on a platform with a case-insensitive file system, such as Windows. The
+    <code>preverify</code> tool always unpacks the jars, so class files with
+    similar lower-case and upper-case names overwrite each other. You can use
+    ProGuard's <code>-dontusemixedcaseclassnames</code> option to work around
+    this problem.
+    <p>
+    If the above doesn't help, there is probably a bug in the optimization step
+    of ProGuard. Make sure you are using the latest version. You should be able
+    to work around the problem by using the <code>-dontoptimize</code> option.
+    You can check the bug database to see if it is a known problem (often with
+    a fix). Otherwise, please report it, preferably with the simplest example
+    on which you can find ProGuard to fail.</dd>
+
+</dl>
+
+Note that it is no longer necessary to use an external preverifier. With the
+<code>-microedition</code> option, ProGuard will preverify the class files for
+Java Micro Edition.
+<p>
+
+<a name="runtime"> </a>
+<h2>Problems at run-time</h2>
+
+If ProGuard runs fine, but your processed application doesn't work, there
+might be several reasons:
+
+<dl>
 <dt><a name="stacktraces"><b>Stack traces without class names or line numbers</b></a></dt>
 
 <dd>If your stack traces don't contain any class names or lines numbers,
@@ -310,23 +336,36 @@ might be several reasons:
     <b>NullPointerException</b></a></dt>
 
 <dd>Your processed code may be unable to find some resource files. ProGuard
-    currently simply copies resource files over from the input jars to the
-    output jar. Their names and contents remain unchanged. If you've used the
-    <code>-defaultpackage</code> option, the package names of some classes may
-    have changed, and along with them, the directory in which they look for
-    their resource files. It's better not to use this option in these
-    circumstances. Also note that directory entries in jar files aren't copied
-    at all.</dd>
+    simply copies resource files over from the input jars to the
+    output jars. Their names and contents remain unchanged, unless you specify
+    the options <code>-adaptresourcefilenames</code> and/or
+    <code>-adaptresourcefilecontents</code>.
+    <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>
+
+<dt><a name="invalidjarfile"><b>Invalid or corrupt jarfile</b></a></dt>
+
+<dd>You are probably starting your application with the java option
+    <code>-jar</code> instead of the option <code>-classpath</code>. The java
+    virtual machine returns with this error message if your jar doesn't
+    contain a manifest file (<code>META-INF/MANIFEST.MF</code>), if the
+    manifest file doesn't specify a main class (<code>Main-Class:</code> ...),
+    or if the jar doesn't contain this main class. You should then make sure
+    that the input jar contains a valid manifest file to start with, that this
+    manifest file is the one that is copied (the first manifest file that is
+    encountered), and that the main class is kept in your configuration,</dd>
 
 <dt><a name="invalidclassexception2"><b>InvalidClassException</b>,
     <b>class loading error</b>, or
-    <b>verification error</b> (in J2ME)</a></dt>
+    <b>verification error</b> (in Java Micro Edition)</a></dt>
 
-<dd>If you get such an error in J2ME, you may have
-    forgotten to preverify your program jar <i>after</i> having processed it
-    with ProGuard.</dd>
+<dd>If you get such an error in Java Micro Edition, you may have forgotten to
+    specify the <code>-microedition</code> option, so the processed class
+    files are preverified properly.</dd>
 
-<dt><a name="failingmidlets"><b>Failing midlets in J2ME</b></a></dt>
+<dt><a name="failingmidlets"><b>Failing midlets</b> (in Java Micro Edition)</a></dt>
 
 <dd>If your midlet simply won't start, you might try using the
     <code>-dontusemixedcaseclassnames</code> option. Even if it has been
@@ -340,27 +379,27 @@ might be several reasons:
 <dd>You may have forgotten to sign your program jar <i>after</i> having
     processed it with ProGuard.</dd>
 
-<dt><a name="classcastexception"><b>ClassCastException: class not an enum</b></a></dt>
+<dt><a name="classcastexception"><b>ClassCastException: class not an enum</b>
+    or <br><b>IllegalArgumentException: class not an enum type</b></a></dt>
 
-<dd>You are probably processing enumeration types and calling
-    <code>EnumSet.allOf</code>. You should then make sure you're preserving
-    the <code>values()</code> method of the enumeration type, as shown in the
-    examples.</dd>
+<dd>You should make sure you're preserving the special methods of enumeration
+    types, which the run-time environment calls by introspection. The required
+    options are shown in the examples.</dd>
 
 <dt><a name="arraystoreexception"><b>ArrayStoreException: sun.reflect.annotation.EnumConstantNotPresentExceptionProxy</b></a></dt>
 
 <dd>You are probably processing annotations involving enumerations. Again, you
-    should make sure you're preserving the <code>values()</code> method of the
-    enumeration type, as shown in the examples.</dd>
+    should make sure you're preserving the special methods of the enumeration
+    type, as shown in the examples.</dd>
 
 <dt><a name="nosuchfieldormethod"><b>Error: No Such Field or Method</b>,
-    <b>Error verifying method</b> (in a J2ME emulator)</a></dt>
+    <b>Error verifying method</b> (in a Java Micro Edition emulator)</a></dt>
 
-<dd>If you get such a message in a Motorola or Sony Ericsson J2ME phone
-    emulator, it's because these emulators don't like packageless classes
-    and/or overloaded fields and methods. You can work around it by not using
-    the options <b>-defaultpackage ''</b> and <b>-overloadaggressively</b>. If
-    you're using the J2ME WTK plugin, you can adapt the configuration
+<dd>If you get such a message in a Motorola or Sony Ericsson phone emulator,
+    it's because these emulators don't like packageless classes and/or
+    overloaded fields and methods. You can work around it by not using the
+    options <b>-repackageclasses ''</b> and <b>-overloadaggressively</b>. If
+    you're using the JME WTK plugin, you can adapt the configuration
     <code>proguard/wtk/default.pro</code> that's inside the
     <code>proguard.jar</code>.</dd>
 
@@ -369,8 +408,7 @@ might be several reasons:
 <dd>You are probably compiling or running some code that has been obfuscated
     with the <code>-overloadaggressively</code> option. This option triggers a
     bug in <code>sun.tools.java.MethodSet.add</code> in Sun's JDK 1.2.2, which
-    is used for (dynamic) compilation. You should then not use this
-    option.</dd>
+    is used for (dynamic) compilation. You should then avoid this option.</dd>
 
 <dt><a name="classformaterror"><b>ClassFormatError: repetitive field name/signature</b></a></dt>
 
diff --git a/docs/manual/usage.html b/docs/manual/usage.html
index cf0e84e..a680ff9 100644
--- a/docs/manual/usage.html
+++ b/docs/manual/usage.html
@@ -21,8 +21,8 @@ files. Typically, you'll put most options in a configuration file (say,
 <p class="code">
 <code><b>java -jar proguard.jar @myconfig.pro</b></code>
 </p>
-You can simply combine command line options and options from configuration
-files, e.g.:
+You can combine command line options and options from configuration files, for
+instance:
 <p class="code">
 <code><b>java -jar proguard.jar @myconfig.pro -verbose</b></code>
 </p>
@@ -51,10 +51,12 @@ The sections below provide more details:
 <li><a href="#shrinkingoptions">Shrinking Options</a>
 <li><a href="#optimizationoptions">Optimization Options</a>
 <li><a href="#obfuscationoptions">Obfuscation Options</a>
+<li><a href="#preverificationoptions">Preverification Options</a>
 <li><a href="#classpath">Class Paths</a>
 <li><a href="#filename">File Names</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>
 <li><a href="#classspecification">Class Specifications</a>
 </ul>
 
@@ -64,7 +66,8 @@ The sections below provide more details:
 <dl>
 <dt><a name="at"><code><b>@</b></code></a><a href="#filename"><i>filename</i></a></dt>
 
-<dd>Short for '<code>-include</code> <i>filename</i>'.</dd>
+<dd>Short for '<a href="#include"><code>-include</code></a>
+     <a href="#filename"><i>filename</i></a>'.</dd>
 
 <dt><a name="include"><code><b>-include</b></code></a>
     <a href="#filename"><i>filename</i></a></dt>
@@ -127,7 +130,7 @@ The sections below provide more details:
     Although this may seem cumbersome, it allows you to process applications
     targeted at different run-time environments. For example, you can process
     <a href="examples.html#application">J2SE applications</a> as well as <a
-    href="examples.html#midlet">J2ME midlets</a>, just by specifying the
+    href="examples.html#midlet">JME midlets</a>, just by specifying the
     appropriate run-time jar.</dd>
 
 <dt><a name="dontskipnonpubliclibraryclasses"><code><b>-dontskipnonpubliclibraryclasses</b></code></a></dt>
@@ -154,6 +157,23 @@ 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="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
+    version number can be one of <code>1.0</code>, <code>1.1</code>,
+    <code>1.2</code>, <code>1.3</code>, <code>1.4</code>, <code>1.5</code> (or
+    just <code>5</code>), or <code>1.6</code> (or just <code>6</code>). By
+    default, the version numbers of the class files are left unchanged. For
+    example, you may want to <a href="examples.html#upgrade">upgrade class
+    files to Java 6</a>, by changing their version numbers and having them
+    preverified.</dd>
+
+<dt><a name="forceprocessing"><code><b>-forceprocessing</b></code></a></dt>
+
+<dd>Specifies to process the input, even if the output seems up to date. The
+    up-to-dateness test is based on a comparison of the date stamps of the
+    specified input, output, and configuration files or directories.</dd>
+
 </dl>
 <p>
 
@@ -162,18 +182,18 @@ The sections below provide more details:
 
 <dl>
 <dt><a name="keep"><code><b>-keep</b></code></a>
+    [<a href="#keepoptionmodifiers">,<i>modifier</i></a>,...]
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
-<dd>Specifies classes and class members (fields and methods) to be preserved.
-    They will be preserved with their original names. This is typically the
-    required seed for recursively determining which other classes and class
-    members need to be preserved. For example, in order to <a
+<dd>Specifies classes and class members (fields and methods) to be preserved
+    as entry points to your code. For example, in order to <a
     href="examples.html#application">keep an application</a>, you can specify
     the main class along with its main method. In order to <a
     href="examples.html#library">process a library</a>, you should specify all
-    publicly accessible items.</dd>
+    publicly accessible elements.</dd>
 
 <dt><a name="keepclassmembers"><code><b>-keepclassmembers</b></code></a>
+    [<a href="#keepoptionmodifiers">,<i>modifier</i></a>,...]
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
 <dd>Specifies class members to be preserved, if their classes are preserved as
@@ -183,6 +203,7 @@ The sections below provide more details:
     interface.</dd>
 
 <dt><a name="keepclasseswithmembers"><code><b>-keepclasseswithmembers</b></code></a>
+    [<a href="#keepoptionmodifiers">,<i>modifier</i></a>,...]
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
 <dd>Specifies classes and class members to be preserved, on the condition that
@@ -193,7 +214,10 @@ The sections below provide more details:
 <dt><a name="keepnames"><code><b>-keepnames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
-<dd>Specifies classes and class members whose names are to be preserved, if
+<dd>Short for <a href="#keep"><code>-keep</code></a>,<a href="#allowshrinking"><code>allowshrinking</code></a>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <p>
+    Specifies classes and class members whose names are to be preserved, if
     they aren't removed in the shrinking phase. For example, you may want to
     <a href="examples.html#serializable">keep all class names</a> of classes
     that implement the <code>Serializable</code> interface, so that the
@@ -204,7 +228,10 @@ The sections below provide more details:
 <dt><a name="keepclassmembernames"><code><b>-keepclassmembernames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
-<dd>Specifies class members whose names are to be preserved, if they aren't
+<dd>Short for <a href="#keepclassmembers"><code>-keepclassmembers</code></a>,<a href="#allowshrinking"><code>allowshrinking</code></a>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <p>
+    Specifies class members whose names are to be preserved, if they aren't
     removed in the shrinking phase. For example, you may want to preserve the
     name of the synthetic <code>class$</code> methods when <a
     href="examples.html#library">processing a library</a>, so obfuscators can
@@ -215,7 +242,10 @@ The sections below provide more details:
 <dt><a name="keepclasseswithmembernames"><code><b>-keepclasseswithmembernames</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
-<dd>Specifies classes and class members whose names are to be preserved, on
+<dd>Short for <a href="#keepclasseswithmembers"><code>-keepclasseswithmembers</code></a>,<a href="#allowshrinking"><code>allowshrinking</code></a>
+    <a href="#classspecification"><i>class_specification</i></a>
+    <p>
+    Specifies classes and class members whose names are to be preserved, on
     the condition that all of the specified class members are present after
     the shrinking phase. For example, you may want to <a
     href="examples.html#native">keep all native method names</a> and the names
@@ -247,7 +277,9 @@ The sections below provide more details:
 <dd>Specifies not to shrink the input class files. By default, shrinking is
     applied; all classes and class members are removed, except for the ones
     listed by the various <code>-keep</code> options, and the ones on which
-    they depend, directly or indirectly.</dd>
+    they depend, directly or indirectly. A shrinking step is also applied
+    after each optimization step, since some optimizations may open the
+    possibility to remove more classes and class members.</dd>
 
 <dt><a name="printusage"><code><b>-printusage</b></code></a>
     [<a href="#filename"><i>filename</i></a>]</dt>
@@ -268,8 +300,8 @@ The sections below provide more details:
     member. <i>In the current implementation, the shortest chain that is
     printed out may sometimes contain circular reasonings -- these do not
     reflect the actual shrinking process.</i> If the <a
-    href="#verbose"><code><b>-verbose</b></code></a> option if specified, the
-    traces include full field and method signatures. Only applicable when
+    href="#verbose"><code>-verbose</code></a> option if specified, the traces
+    include full field and method signatures. Only applicable when
     shrinking.</dd>
 
 </dl>
@@ -284,6 +316,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="optimizationpasses"><code><b>-optimizationpasses</b></code></a> <i>n</i></dt>
+
+<dd>Specifies the number of optimization passes to be performed. By default, a
+    single pass is performed. Multiple passes may result in further
+    improvements. If no improvements are found after an optimization pass, the
+    optimization is ended. Only applicable when optimizing.</dd>
+
 <dt><a name="assumenosideeffects"><code><b>-assumenosideeffects</b></code></a>
     <a href="#classspecification"><i>class_specification</i></a></dt>
 
@@ -294,9 +333,10 @@ The sections below provide more details:
     methods automatically. It will not analyze library code, for which this
     option can thus be useful. For example, you could specify the method
     <code>System.currentTimeMillis()</code>, so that any idle calls to it will
-    be removed. Only applicable when optimizing. In general, making
-    assumptions can be dangerous; you can break your application. <i>Only use
-    this option if you know what you're doing!</i></dd>
+    be removed. Note that ProGuard applies the option to the entire hierarchy
+    of the specified methods. Only applicable when optimizing. In general,
+    making assumptions can be dangerous; you can easily break the processed
+    code. <i>Only use this option if you know what you're doing!</i></dd>
 
 <dt><a name="allowaccessmodification"><code><b>-allowaccessmodification</b></code></a></dt>
 
@@ -310,7 +350,12 @@ The sections below provide more details:
     "http://java.sun.com/docs/books/jls/second_edition/html/binaryComp.doc.html#47259"
     >Section 13.4.6</a>) formally do not require this, some virtual machines
     would have problems with the processed code otherwise. Only applicable
-    when optimizing.</dd>
+    when optimizing (and when 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>
 
 </dl>
 <p>
@@ -332,8 +377,9 @@ The sections below provide more details:
 
 <dd>Specifies to print the mapping from old names to new names for classes and
     class members that have been renamed. The mapping is printed to the
-    standard output or to the given file. It is required for subsequent
-    incremental obfuscation, or if you ever want to make sense again of <a
+    standard output or to the given file. For example, it is required for
+    subsequent <a href="examples.html#incremental">incremental
+    obfuscation</a>, or if you ever want to make sense again of <a
     href="examples.html#stacktrace">obfuscated stack traces</a>. Only
     applicable when obfuscating.</dd>
 
@@ -349,23 +395,23 @@ The sections below provide more details:
     obfuscation</a>, i.e. processing add-ons or small patches to an existing
     piece of code. In such cases, you should consider whether you also need
     the option <a
-    href="#useuniqueclassmembernames"><code><b>-useuniqueclassmembernames</b></code></a>.
+    href="#useuniqueclassmembernames"><code>-useuniqueclassmembernames</code></a>.
     Only applicable when obfuscating.</dd>
 
 <dt><a name="obfuscationdictionary"><code><b>-obfuscationdictionary</b></code></a>
     <a href="#filename"><i>filename</i></a></dt>
 
 <dd>Specifies a text file from which all valid words are used as obfuscated
-    method names. White space, punctuation characters, duplicate words, and
-    comments after a <code><b>#</b></code> sign are ignored. By default, short
-    names like 'a', 'b', etc. are used as obfuscated method names. With an
-    obfuscation dictionary, you can specify a list of reserved key words, or
-    identifiers with foreign characters, for instance. Note that this hardly
-    improves the obfuscation. Decent compilers can automatically replace them,
-    and the effect can fairly simply be undone by obfuscating again with
-    simpler names. The most useful application is specifying strings that are
-    typically already present in class files (such as 'Code' and
-    'Exceptions'), thus reducing the class file sizes just a little bit more.
+    field and method names. By default, short names like 'a', 'b', etc. are
+    used as obfuscated names. With an obfuscation dictionary, you can specify
+    a list of reserved key words, or identifiers with foreign characters, for
+    instance. White space, punctuation characters, duplicate words, and
+    comments after a <code><b>#</b></code> sign are ignored. Note that an
+    obfuscation dictionary hardly improves the obfuscation. Decent compilers
+    can automatically replace them, and the effect can fairly simply be undone
+    by obfuscating again with simpler names. The most useful application is
+    specifying strings that are typically already present in class files (such
+    as 'Code'), thus reducing the class file sizes just a little bit more.
     Only applicable when obfuscating.</dd>
 
 <dt><a name="overloadaggressively"><code><b>-overloadaggressively</b></code></a></dt>
@@ -373,10 +419,10 @@ The sections below provide more details:
 <dd>Specifies to apply aggressive overloading while obfuscating. Multiple
     fields and methods can then get the same names, as long as their arguments
     and return types are different (not just their arguments). This option can
-    make the output jar even smaller (and less comprehensible). Only
+    make the processed code even smaller (and less comprehensible). Only
     applicable when obfuscating.
     <p>
-    Counter-indications: the resulting class files fall within the Java
+    Counter-indication: 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
@@ -425,29 +471,9 @@ The sections below provide more details:
     <p>
     This option is only applicable when obfuscating. In fact, if you are
     planning on performing incremental obfuscation, you probably want to avoid
-    shrinking and optimization altogether, since these step could remove or
+    shrinking and optimization altogether, since these steps could remove or
     modify parts of your code that are essential for later additions.</dd>
 
-<dt><a name="defaultpackage"><code><b>-defaultpackage</b></code></a>
-    [<i>package_name</i>]</dt>
-
-<dd>Specifies to repackage all class files that are renamed into the single
-    given package. Without argument or with an empty string (''), the package
-    is removed completely. This option can make the output jar even smaller
-    (and less comprehensible). Only applicable when obfuscating.
-    <p>
-    This option implies the <a
-    href="#allowaccessmodification"><code><b>-allowaccessmodification</b></code></a>
-    option. In this case, ProGuard avoids access permissions becoming
-    inconsistent, by making all package visible classes and class members
-    public. These changes don't affect the functioning of applications. It
-    might be confusing and therefore less desirable when processing libraries.
-    <p>
-    Counter-indications: 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="dontusemixedcaseclassnames"><code><b>-dontusemixedcaseclassnames</b></code></a></dt>
 
 <dd>Specifies not to generate mixed-case class names while obfuscating. By
@@ -460,6 +486,35 @@ 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="flattenpackagehierarchy"><code><b>-flattenpackagehierarchy</b></code></a>
+    [<i>package_name</i>]</dt>
+
+<dd>Specifies to repackage all packages that are renamed, by moving them into
+    the single given parent package. Without argument or with an empty string
+    (''), the packages are moved into the root package. This option is one
+    example of further <a href="examples.html#repackaging">obfuscating package
+    names</a>. It can make the processed code smaller and less comprehensible.
+    Only applicable when obfuscating.</dd>
+
+<dt><a name="repackageclasses"><code><b>-repackageclasses</b></code></a>
+    [<i>package_name</i>]</dt>
+
+<dd>Specifies to repackage all class files that are renamed, by moving them
+    into the single given package. Without argument or with an empty string
+    (''), the package is removed completely. This option option overrides the
+    <a
+    href="#flattenpackagehierarchy"><code>-flattenpackagehierarchy</code></a>
+    option. It is another example of further <a
+    href="examples.html#repackaging">obfuscating package names</a>. It can
+    make the processed code even smaller and less comprehensible. Its
+    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>
+
 <dt><a name="keepattributes"><code><b>-keepattributes</b></code></a>
     [<i>attribute_name<b>,</b>...</i>]</dt>
 
@@ -468,20 +523,25 @@ The sections below provide more details:
     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 filters). Typical optional attributes are
+    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>,
     <code>LineNumberTable</code>, <code>LocalVariableTable</code>,
-    <code>LocalVariableTypeTable</code>, <code>SourceFile</code>,
-    <code>SourceDir</code>, <code>Deprecated</code>, <code>Synthetic</code>,
-    <code>Signature</code>, <code>EnclosingMethod</code>,
-    <code>RuntimeVisibleAnnotations</code>,
+    <code>LocalVariableTypeTable</code>, <code>Synthetic</code>,
+    <code>EnclosingMethod</code>, <code>RuntimeVisibleAnnotations</code>,
     <code>RuntimeInvisibleAnnotations</code>,
     <code>RuntimeVisibleParameterAnnotations</code>,
     <code>RuntimeInvisibleParameterAnnotations</code>, and
     <code>AnnotationDefault</code>. The <code>InnerClasses</code> attribute
     name can be specified as well, referring to the source name part of this
-    attribute. For example, you could keep the <code>Deprecated</code>
-    attribute when <a href="examples.html#library">processing a library</a>.
-    Only applicable when obfuscating.</dd>
+    attribute. For example, you should at least keep the
+    <code>Exceptions</code>, <code>InnerClasses</code>, and
+    <code>Signature</code> attributes when <a
+    href="examples.html#library">processing a library</a>. As another example,
+    you should keep the <code>SourceFile</code> and
+    <code>LineNumberTable</code> attributes for <a
+    href="examples.html#stacktrace">producing useful obfuscated stack
+    traces</a>. Only applicable when obfuscating.</dd>
 
 <dt><a name="renamesourcefileattribute"><code><b>-renamesourcefileattribute</b></code></a>
     [<i>string</i>]</dt>
@@ -494,6 +554,52 @@ 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="adaptresourcefilenames"><code><b>-adaptresourcefilenames</b></code></a>
+    [<i><a href="#filters">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
+    files that correspond to class files are renamed. With a filter, only
+    matching files are renamed. For example, see <a
+    href="examples.html#resourcefiles">processing resource files</a>. Only
+    applicable when obfuscating.</dd>
+
+<dt><a name="adaptresourcefilecontents"><code><b>-adaptresourcefilecontents</b></code></a>
+    [<i><a href="#filters">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
+    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>
+
+</dl>
+<p>
+
+<a name="preverificationoptions"> </a>
+<h2>Preverification Options</h2>
+
+<dl>
+<dt><a name="dontpreverify"><code><b>-dontpreverify</b></code></a></dt>
+
+<dd>Specifies not to preverify the processed class files. By default, class
+    files are preverified if they are targeted at Java Micro Edition or at
+    Java 6 or higher. For Java Micro Edition, preverification is required, so
+    you will need to run an external preverifier on the processed code if you
+    specify this option. For Java 6, preverification is not required (yet),
+    but it improves the efficiency of the class loading in the Java Virtual
+    Machine.</dd>
+
+<dt><a name="microedition"><code><b>-microedition</b></code></a></dt>
+
+<dd>Specifies that the processed class files are targeted at Java Micro
+    Edition. The preverifier will then add the appropriate StackMap
+    attributes, which are different from the default StackMapTable attributes
+    for Java Standard Edition. For example, you will need this option if you
+    are <a href="examples.html#midlets">processing midlets</a>.</dd>
+
 </dl>
 <p>
 
@@ -515,19 +621,28 @@ The sections below provide more details:
 
 <dt><a name="dontwarn"><code><b>-dontwarn</b></code></a></dt>
 
-<dd>Specifies not to warn about unresolved references at all. Again, if the
-    unresolved classes or class members are indeed required for processing,
-    the output jar will not function properly. <i>Only use this option if you
-    know what you're doing!</i></dd>
+<dd>Specifies not to warn about unresolved references and other important
+    problems at all. 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>
 
-<dd>Specifies to print any warnings about unresolved references to
-    superclasses, interfaces, or class members, but to continue processing in
-    any case. If the classes or class members are indeed required for
-    processing, the output jar will not function properly. <i>Only use this
+<dd>Specifies to print any warnings about unresolved references, and other
+    important problems, but to continue processing in any case. 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="printconfiguration"><code><b>-printconfiguration</b></code></a>
+    [<a href="#filename"><i>filename</i></a>]</dt>
+
+<dd>Specifies to write out the entire configuration that has been parsed, with
+    included files and replaced variables. The structure is printed to the
+    standard output or to the given file. This can sometimes be useful for
+    debugging configurations, or for converting XML configurations into a more
+    readable format.</dd>
+
 <dt><a name="dump"><code><b>-dump</b></code></a>
     [<a href="#filename"><i>filename</i></a>]</dt>
 
@@ -535,7 +650,7 @@ The sections below provide more details:
     any processing. The structure is printed to the standard output or to the
     given file. For example, you may want to <a
     href="examples.html#structure">write out the contents of a given jar
-    file</a>, without shrinking or obfuscating it first.</dd>
+    file</a>, without processing it at all.</dd>
 
 </dl>
 <p>
@@ -544,9 +659,9 @@ The sections below provide more details:
 <h2>Class Paths</h2>
 
 ProGuard accepts a generalization of class paths to specify input files and
-output files. A class path consists of entries, separated by the path
-separator (e.g. '<b>:</b>' on Unix, or '<b>;</b>' on Windows platforms). The
-order of the entries determines their priorities, in case of duplicates.
+output files. A class path consists of entries, separated by the traditional
+path separator (e.g. '<b>:</b>' on Unix, or '<b>;</b>' on Windows platforms).
+The order of the entries determines their priorities, in case of duplicates.
 <p>
 Each input entry can be:
 <ul>
@@ -580,7 +695,8 @@ everything to an output directory is the most straightforward option: the
 output directory will contain a complete reconstruction of the input entries.
 The packaging can be almost arbitrarily complex though: you could process an
 entire application, packaged in a zip file along with its documentation,
-writing it out as a zip file again.
+writing it out as a zip file again. The Examples section shows a few ways
+to <a href="examples.html#restructuring">restructure output archives</a>.
 <p>
 Files and directories can be specified as discussed in the section on <a
 href="#filename">file names</a> below.
@@ -688,6 +804,9 @@ For example,
 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 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>
@@ -735,6 +854,36 @@ If you're not sure which option you need, you should probably simply use
 are not removed in the shrinking step, and not renamed in the obfuscation step.
 <p>
 
+<a name="keepoptionmodifiers"> </a>
+<h2>Keep Option Modifiers</h2>
+
+<dl>
+<dt><a name="allowshrinking"><code><b>allowshrinking</b></code></a></dt>
+
+<dd>Specifies that the entry points specified in the <a href="#keep">-keep</a>
+    option may be shrunk, even if they have to be preserved otherwise. That
+    is, the entry points may be removed in the shrinking step, but if they are
+    necessary after all, they may not be optimized or obfuscated.</dd>
+
+<dt><a name="allowoptimization"><code><b>allowoptimization</b></code></a></dt>
+
+<dd>Specifies that the entry points specified in the <a href="#keep">-keep</a>
+    option may be optimized, even if they have to be preserved otherwise. That
+    is, the entry points may be altered in the optimization step, but they may
+    not be removed or obfuscated. This modifier is only useful for achieving
+    unusual requirements.</dd>
+
+<dt><a name="allowobfuscation"><code><b>allowobfuscation</b></code></a></dt>
+
+<dd>Specifies that the entry points specified in the <a href="#keep">-keep</a>
+    option may be obfuscated, even if they have to be preserved otherwise. That
+    is, the entry points may be renamed in the obfuscation step, but they may
+    not be removed or optimized. This modifier is only useful for achieving
+    unusual requirements.</dd>
+
+</dl>
+<p>
+
 <a name="classspecification"> </a>
 <h2>Class Specifications</h2>
 
@@ -750,16 +899,16 @@ definition:
 <p>
 
 <pre>
-[[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] ([<b>!</b>]<b>interface</b>)|<b>class</b> <i>classname</i>
-    [<b>extends</b>|<b>implements</b> <i>classname</i>]
+[<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>final</b>|<b>abstract</b> ...] [<b>!</b>]<b>interface</b>|<b>class</b> <i>classname</i>
+    [<b>extends</b>|<b>implements</b> [<b>@</b><i>annotationtype</i>] <i>classname</i>]
 [<b>{</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>volatile</b>|<b>transient</b> ...] <b><fields></b> |
                                                                       (<i>fieldtype fieldname</i>)<b>;</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b>|<b>synchronized</b>|<b>native</b>|<b>abstract</b>|<b>strictfp</b> ...] <b><methods></b> |
                                                                                            <b><init>(</b><i>argumenttype,...</i><b>)</b> |
                                                                                            <i>classname</i><b>(</b><i>argumenttype,...</i><b>)</b> |
                                                                                            (<i>returntype methodname</i><b>(</b><i>argumenttype,...</i><b>)</b>)<b>;</b>
-    [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
+    [<b>@</b><i>annotationtype</i>] [[<b>!</b>]<b>public</b>|<b>private</b>|<b>protected</b>|<b>static</b> ... ] <b>*;</b>
     ...
 <b>}</b>]
 </pre>
@@ -830,8 +979,14 @@ files.
     separate option.
     <p>
 
+<li>The <code><b>@</b></code> specifications can be used to restrict classes
+    and class members to the ones that are annotated with the specified
+    annotation types. An <i>annotationtype</i> is specified just like a
+    <i>classname</i>.
+    <p>
+
 <li>Fields and methods are specified much like in Java, except that method
-    argument lists don't contain argument names (as is common in other tools
+    argument lists don't contain argument names (just like in other tools
     like <code>javadoc</code> and <code>javap</code>). The specifications can
     also contain the following catch-all wildcards:
 
@@ -868,6 +1023,9 @@ files.
     Types in descriptors can contain the following wildcards:
 
 <table cellspacing="10">
+<tr><td valign="top"><code><b>%</b></code></td>
+    <td>matches any primitive type ("<code>boolean</code>", "<code>int</code>",
+        etc, but not "<code>void</code>").</td></tr>
 <tr><td valign="top"><code><b>?</b></code></td>
     <td>matches any single character in a class name.</td></tr>
 <tr><td valign="top"><code><b>*</b></code></td>
@@ -875,17 +1033,20 @@ files.
 <tr><td valign="top"><code><b>**</b></code></td>
     <td>matches any part of a class name, possibly containing any number of
         package separators.</td></tr>
-<tr><td valign="top"><code><b>%</b></code></td>
-    <td>matches any primitive type ("<code>boolean</code>", "<code>int</code>",
-        etc, but not "<code>void</code>").</td></tr>
+<tr><td valign="top"><code><b>***</b></code></td>
+    <td>matches any type (primitive or non-primitive, array or
+        non-array).</td></tr>
+<tr><td valign="top"><code><b>...</b></code></td>
+    <td>matches any number of arguments of any type.</td></tr>
+
 </table>
 
     Note that the <code>?</code>, <code>*</code>, and <code>**</code>
-    wildcards will never match primitive types. None of the wildcards will
-    match array types of different dimensions than specified by the regular
-    expression. For example, "<code>** get*()</code>" matches
-    "<code>java.lang.Object getObject()</code>", but not "<code>float
-    getFloat()</code>", nor "<code>java.lang.Object[] getObjects()</code>".
+    wildcards will never match primitive types. Furthermore, only the
+    <code>***</code> wildcards will match array types of any dimension. For
+    example, "<code>** get*()</code>" matches "<code>java.lang.Object
+    getObject()</code>", but not "<code>float getFloat()</code>", nor
+    "<code>java.lang.Object[] getObjects()</code>".
     <p>
 
 <li>Constructors can also be specified using their short class names (without
diff --git a/docs/manual/wtk.html b/docs/manual/wtk.html
index dab39e2..77bfb90 100644
--- a/docs/manual/wtk.html
+++ b/docs/manual/wtk.html
@@ -4,14 +4,14 @@
 <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>ProGuard J2ME Wireless Toolkit Integration</title>
+<title>ProGuard JME Wireless Toolkit Integration</title>
 </head>
 <body>
 
-<h2>J2ME Wireless Toolkit Integration</h2>
+<h2>JME Wireless Toolkit Integration</h2>
 
-<b>ProGuard</b> can be seamlessly integrated in the Sun J2ME Wireless Toolkit
-(WTK).
+<b>ProGuard</b> can be seamlessly integrated in Sun's Wireless Toolkit (WTK)
+for Java Micro Edition (JME).
 <p>
 
 The WTK already comes with a plug-in for ProGuard. Alternatively, ProGuard
@@ -45,7 +45,7 @@ the build process. The build process is started from the WTK menu bar:
 This option will compile, shrink, obfuscate, verify, and install your midlets
 for testing.
 <p>
-Should you ever need to customize your ProGuard configuration for the J2ME WTK,
+Should you ever need to customize your ProGuard configuration for the JME WTK,
 you can adapt the configuration file <code>proguard/wtk/default.pro</code>
 that's inside the <code>proguard.jar</code>.
 
diff --git a/docs/quality.html b/docs/quality.html
index f9e51eb..c8ae514 100644
--- a/docs/quality.html
+++ b/docs/quality.html
@@ -31,7 +31,7 @@ full-screen size.
 <p>
 
 In addition, <b>ProGuard</b> is tested against a constantly growing test suite
-(over 300 tests at this time of writing). These small programs contain a wide
+(over 400 tests at this time of writing). These small programs contain a wide
 range of common and uncommon constructs, in order to detect any regression
 problems as soon as possible.
 
diff --git a/docs/results.html b/docs/results.html
index 3e5c003..240f773 100644
--- a/docs/results.html
+++ b/docs/results.html
@@ -10,9 +10,11 @@
 
 <h2>Results</h2>
 
-<b>ProGuard</b> shrinks, optimizes, and obfuscates jars quickly and
-effectively. The improvements obviously depend on the original code. The table
-below presents some typical results:
+<b>ProGuard</b> successfully processes any Java bytecode, ranging from small
+midlets to entire run-time libraries. It primarily reduces the size of the
+processed code, with some potential increase in efficiency as an added bonus.
+The improvements obviously depend on the original code. The table below
+presents some typical results:
 <p>
 
 <table>
@@ -29,86 +31,97 @@ below presents some typical results:
 </tr>
 
 <tr>
-<td><a target="other" href="http://www.gnu.org/software/classpath/">ClassPath</a>, the GNU runtime library</td>
-<td align="center">2.0 M</td>
-<td align="center">2.0 M</td>
-<td align="center">1.9 M</td>
-<td align="center">1.9 M</td>
-<td align="center">4 %</td>
-<td align="center">31 s</td>
-<td align="center">39 M</td>
+<td><a target="other" href="http://java.sun.com/j2me/">Worm</a>, a sample midlet from Sun's JME</td>
+<td align="center">10.3 K</td>
+<td align="center">9.8 K</td>
+<td align="center">9.6 K</td>
+<td align="center">8.5 K</td>
+<td align="center">18 %</td>
+<td align="center">2 s</td>
+<td align="center">19 M</td>
 </tr>
 
 <tr>
-<td><a target="other" href="http://java.sun.com/j2me/">Worm</a>, a sample midlet from Sun's J2ME</td>
-<td align="center">9.9 K</td>
-<td align="center">9.4 K</td>
-<td align="center">9.3 K</td>
-<td align="center">8.1 K</td>
-<td align="center">17 %</td>
-<td align="center">2 s</td>
-<td align="center">13 M</td>
+<td><a target="other" href="http://www.javadocking.com/">Javadocking</a>, a docking library</td>
+<td align="center">290 K</td>
+<td align="center">281 K</td>
+<td align="center">270 K</td>
+<td align="center">201 K</td>
+<td align="center">30 %</td>
+<td align="center">12 s</td>
+<td align="center">32 M</td>
 </tr>
 
 <tr>
 <td><b>ProGuard</b> itself</td>
-<td align="center">404 K</td>
-<td align="center">364 K</td>
-<td align="center">361 K</td>
-<td align="center">231 K</td>
-<td align="center">42 %</td>
-<td align="center">21 s</td>
-<td align="center">35 M</td>
+<td align="center">648 K</td>
+<td align="center">579 K</td>
+<td align="center">557 K</td>
+<td align="center">348 K</td>
+<td align="center">46 %</td>
+<td align="center">28 s</td>
+<td align="center">66 M</td>
 </tr>
 
 <tr>
 <td><a target="other" href="http://www.clarkware.com/software/JDepend.html">JDepend</a>, a Java quality metrics tool</td>
 <td align="center">57 K</td>
 <td align="center">36 K</td>
-<td align="center">35 K</td>
-<td align="center">30 K</td>
-<td align="center">48 %</td>
-<td align="center">8 s</td>
+<td align="center">33 K</td>
+<td align="center">28 K</td>
+<td align="center">51 %</td>
+<td align="center">6 s</td>
 <td align="center">24 M</td>
 </tr>
 
 <tr>
+<td><a target="other" href="http://java.sun.com/javase/6/">the run-time classes</a> from Sun's Java 6</td>
+<td align="center">53 M</td>
+<td align="center">23 M</td>
+<td align="center">22 M</td>
+<td align="center">18 M</td>
+<td align="center">66 %</td>
+<td align="center">16 min</td>
+<td align="center">270 M</td>
+</tr>
+
+<tr>
 <td><a target="other" href="http://jakarta.apache.org/tomcat/index.html">Tomcat</a>, the Apache servlet container</td>
-<td align="center">1.2 M</td>
-<td align="center">538 K</td>
-<td align="center">528 K</td>
-<td align="center">363 K</td>
-<td align="center">68 %</td>
-<td align="center">20 s</td>
-<td align="center">39 M</td>
+<td align="center">1.1 M</td>
+<td align="center">466 K</td>
+<td align="center">426 K</td>
+<td align="center">295 K</td>
+<td align="center">74 %</td>
+<td align="center">17 s</td>
+<td align="center">44 M</td>
 </tr>
 
 <tr>
 <td><a target="other" href="http://www.kclee.com/clemens/java/javancss/">JavaNCSS</a>, a Java source metrics tool</td>
-<td align="center">624 K</td>
-<td align="center">243 K</td>
-<td align="center">235 K</td>
-<td align="center">158 K</td>
-<td align="center">74 %</td>
-<td align="center">13 s</td>
-<td align="center">35 M</td>
+<td align="center">632 K</td>
+<td align="center">242 K</td>
+<td align="center">212 K</td>
+<td align="center">152 K</td>
+<td align="center">75 %</td>
+<td align="center">20 s</td>
+<td align="center">36 M</td>
 </tr>
 
 <tr>
 <td><a target="other" href="http://ant.apache.org/">Ant</a>, the Apache build tool</td>
-<td align="center">2.3 M</td>
-<td align="center">276 K</td>
-<td align="center">265 K</td>
-<td align="center">203 K</td>
-<td align="center">91 %</td>
-<td align="center">20 s</td>
-<td align="center">49 M</td>
+<td align="center">2.4 M</td>
+<td align="center">401 K</td>
+<td align="center">325 K</td>
+<td align="center">242 K</td>
+<td align="center">90 %</td>
+<td align="center">23 s</td>
+<td align="center">61 M</td>
 </tr>
 
 </table>
 <p>
-Results were measured with ProGuard 3.0 on a 2.6 GHz Pentium 4 with 512 MB
-of memory, using Sun JDK 1.4.2 in Fedora Core 1 Linux.
+Results were measured with ProGuard 4.0 on a 2.6 GHz Pentium 4 with 512 MB
+of memory, using Sun JDK 1.5.0 in Fedora Core 3 Linux.
 <p>
 The program sizes include companion libraries. The shrinking step produces the
 best results for programs that use only small parts of their libraries. The
@@ -116,14 +129,22 @@ obfuscation step can significantly shrink large programs even further, since
 the identifiers of their many internal references can be replaced by short
 identifiers.
 <p>
-Timings are mainly governed by the fixed overhead of reading jars and
-initializing data structures. The actual shrinking, optimization, and
-obfuscation are typically fast in comparison.
+The Java 6 run-time classes are the most complex example. The classes perform
+a lot of introspection, interacting with the native code of the virtual
+machine. The 1500+ lines of configuration were largely composed by automated
+analysis, complemented by a great deal of trial and error. The configuration
+is probably not complete, but the resulting library successfully serves as a
+run-time environment for running applications like ProGuard and the ProGuard
+GUI.
+<p>
+For small inputs, timings are governed by the reading and parsing of the jars.
+For large inputs, the optimization step becomes more important. For instance,
+processing the Java 6 run-time classes without optimization only takes 2
+minutes.
 <p>
 Memory usage (the amount of physical memory used by ProGuard while processing)
-is governed by the basic java virtual machine and the total size of the
+is governed by the basic java virtual machine and by the total size of the
 library jars and program jars.
-
 <hr>
 <address>
 Copyright © 2002-2007
diff --git a/docs/sanawarelogo.png b/docs/sanawarelogo.png
new file mode 100644
index 0000000..bf7218b
Binary files /dev/null and b/docs/sanawarelogo.png differ
diff --git a/docs/screenshot_console.gif b/docs/screenshot_console.gif
index f920538..8aea61e 100644
Binary files a/docs/screenshot_console.gif and b/docs/screenshot_console.gif differ
diff --git a/docs/screenshot_console_small.gif b/docs/screenshot_console_small.gif
index f8cd9dc..3f55f5b 100644
Binary files a/docs/screenshot_console_small.gif and b/docs/screenshot_console_small.gif differ
diff --git a/docs/screenshot_gui1.gif b/docs/screenshot_gui1.gif
index 68b1ee9..233e180 100644
Binary files a/docs/screenshot_gui1.gif and b/docs/screenshot_gui1.gif differ
diff --git a/docs/screenshot_gui2.gif b/docs/screenshot_gui2.gif
index 4ab61af..aecdb35 100644
Binary files a/docs/screenshot_gui2.gif and b/docs/screenshot_gui2.gif differ
diff --git a/docs/screenshot_gui3.gif b/docs/screenshot_gui3.gif
index 5805e39..17950ee 100644
Binary files a/docs/screenshot_gui3.gif and b/docs/screenshot_gui3.gif differ
diff --git a/docs/screenshot_gui4.gif b/docs/screenshot_gui4.gif
index 9326203..31e60fa 100644
Binary files a/docs/screenshot_gui4.gif and b/docs/screenshot_gui4.gif differ
diff --git a/docs/screenshot_gui5.gif b/docs/screenshot_gui5.gif
index ac510e9..1313db2 100644
Binary files a/docs/screenshot_gui5.gif and b/docs/screenshot_gui5.gif differ
diff --git a/docs/screenshot_gui6.gif b/docs/screenshot_gui6.gif
index f75027a..c200b97 100644
Binary files a/docs/screenshot_gui6.gif and b/docs/screenshot_gui6.gif differ
diff --git a/docs/screenshot_gui7.gif b/docs/screenshot_gui7.gif
index 12e088c..bc5d6ed 100644
Binary files a/docs/screenshot_gui7.gif and b/docs/screenshot_gui7.gif differ
diff --git a/docs/screenshot_gui8.gif b/docs/screenshot_gui8.gif
index f862ccf..95a1a30 100644
Binary files a/docs/screenshot_gui8.gif and b/docs/screenshot_gui8.gif differ
diff --git a/docs/screenshots_gui_small.gif b/docs/screenshots_gui_small.gif
index a367431..bc32ccb 100644
Binary files a/docs/screenshots_gui_small.gif and b/docs/screenshots_gui_small.gif differ
diff --git a/docs/sections.html b/docs/sections.html
index 3227667..efa7012 100644
--- a/docs/sections.html
+++ b/docs/sections.html
@@ -49,6 +49,9 @@ document.write("\" width=\"88\" height=\"31\" alt=\"SourceForge\">");
 <p>
 <a href="http://www.luciad.com/" target="other">
 <img src="luciadlogo.png" width="88" height="24" alt="Luciad"></a>
+<p>
+<a href="http://www.javadocking.com/" target="other">
+<img src="sanawarelogo.png" width="88" height="24" alt="Sanaware"></a>
 </center>
 
 </body>
diff --git a/docs/testimonials.html b/docs/testimonials.html
index f58002b..73c3b89 100644
--- a/docs/testimonials.html
+++ b/docs/testimonials.html
@@ -12,11 +12,12 @@
 
 And now for some shameless self-glorification and name-dropping...
 <p>
-<b>ProGuard</b> is probably the most popular free obfuscator and shrinker. It
-is already being used by developers at companies and organizations like Sun,
-IBM, HP, Siemens, Nokia, and NATO. Although the quotes below probably don't
-represent official views of any kind, encouragements like these do keep me
-happy.
+<b>ProGuard</b> is probably the most popular java shrinker, optimizer, and
+obfuscator world-wide. It is being used by developers at companies and
+organizations like Sun, IBM, HP, Siemens, Nokia, and NATO. It is the default
+tool in many development environments like Sun's Wireless Toolkit, Netbeans,
+EclipseME, and more. Although the quotes below probably don't represent
+official views of any kind, encouragements like these do keep me happy.
 <p>
 
 <center><table class="note">
@@ -81,10 +82,8 @@ and accurate shrinker of them all.
 </tr></table></center>
 <p>
 
-From the article <a target="other"
-href="http://developers.sun.com/techtopics/mobility/midp/ttips/proguard/">"Obfuscating
-MIDlet Suites with ProGuard"</a> at <a target="other"
-href="http://developers.sun.com/">developers.sun.com</a>:
+From the article "Obfuscating MIDlet Suites with ProGuard" at <a
+target="other" href="http://developers.sun.com/">developers.sun.com</a>:
 <p>
 <center><table class="note">
 <tr><td class="note"><p class="note"><cite>
diff --git a/docs/title.html b/docs/title.html
index e506f1a..f9ee64f 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 3.9</div>
+<div>Version 4.1</div>
 </div>
 
 </body>
diff --git a/examples/applications.pro b/examples/annotations/examples.pro
similarity index 60%
copy from examples/applications.pro
copy to examples/annotations/examples.pro
index 9b19761..270092a 100644
--- a/examples/applications.pro
+++ b/examples/annotations/examples.pro
@@ -1,31 +1,33 @@
 #
-# This ProGuard configuration file illustrates how to process applications.
+# This ProGuard configuration file illustrates how to use annotations for
+# specifying which classes and class members should be kept.
 # Usage:
-#     java -jar proguard.jar @applications.pro
+#     java -jar proguard.jar @examples.pro
 #
 
-# Specify the input jars, output jars, and library jars.
+# Specify the input, output, and library jars.
+# This is assuming the code has been compiled in the examples directory.
 
--injars  in.jar
--outjars out.jar
+#-injars  examples(*.class)
+-injars  classes(*.class)
+-outjars out
 
 -libraryjars <java.home>/lib/rt.jar
-#-libraryjars junit.jar
-#-libraryjars servlet.jar
-#-libraryjars jai_core.jar
-#...
 
-# Preserve all public applications.
+# Some important configuration is based on the annotations in the code.
+# We have to specify what the annotations mean to ProGuard.
 
--keepclasseswithmembers public class * {
-    public static void main(java.lang.String[]);
-}
+-include lib/annotations.pro
+
+#
+# We can then still add any other options that might be useful.
+#
 
 # Print out a list of what we're preserving.
 
 -printseeds
 
-# Preserve all annotations.
+# Preserve all annotations themselves.
 
 -keepattributes *Annotation*
 
@@ -51,15 +53,9 @@
 
 -keepclassmembers class * implements java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
     java.lang.Object readResolve();
 }
-
-# Your application may contain more items that need to be preserved; 
-# typically classes that are dynamically created using Class.forName:
-
-# -keep public class mypackage.MyClass
-# -keep public interface mypackage.MyInterface
-# -keep public class * implements mypackage.MyInterface
diff --git a/examples/annotations/examples/Applet.java b/examples/annotations/examples/Applet.java
new file mode 100644
index 0000000..5a04887
--- /dev/null
+++ b/examples/annotations/examples/Applet.java
@@ -0,0 +1,20 @@
+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
+ *
+ * The annotation will preserve the class and its essential methods.
+ */
+ at Keep
+public class Applet extends java.applet.Applet
+{
+    // Implementations for Applet.
+
+    public void init()
+    {
+        // ...
+    }
+}
diff --git a/examples/annotations/examples/Application.java b/examples/annotations/examples/Application.java
new file mode 100644
index 0000000..96ac208
--- /dev/null
+++ b/examples/annotations/examples/Application.java
@@ -0,0 +1,18 @@
+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
+ *
+ * The annotation will preserve the class and its main method.
+ */
+ at KeepApplication
+public class Application
+{
+    public static void main(String[] args)
+    {
+        System.out.println("The answer is 42");
+    }
+}
diff --git a/examples/annotations/examples/Bean.java b/examples/annotations/examples/Bean.java
new file mode 100644
index 0000000..e49a88e
--- /dev/null
+++ b/examples/annotations/examples/Bean.java
@@ -0,0 +1,54 @@
+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
+ *
+ * The annotations will preserve the class and its public getters and setters.
+ */
+ at Keep
+ at KeepPublicGettersSetters
+public class Bean
+{
+    public boolean booleanProperty;
+    public int     intProperty;
+    public String  stringProperty;
+
+
+    public boolean isBooleanProperty()
+    {
+        return booleanProperty;
+    }
+
+
+    public void setBooleanProperty(boolean booleanProperty)
+    {
+        this.booleanProperty = booleanProperty;
+    }
+
+
+    public int getIntProperty()
+    {
+        return intProperty;
+    }
+
+
+    public void setIntProperty(int intProperty)
+    {
+        this.intProperty = intProperty;
+    }
+
+
+    public String getStringProperty()
+    {
+        return stringProperty;
+    }
+
+
+    public void setStringProperty(String stringProperty)
+    {
+        this.stringProperty = stringProperty;
+    }
+}
diff --git a/examples/annotations/examples/NativeCallBack.java b/examples/annotations/examples/NativeCallBack.java
new file mode 100644
index 0000000..9c34884
--- /dev/null
+++ b/examples/annotations/examples/NativeCallBack.java
@@ -0,0 +1,42 @@
+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
+ *
+ * The annotation will preserve the class and its main method.
+ */
+ at KeepApplication
+public class NativeCallBack
+{
+    /**
+     * Suppose this is a native method that computes an answer.
+     *
+     * The -keep option regular ProGuard configuration will make sure it is
+     * not renamed when processing this code.
+     */
+    public native int computeAnswer();
+
+
+    /**
+     * Suppose this method is called back from the above native method.
+     *
+     * ProGuard would remove it, because it is not referenced from java.
+     * The annotation will make sure it is preserved anyhow.
+     */
+    @Keep
+    public int getAnswer()
+    {
+        return 42;
+    }
+
+
+    public static void main(String[] args)
+    {
+        int answer = new NativeCallBack().computeAnswer();
+
+        System.out.println("The answer is " + answer);
+    }
+}
diff --git a/examples/annotations/lib/annotations.jar b/examples/annotations/lib/annotations.jar
new file mode 100644
index 0000000..e6e440b
Binary files /dev/null and b/examples/annotations/lib/annotations.jar differ
diff --git a/examples/annotations/lib/annotations.pro b/examples/annotations/lib/annotations.pro
new file mode 100644
index 0000000..f704af2
--- /dev/null
+++ b/examples/annotations/lib/annotations.pro
@@ -0,0 +1,118 @@
+#
+# This ProGuard configuration file specifies how annotations can be used
+# to configure the processing of other code.
+# Usage:
+#     java -jar proguard.jar @annotations.pro -libraryjars annotations.jar ...
+#
+# Note that the other input/output options still have to be specified.
+# If you specify them in a separate file, you can simply include this file:
+#     -include annotations.pro
+#
+# You can add any other options that are required. For instance, if you are
+# processing a library, you can still include the options from library.pro.
+
+
+# The annotations are defined in the accompanying jar. For now, we'll start
+# with these. You can always define your own annotations, if necessary.
+-libraryjars annotations.jar
+
+
+# The following annotations can be specified with classes and with class
+# members.
+
+# @Keep specifies not to shrink, optimize, or obfuscate the annotated class
+# or class member as an entry point.
+
+-keep @proguard.annotation.Keep class *
+
+-keepclassmembers class * {
+    @proguard.annotation.Keep *;
+}
+
+
+# @KeepName specifies not to optimize or obfuscate the annotated class or
+# class member as an entry point.
+
+-keepnames @proguard.annotation.KeepName class *
+
+-keepclassmembernames class * {
+    @proguard.annotation.KeepName *;
+}
+
+
+# The following annotations can only be specified with classes.
+
+# @KeepImplementations and @KeepPublicImplementations specify to keep all,
+# resp. all public, implementations or extensions of the annotated class as
+# entry points. Note the extension of the java-like syntax, adding annotations
+# before the (wild-carded) interface name.
+
+-keep        class * implements @proguard.annotation.KeepImplementations       *
+-keep public class * implements @proguard.annotation.KeepPublicImplementations *
+
+# @KeepApplication specifies to keep the annotated class as an application,
+# together with its main method.
+
+-keepclasseswithmembers @proguard.annotation.KeepApplication public class * {
+    public static void main(java.lang.String[]);
+}
+
+# @KeepClassMembers, @KeepPublicClassMembers, and
+# @KeepPublicProtectedClassMembers specify to keep all, all public, resp.
+# all public or protected, class members of the annotated class from being
+# shrunk, optimized, or obfuscated as entry points.
+
+-keepclassmembers @proguard.annotation.KeepClassMembers class * {
+    *;
+}
+
+-keepclassmembers @proguard.annotation.KeepPublicClassMembers class * {
+    public *;
+}
+
+-keepclassmembers @proguard.annotation.KeepPublicProtectedClassMembers class * {
+    public protected *;
+}
+
+# @KeepClassMemberNames, @KeepPublicClassMemberNames, and
+# @KeepPublicProtectedClassMemberNames specify to keep all, all public, resp.
+# all public or protected, class members of the annotated class from being
+# optimized or obfuscated as entry points.
+
+-keepclassmembernames @proguard.annotation.KeepClassMemberNames class * {
+    *;
+}
+
+-keepclassmembernames @proguard.annotation.KeepPublicClassMemberNames class * {
+    public *;
+}
+
+-keepclassmembernames @proguard.annotation.KeepPublicProtectedClassMemberNames class * {
+    public protected *;
+}
+
+# @KeepGettersSetters and @KeepPublicGettersSetters specify to keep all, resp.
+# all public, getters and setters of the annotated class from being shrunk,
+# optimized, or obfuscated as entry points.
+
+-keepclassmembers @proguard.annotation.KeepGettersSetters class * {
+    void set*(***);
+    void set*(int, ***);
+
+    boolean is*();
+    boolean is*(int);
+
+    *** get*();
+    *** get*(int);
+}
+
+-keepclassmembers @proguard.annotation.KeepPublicGettersSetters class * {
+    public void set*(***);
+    public void set*(int, ***);
+
+    public boolean is*();
+    public boolean is*(int);
+
+    public *** get*();
+    public *** get*(int);
+}
diff --git a/examples/annotations/src/proguard/annotation/Keep.java b/examples/annotations/src/proguard/annotation/Keep.java
new file mode 100644
index 0000000..93a469f
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/Keep.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies not to optimize or obfuscate the annotated class or
+ * class member as an entry point.
+ */
+ at Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface Keep {}
diff --git a/examples/annotations/src/proguard/annotation/KeepApplication.java b/examples/annotations/src/proguard/annotation/KeepApplication.java
new file mode 100644
index 0000000..181f9b1
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepApplication.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep the annotated class as an application,
+ * together with its a main method.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepApplication {}
diff --git a/examples/annotations/src/proguard/annotation/KeepClassMemberNames.java b/examples/annotations/src/proguard/annotation/KeepClassMemberNames.java
new file mode 100644
index 0000000..b2f1df9
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepClassMemberNames.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all class members of the annotated class
+ * from being optimized or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepClassMemberNames {}
diff --git a/examples/annotations/src/proguard/annotation/KeepClassMembers.java b/examples/annotations/src/proguard/annotation/KeepClassMembers.java
new file mode 100644
index 0000000..7ed755b
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepClassMembers.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all class members of the annotated class
+ * from being shrunk, optimized, or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepClassMembers {}
diff --git a/examples/annotations/src/proguard/annotation/KeepGettersSetters.java b/examples/annotations/src/proguard/annotation/KeepGettersSetters.java
new file mode 100644
index 0000000..497dcb7
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepGettersSetters.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all getters and setters of the annotated
+ * class from being shrunk, optimized, or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepGettersSetters {}
diff --git a/examples/annotations/src/proguard/annotation/KeepImplementations.java b/examples/annotations/src/proguard/annotation/KeepImplementations.java
new file mode 100644
index 0000000..47406a3
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepImplementations.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all implementations or extensions of the
+ * annotated class as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepImplementations {}
diff --git a/examples/annotations/src/proguard/annotation/KeepName.java b/examples/annotations/src/proguard/annotation/KeepName.java
new file mode 100644
index 0000000..5dd3680
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepName.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies not to optimize or obfuscate the annotated class or
+ * class member as an entry point.
+ */
+ at Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepName {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java b/examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java
new file mode 100644
index 0000000..f24b126
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicClassMemberNames.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public class members of the annotated
+ * class from being optimized or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicClassMemberNames {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java b/examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java
new file mode 100644
index 0000000..2be7fa4
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicClassMembers.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public class members of the annotated
+ * class from being shrunk, optimized, or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicClassMembers {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java b/examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java
new file mode 100644
index 0000000..6028ba9
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicGettersSetters.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public getters and setters of the
+ * annotated class from being shrunk, optimized, or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicGettersSetters {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicImplementations.java b/examples/annotations/src/proguard/annotation/KeepPublicImplementations.java
new file mode 100644
index 0000000..52ee5b9
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicImplementations.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public implementations or extensions
+ * of the annotated class as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicImplementations {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java b/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java
new file mode 100644
index 0000000..59f0004
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMemberNames.java
@@ -0,0 +1,18 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public or protected class members of
+ * the annotated class from being optimized or obfuscated as entry points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicProtectedClassMemberNames {}
diff --git a/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java b/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java
new file mode 100644
index 0000000..e918a9b
--- /dev/null
+++ b/examples/annotations/src/proguard/annotation/KeepPublicProtectedClassMembers.java
@@ -0,0 +1,19 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ */
+package proguard.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation specifies to keep all public or protected class members of
+ * the annotated class from being shrunk, optimized, or obfuscated as entry
+ * points.
+ */
+ at Target({ ElementType.TYPE })
+ at Retention(RetentionPolicy.CLASS)
+ at Documented
+public @interface KeepPublicProtectedClassMembers {}
diff --git a/examples/ant/applets.xml b/examples/ant/applets.xml
index 820a4f2..a55b1c3 100644
--- a/examples/ant/applets.xml
+++ b/examples/ant/applets.xml
@@ -50,9 +50,12 @@
          compatible, please refer to the manual. -->
 
     <keepclassmembers implements="java.io.Serializable">
-      <field  access    ="final"
+      <field  access    ="static final"
               type      ="long"
               name      ="serialVersionUID" />
+      <field  access    ="static final"
+              type      ="java.io.ObjectStreamField[]"
+              name      ="serialPersistentFields" />
       <method access    ="private"
               type      ="void"
               name      ="writeObject"
@@ -60,7 +63,7 @@
       <method access    ="private"
               type      ="void"
               name      ="readObject"
-              parameters="java.io.ObjectOutputStream" />
+              parameters="java.io.ObjectInputStream" />
       <method type      ="java.lang.Object"
               name      ="writeReplace"
               parameters="" />
diff --git a/examples/ant/applications2.xml b/examples/ant/applications2.xml
index 54947d8..2e28f01 100644
--- a/examples/ant/applications2.xml
+++ b/examples/ant/applications2.xml
@@ -52,6 +52,7 @@
 
     -keepclassmembers class * implements java.io.Serializable {
         static final long serialVersionUID;
+        static final java.io.ObjectStreamField[] serialPersistentFields;
         private void writeObject(java.io.ObjectOutputStream);
         private void readObject(java.io.ObjectInputStream);
         java.lang.Object writeReplace();
diff --git a/examples/ant/applications3.xml b/examples/ant/applications3.xml
index 5a7cb80..d5501e0 100644
--- a/examples/ant/applications3.xml
+++ b/examples/ant/applications3.xml
@@ -61,9 +61,12 @@
          compatible, please refer to the manual. -->
 
     <keepclassmembers implements="java.io.Serializable">
-      <field  access    ="final"
+      <field  access    ="static final"
               type      ="long"
               name      ="serialVersionUID" />
+      <field  access    ="static final"
+              type      ="java.io.ObjectStreamField[]"
+              name      ="serialPersistentFields" />
       <method access    ="private"
               type      ="void"
               name      ="writeObject"
@@ -71,7 +74,7 @@
       <method access    ="private"
               type      ="void"
               name      ="readObject"
-              parameters="java.io.ObjectOutputStream" />
+              parameters="java.io.ObjectInputStream" />
       <method type      ="java.lang.Object"
               name      ="writeReplace"
               parameters="" />
diff --git a/examples/ant/library.xml b/examples/ant/library.xml
index 5a8320a..d87bd16 100644
--- a/examples/ant/library.xml
+++ b/examples/ant/library.xml
@@ -74,6 +74,9 @@
       <field  access    ="final"
               type      ="long"
               name      ="serialVersionUID" />
+      <field  access    ="static final"
+              type      ="java.io.ObjectStreamField[]"
+              name      ="serialPersistentFields" />
       <method access    ="private"
               type      ="void"
               name      ="writeObject"
@@ -81,7 +84,7 @@
       <method access    ="private"
               type      ="void"
               name      ="readObject"
-              parameters="java.io.ObjectOutputStream" />
+              parameters="java.io.ObjectInputStream" />
       <method type      ="java.lang.Object"
               name      ="writeReplace"
               parameters="" />
diff --git a/examples/ant/midlets.xml b/examples/ant/midlets.xml
index 5b8643c..b17d036 100644
--- a/examples/ant/midlets.xml
+++ b/examples/ant/midlets.xml
@@ -7,8 +7,9 @@
   <taskdef resource="proguard/ant/task.properties"
            classpath="lib/proguard.jar" />
 
-  <proguard overloadaggressively="on"
-            defaultpackage=""
+  <proguard microedition="on"
+            overloadaggressively="on"
+            repackageclasses=""
             allowaccessmodification="on"
             printseeds="on">
 
diff --git a/examples/ant/proguard.xml b/examples/ant/proguard.xml
index 6ec0ca9..c9ecedc 100644
--- a/examples/ant/proguard.xml
+++ b/examples/ant/proguard.xml
@@ -11,7 +11,7 @@
 
   <proguard printmapping="proguard.map"
             overloadaggressively="on"
-            defaultpackage=""
+            repackageclasses=""
             allowaccessmodification="on">
 
     <!-- Specify the input jars, output jars, and library jars. -->
@@ -20,7 +20,7 @@
     <outjar file="examples/ant/proguard_out.jar" />
 
     <libraryjar file="${java.home}/lib/rt.jar" />
-    <libraryjar file="/usr/local/java/ant1.6.2/lib/ant.jar" />
+    <libraryjar file="/usr/local/java/ant1.6.5/lib/ant.jar" />
     <libraryjar file="/usr/local/java/wtk2.1/wtklib/kenv.zip" />
 
     <!-- The main seeds: ProGuard and its companion tool ReTrace. -->
@@ -47,9 +47,8 @@
     <!-- If we have ant.jar, we can properly process the Ant task. -->
 
     <keep access="public" name="proguard.ant.*">
-      <method access="public" type="void" name="set*" parameters="%"  />
-      <method access="public" type="void" name="set*" parameters="**" />
-      <method access="public" type="void" name="add*" parameters="**" />
+      <method access="public" type="void" name="set*" parameters="***" />
+      <method access="public" type="void" name="add*" parameters="***" />
     </keep>
 
     <!-- If we have kenv.zip, we can process the J2ME WTK plugin. -->
diff --git a/examples/ant/servlets.xml b/examples/ant/servlets.xml
index 69efebc..83af0f2 100644
--- a/examples/ant/servlets.xml
+++ b/examples/ant/servlets.xml
@@ -50,9 +50,12 @@
          compatible, please refer to the manual. -->
 
     <keepclassmembers implements="java.io.Serializable">
-      <field  access    ="final"
+      <field  access    ="static final"
               type      ="long"
               name      ="serialVersionUID" />
+      <field  access    ="static final"
+              type      ="java.io.ObjectStreamField[]"
+              name      ="serialPersistentFields" />
       <method access    ="private"
               type      ="void"
               name      ="writeObject"
@@ -60,7 +63,7 @@
       <method access    ="private"
               type      ="void"
               name      ="readObject"
-              parameters="java.io.ObjectOutputStream" />
+              parameters="java.io.ObjectInputStream" />
       <method type      ="java.lang.Object"
               name      ="writeReplace"
               parameters="" />
diff --git a/examples/applets.pro b/examples/applets.pro
index 10dc8aa..ee90db0 100644
--- a/examples/applets.pro
+++ b/examples/applets.pro
@@ -45,6 +45,7 @@
 
 -keepclassmembers class * implements java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
diff --git a/examples/applications.pro b/examples/applications.pro
index 9b19761..29d8e43 100644
--- a/examples/applications.pro
+++ b/examples/applications.pro
@@ -51,6 +51,7 @@
 
 -keepclassmembers class * implements java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
diff --git a/examples/dictionaries/compact.txt b/examples/dictionaries/compact.txt
index c97bdd3..5636a3e 100644
--- a/examples/dictionaries/compact.txt
+++ b/examples/dictionaries/compact.txt
@@ -7,7 +7,6 @@
 #
 
 Code
-Exceptions
 V
 I
 Z
diff --git a/examples/dictionaries/shakespeare.txt b/examples/dictionaries/shakespeare.txt
old mode 100755
new mode 100644
diff --git a/examples/library.pro b/examples/library.pro
index adf6be3..37be47e 100644
--- a/examples/library.pro
+++ b/examples/library.pro
@@ -20,7 +20,8 @@
 
 -printmapping out.map
 -renamesourcefileattribute SourceFile
--keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
+                SourceFile,LineNumberTable,EnclosingMethod
 
 # Preserve all annotations.
 
@@ -62,6 +63,7 @@
 
 -keepclassmembers class * implements java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
diff --git a/examples/midlets.pro b/examples/midlets.pro
index 5ffb85b..9026760 100644
--- a/examples/midlets.pro
+++ b/examples/midlets.pro
@@ -13,6 +13,10 @@
 -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar
 -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar
 
+# Preverify the code suitably for Java Micro Edition.
+
+-microedition
+
 # Allow methods with the same signature, except for the return type,
 # to get the same obfuscation name.
 
@@ -20,7 +24,7 @@
 
 # Put all obfuscated classes into the nameless root package.
 
--defaultpackage ''
+-repackageclasses ''
 
 # Allow classes and class members to be made public.
 
diff --git a/examples/proguard.pro b/examples/proguard.pro
index 925e669..a44fae9 100644
--- a/examples/proguard.pro
+++ b/examples/proguard.pro
@@ -25,7 +25,7 @@
 
 # Put all obfuscated classes into the nameless root package.
 
--defaultpackage ''
+-repackageclasses ''
 
 # Allow classes and class members to be made public.
 
@@ -40,11 +40,10 @@
 # If you want to preserve the Ant task as well, you'll have to specify the
 # main ant.jar.
 
-#-libraryjars /usr/local/java/ant1.6.2/lib/ant.jar
+#-libraryjars /usr/local/java/ant1.6.5/lib/ant.jar
 #-keep public class proguard.ant.* {
-#    public void set*(%);
-#    public void set*(**);
-#    public void add*(**);
+#    public void set*(***);
+#    public void add*(***);
 #}
 
 
diff --git a/examples/proguardall.pro b/examples/proguardall.pro
index 346edb9..8a24d25 100644
--- a/examples/proguardall.pro
+++ b/examples/proguardall.pro
@@ -17,7 +17,7 @@
 # You may have to adapt the paths below.
 
 -libraryjars <java.home>/lib/rt.jar
--libraryjars /usr/local/java/ant1.6.2/lib/ant.jar
+-libraryjars /usr/local/java/ant1.6.5/lib/ant.jar
 -libraryjars /usr/local/java/wtk2.1/wtklib/kenv.zip
 
 # Allow methods with the same signature, except for the return type,
@@ -27,11 +27,7 @@
 
 # Put all obfuscated classes into the nameless root package.
 
--defaultpackage ''
-
-# Allow classes and class members to be made public.
-
--allowaccessmodification
+-repackageclasses ''
 
 # The main entry points.
 
@@ -50,18 +46,10 @@
 # If we have ant.jar, we can properly process the Ant task.
 
 -keep public class proguard.ant.* {
-    public void set*(%);
-    public void set*(**);
-    public void add*(**);
+    public void set*(***);
+    public void add*(***);
 }
 
 # If we have kenv.zip, we can process the J2ME WTK plugin.
 
 -keep public class proguard.wtk.ProGuardObfuscator
-
-# In addition, the following classes load resource files, based on their class
-# names or package names, so we don't want them to be obfuscated or moved to 
-# the default package.
-
--keep class proguard.gui.GUIResources
--keep class proguard.gui.ClassPathPanel$MyListCellRenderer
diff --git a/examples/proguardgui.pro b/examples/proguardgui.pro
index dfbf66a..aa9b9c7 100644
--- a/examples/proguardgui.pro
+++ b/examples/proguardgui.pro
@@ -30,21 +30,10 @@
 
 # Put all obfuscated classes into the nameless root package.
 
--defaultpackage ''
-
-# Allow classes and class members to be made public.
-
--allowaccessmodification
+-repackageclasses ''
 
 # The entry point: ProGuardGUI and its main method.
 
 -keep public class proguard.gui.ProGuardGUI {
     public static void main(java.lang.String[]);
 }
-
-# In addition, the following classes load resource files, based on their class
-# names or package names, so we don't want them to be obfuscated or moved to
-# the default package.
-
--keep class proguard.gui.GUIResources
--keep class proguard.gui.ClassPathPanel$MyListCellRenderer
diff --git a/examples/retrace.pro b/examples/retrace.pro
index 8942756..39f5a95 100644
--- a/examples/retrace.pro
+++ b/examples/retrace.pro
@@ -30,7 +30,7 @@
 
 # Put all obfuscated classes into the nameless root package.
 
--defaultpackage ''
+-repackageclasses ''
 
 # Allow classes and class members to be made public.
 
diff --git a/examples/servlets.pro b/examples/servlets.pro
index 71a7c6a..fdc36b0 100644
--- a/examples/servlets.pro
+++ b/examples/servlets.pro
@@ -46,6 +46,7 @@
 
 -keepclassmembers class * implements java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
diff --git a/lib/proguard.jar b/lib/proguard.jar
deleted file mode 100644
index 3066514..0000000
Binary files a/lib/proguard.jar and /dev/null differ
diff --git a/lib/proguardgui.jar b/lib/proguardgui.jar
deleted file mode 100644
index 23d4b22..0000000
Binary files a/lib/proguardgui.jar and /dev/null differ
diff --git a/lib/retrace.jar b/lib/retrace.jar
deleted file mode 100644
index 3ecc097..0000000
Binary files a/lib/retrace.jar and /dev/null differ
diff --git a/src/proguard/ArgumentWordReader.java b/src/proguard/ArgumentWordReader.java
index 6e3d734..512c600 100644
--- a/src/proguard/ArgumentWordReader.java
+++ b/src/proguard/ArgumentWordReader.java
@@ -1,6 +1,6 @@
-/* $Id: ArgumentWordReader.java,v 1.14.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import java.io.*;
  */
 public class ArgumentWordReader extends WordReader
 {
-    private String[] arguments;
+    private final String[] arguments;
     private int      index = 0;
 
 
diff --git a/src/proguard/ClassPath.java b/src/proguard/ClassPath.java
index 2efcb2b..8cb27c8 100644
--- a/src/proguard/ClassPath.java
+++ b/src/proguard/ClassPath.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPath.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -30,7 +30,7 @@ import java.util.*;
  */
 public class ClassPath
 {
-    private List classPathEntries = new ArrayList();
+    private final List classPathEntries = new ArrayList();
 
 
     /**
diff --git a/src/proguard/ClassPathEntry.java b/src/proguard/ClassPathEntry.java
index 3c8c7d8..ec1f9ce 100644
--- a/src/proguard/ClassPathEntry.java
+++ b/src/proguard/ClassPathEntry.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPathEntry.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,7 +27,7 @@ import java.io.*;
  * This class represents an entry from a class path: a jar, a war, a zip, an
  * ear, or a directory, with a name and a flag to indicates whether the entry is
  * an input entry or an output entry. Optional filters can be specified for the
- * names of the contained resource/class files, jars, wars, ears, and zips.
+ * names of the contained resource/classes, jars, wars, ears, and zips.
  *
  * @author Eric Lafortune
  */
diff --git a/src/proguard/ClassSpecification.java b/src/proguard/ClassSpecification.java
index b1a8338..b6d0cf7 100644
--- a/src/proguard/ClassSpecification.java
+++ b/src/proguard/ClassSpecification.java
@@ -1,6 +1,6 @@
-/* $Id: ClassSpecification.java,v 1.4.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,7 +22,6 @@ package proguard;
 
 import java.util.*;
 
-
 /**
  * This class stores a specification of classes and possibly class members.
  * The specification is template-based: the class names and class member names
@@ -33,113 +32,146 @@ import java.util.*;
  */
 public class ClassSpecification implements Cloneable
 {
-    public int     requiredSetAccessFlags;
-    public int     requiredUnsetAccessFlags;
-    public String  className;
-    public String  extendsClassName;
-    public boolean markClassFiles;
-    public boolean markConditionally;
-    public String  comments;
+    public final String comments;
+    public int    requiredSetAccessFlags;
+    public int    requiredUnsetAccessFlags;
+    public final String annotationType;
+    public String className;
+    public final String extendsAnnotationType;
+    public final String extendsClassName;
 
-    public List    fieldSpecifications;
-    public List    methodSpecifications;
+    public List   fieldSpecifications;
+    public List   methodSpecifications;
 
 
     /**
-     * Creates a new option to keep all possible class(es).
-     * The option doesn't have comments.
+     * Creates a new ClassSpecification for all possible classes, without
+     * comments or class members.
      */
     public ClassSpecification()
     {
-        this(0,
+        this(null,
              0,
+             0,
+             null,
              null,
              null,
-             true,
-             false);
+             null);
+    }
+
+
+    /**
+     * Creates a new ClassSpecification that is a copy of the given specification.
+     */
+    public ClassSpecification(ClassSpecification classSpecification)
+    {
+        this(classSpecification.comments,
+             classSpecification.requiredSetAccessFlags,
+             classSpecification.requiredUnsetAccessFlags,
+             classSpecification.annotationType,
+             classSpecification.className,
+             classSpecification.extendsAnnotationType,
+             classSpecification.extendsClassName,
+             classSpecification.fieldSpecifications,
+             classSpecification.methodSpecifications);
     }
 
 
     /**
-     * Creates a new option to keep the specified class(es).
-     * The option doesn't have comments.
+     * Creates a new ClassSpecification for the specified class(es), without
+     * class members.
      *
+     * @param comments                 provides optional comments on this
+     *                                 specification.
      * @param requiredSetAccessFlags   the class access flags that must be set
      *                                 in order for the class to apply.
-     * @param requiredUnsetAccessFlags the class access flags that must be unset
-     *                                 in order for the class to apply.
+     * @param requiredUnsetAccessFlags the class access flags that must be
+     *                                 unset in order for the class to apply.
+     * @param annotationType           the name of the class that must be an
+     *                                 annotation of the class in order for it
+     *                                 to apply. The name may be null to
+     *                                 specify that no annotation is required.
      * @param className                the class name. The name may be null to
      *                                 specify any class, or it may contain
      *                                 "**", "*", or "?" wildcards.
-     * @param extendsClassName         the name of the class that the class must
-     *                                 extend or implement in order to apply.
-     *                                 The name may be null to specify any class.
-     * @param markClassFiles           specifies whether to mark the class files.
-     *                                 If false, only class members are marked.
-     *                                 If true, the class files are marked as
-     *                                 well.
-     * @param markConditionally        specifies whether to mark the class files
-     *                                 and class members conditionally.
-     *                                 If true, class files and class members
-     *                                 are marked, on the condition that all
-     *                                 specified class members are present.
+     * @param extendsAnnotationType    the name of the class of that must be
+     *                                 an annotation of the class that the
+     *                                 class must extend or implement in order
+     *                                 to apply. The name may be null to
+     *                                 specify that no annotation is required.
+     * @param extendsClassName         the name of the class that the class
+     *                                 must extend or implement in order to
+     *                                 apply. The name may be null to specify
+     *                                 any class.
      */
-    public ClassSpecification(int     requiredSetAccessFlags,
-                              int     requiredUnsetAccessFlags,
-                              String  className,
-                              String  extendsClassName,
-                              boolean markClassFiles,
-                              boolean markConditionally)
+    public ClassSpecification(String comments,
+                              int    requiredSetAccessFlags,
+                              int    requiredUnsetAccessFlags,
+                              String annotationType,
+                              String className,
+                              String extendsAnnotationType,
+                              String extendsClassName)
     {
-        this(requiredSetAccessFlags,
+        this(comments,
+             requiredSetAccessFlags,
              requiredUnsetAccessFlags,
+             annotationType,
              className,
+             extendsAnnotationType,
              extendsClassName,
-             markClassFiles,
-             markConditionally,
+             null,
              null);
     }
 
 
     /**
-     * Creates a new option to keep the specified class(es).
+     * Creates a new ClassSpecification for the specified classes and class
+     * members.
      *
+     * @param comments                 provides optional comments on this
+     *                                 specification.
      * @param requiredSetAccessFlags   the class access flags that must be set
      *                                 in order for the class to apply.
-     * @param requiredUnsetAccessFlags the class access flags that must be unset
-     *                                 in order for the class to apply.
+     * @param requiredUnsetAccessFlags the class access flags that must be
+     *                                 unset in order for the class to apply.
+     * @param annotationType           the name of the class that must be an
+     *                                 annotation of the class in order for it
+     *                                 to apply. The name may be null to
+     *                                 specify that no annotation is required.
      * @param className                the class name. The name may be null to
      *                                 specify any class, or it may contain
      *                                 "**", "*", or "?" wildcards.
-     * @param extendsClassName         the name of the class that the class must
-     *                                 extend or implement in order to apply.
-     *                                 The name may be null to specify any class.
-     * @param markClassFiles           specifies whether to mark the class files.
-     *                                 If false, only class members are marked.
-     *                                 If true, the class files are marked as
-     *                                 well.
-     * @param markConditionally        specifies whether to mark the class files
-     *                                 and class members conditionally.
-     *                                 If true, class files and class members
-     *                                 are marked, on the condition that all
-     *                                 specified class members are present.
-     * @param comments                 provides optional comments on this option.
+     * @param extendsAnnotationType    the name of the class of that must be
+     *                                 an annotation of the class that the
+     *                                 class must extend or implement in order
+     *                                 to apply. The name may be null to
+     *                                 specify that no annotation is required.
+     * @param extendsClassName         the name of the class that the class
+     *                                 must extend or implement in order to
+     *                                 apply. The name may be null to specify
+     *                                 any class.
+     * @param fieldSpecifications      the field specifications.
+     * @param methodSpecifications     the method specifications.
      */
-    public ClassSpecification(int     requiredSetAccessFlags,
-                              int     requiredUnsetAccessFlags,
-                              String  className,
-                              String  extendsClassName,
-                              boolean markClassFiles,
-                              boolean markConditionally,
-                              String  comments)
+    public ClassSpecification(String comments,
+                              int    requiredSetAccessFlags,
+                              int    requiredUnsetAccessFlags,
+                              String annotationType,
+                              String className,
+                              String extendsAnnotationType,
+                              String extendsClassName,
+                              List   fieldSpecifications,
+                              List   methodSpecifications)
     {
+        this.comments                 = comments;
         this.requiredSetAccessFlags   = requiredSetAccessFlags;
         this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
+        this.annotationType           = annotationType;
         this.className                = className;
+        this.extendsAnnotationType    = extendsAnnotationType;
         this.extendsClassName         = extendsClassName;
-        this.markClassFiles           = markClassFiles;
-        this.markConditionally        = markConditionally;
-        this.comments                 = comments;
+        this.fieldSpecifications      = fieldSpecifications;
+        this.methodSpecifications     = methodSpecifications;
     }
 
 
@@ -148,7 +180,7 @@ public class ClassSpecification implements Cloneable
      *
      * @param fieldSpecification the field specification.
      */
-    public void addField(ClassMemberSpecification fieldSpecification)
+    public void addField(MemberSpecification fieldSpecification)
     {
         if (fieldSpecifications == null)
         {
@@ -164,7 +196,7 @@ public class ClassSpecification implements Cloneable
      *
      * @param methodSpecification the method specification.
      */
-    public void addMethod(ClassMemberSpecification methodSpecification)
+    public void addMethod(MemberSpecification methodSpecification)
     {
         if (methodSpecifications == null)
         {
@@ -180,34 +212,37 @@ public class ClassSpecification implements Cloneable
 
     public boolean equals(Object object)
     {
-        if (this.getClass() != object.getClass())
+        if (object == null ||
+            this.getClass() != object.getClass())
         {
             return false;
         }
 
         ClassSpecification other = (ClassSpecification)object;
         return
-            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags  ) &&
-            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags) &&
-            (this.markClassFiles           == other.markClassFiles          ) &&
-            (this.markConditionally        == other.markConditionally       ) &&
-            (this.className            == null ? other.className            == null : this.className.equals(other.className))                     &&
-            (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.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)            );
     }
 
     public int hashCode()
     {
         return
-            requiredSetAccessFlags                                               ^
-            requiredUnsetAccessFlags                                             ^
-            (className            == null ? 0 : className.hashCode()           ) ^
-            (extendsClassName     == null ? 0 : extendsClassName.hashCode()    ) ^
-            (markClassFiles               ? 0 : 1                              ) ^
-            (markConditionally            ? 0 : 2                              ) ^
-            (fieldSpecifications  == null ? 0 : fieldSpecifications.hashCode() ) ^
-            (methodSpecifications == null ? 0 : methodSpecifications.hashCode());
+//          (comments              == null ? 0 : comments.hashCode()             ) ^
+            (requiredSetAccessFlags                                              ) ^
+            (requiredUnsetAccessFlags                                            ) ^
+            (annotationType        == null ? 0 : annotationType.hashCode()       ) ^
+            (className             == null ? 0 : className.hashCode()            ) ^
+            (extendsAnnotationType == null ? 0 : extendsAnnotationType.hashCode()) ^
+            (extendsClassName      == null ? 0 : extendsClassName.hashCode()     ) ^
+            (fieldSpecifications   == null ? 0 : fieldSpecifications.hashCode()  ) ^
+            (methodSpecifications  == null ? 0 : methodSpecifications.hashCode() );
     }
 
     public Object clone()
diff --git a/src/proguard/ClassSpecificationVisitorFactory.java b/src/proguard/ClassSpecificationVisitorFactory.java
index f9d6a9d..17148d1 100644
--- a/src/proguard/ClassSpecificationVisitorFactory.java
+++ b/src/proguard/ClassSpecificationVisitorFactory.java
@@ -1,6 +1,6 @@
-/* $Id: ClassSpecificationVisitorFactory.java,v 1.7.2.1 2006/05/06 13:19:00 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,10 +20,11 @@
  */
 package proguard;
 
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.AllAttributeVisitor;
 import proguard.classfile.visitor.*;
 
-import java.util.*;
-
+import java.util.List;
 
 /**
  * This factory creates visitors to efficiently travel to specified classes and
@@ -34,28 +35,79 @@ import java.util.*;
 public class ClassSpecificationVisitorFactory
 {
     /**
-     * Creates a new ClassPoolVisitor to efficiently travel to the specified
+     * Constructs a ClassPoolVisitor to efficiently travel to the specified
      * classes and class members.
      *
-     * @param classSpecifications the specifications of the classes and class
-     *                            members to visit.
-     * @param classFileVisitor    the ClassFileVisitor to be applied to matching
+     * @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.
+     */
+    public static ClassPoolVisitor createClassPoolVisitor(List          keepSpecifications,
+                                                          ClassVisitor  classVisitor,
+                                                          MemberVisitor memberVisitor,
+                                                          boolean       shrinking,
+                                                          boolean       optimizing,
+                                                          boolean       obfuscating)
+    {
+        MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
+
+        if (keepSpecifications != null)
+        {
+            for (int index = 0; index < keepSpecifications.size(); index++)
+            {
+                KeepSpecification keepSpecification =
+                    (KeepSpecification)keepSpecifications.get(index);
+
+                if ((shrinking   && !keepSpecification.allowShrinking)    ||
+                    (optimizing  && !keepSpecification.allowOptimization) ||
+                    (obfuscating && !keepSpecification.allowObfuscation))
+                {
+                    multiClassPoolVisitor.addClassPoolVisitor(
+                        createClassPoolVisitor(keepSpecification,
+                                               classVisitor,
+                                               memberVisitor));
+                }
+            }
+        }
+
+        return multiClassPoolVisitor;
+    }
+
+
+    /**
+     * Constructs a ClassPoolVisitor to efficiently travel to the specified
+     * classes and class members.
+     *
+     * @param classSpecifications the list of ClassSpecification instances,
+     *                            defining of the classes and class members to
+     *                            visit.
+     * @param classVisitor        the ClassVisitor to be applied to matching
      *                            classes.
-     * @param memberInfoVisitor   the MemberInfoVisitor to be applied to matching
+     * @param memberVisitor       the MemberVisitor to be applied to matching
      *                            class members.
      */
-    public static ClassPoolVisitor createClassPoolVisitor(List              classSpecifications,
-                                                          ClassFileVisitor  classFileVisitor,
-                                                          MemberInfoVisitor memberInfoVisitor)
+    public static ClassPoolVisitor createClassPoolVisitor(List          classSpecifications,
+                                                          ClassVisitor  classVisitor,
+                                                          MemberVisitor memberVisitor)
     {
         MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
 
         if (classSpecifications != null)
         {
-            addClassPoolVisitors(classSpecifications,
-                                 classFileVisitor,
-                                 memberInfoVisitor,
-                                 multiClassPoolVisitor);
+            for (int index = 0; index < classSpecifications.size(); index++)
+            {
+                ClassSpecification classSpecification =
+                    (ClassSpecification)classSpecifications.get(index);
+
+                multiClassPoolVisitor.addClassPoolVisitor(
+                    createClassPoolVisitor(classSpecification,
+                                           classVisitor,
+                                           memberVisitor));
+            }
         }
 
         return multiClassPoolVisitor;
@@ -63,244 +115,323 @@ public class ClassSpecificationVisitorFactory
 
 
     /**
-     * Adds new ClassPoolVisitor instances to the given MultiClassPoolVisitor,
-     * to efficiently travel to the specified classes and class members.
-
-     * @param classSpecifications   the specifications of the classes and class
-     *                              members to visit.
-     * @param classFileVisitor      the ClassFileVisitor to be applied to matching
-     *                              classes.
-     * @param memberInfoVisitor     the MemberInfoVisitor to be applied to matching
-     *                              class members.
-     * @param multiClassPoolVisitor the MultiClassPoolVisitor to which the new
-     *                              visitors will be added.
+     * Constructs a ClassPoolVisitor to efficiently travel to the specified
+     * classes and class members.
+     *
+     * @param keepSpecification 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 void addClassPoolVisitors(List                  classSpecifications,
-                                             ClassFileVisitor      classFileVisitor,
-                                             MemberInfoVisitor     memberInfoVisitor,
-                                             MultiClassPoolVisitor multiClassPoolVisitor)
+    private static ClassPoolVisitor createClassPoolVisitor(KeepSpecification keepSpecification,
+                                                           ClassVisitor      classVisitor,
+                                                           MemberVisitor     memberVisitor)
     {
-        for (int index = 0; index < classSpecifications.size(); index++)
+        // Don't  visit the classes if not specified.
+        if (!keepSpecification.markClasses &&
+            !keepSpecification.markConditionally)
         {
-            multiClassPoolVisitor.addClassPoolVisitor(
-                createClassPoolVisitor((ClassSpecification)classSpecifications.get(index),
-                                       classFileVisitor,
-                                       memberInfoVisitor));
+            classVisitor = null;
         }
+
+        // If specified, let the marker visit the class and its class
+        // members conditionally.
+        if (keepSpecification.markConditionally)
+        {
+            // Combine both visitors.
+            ClassVisitor composedClassVisitor =
+                createCombinedClassVisitor(keepSpecification,
+                                           classVisitor,
+                                           memberVisitor);
+
+            // Replace the class visitor.
+            classVisitor =
+                createClassMemberTester(keepSpecification,
+                                        composedClassVisitor);
+
+            // Discard the member visitor, because it has already been included.
+            memberVisitor = null;
+        }
+
+        return createClassPoolVisitor((ClassSpecification)keepSpecification,
+                                      classVisitor,
+                                      memberVisitor);
     }
 
 
     /**
-     * Creates a new ClassPoolVisitor to efficiently travel to the specified
+     * Constructs a ClassPoolVisitor to efficiently travel to the specified
      * classes and class members.
      *
      * @param classSpecification the specifications of the class(es) and class
      *                           members to visit.
-     * @param classFileVisitor   the ClassFileVisitor to be applied to matching
+     * @param classVisitor       the ClassVisitor to be applied to matching
      *                           classes.
-     * @param memberInfoVisitor  the MemberInfoVisitor to be applied to matching
+     * @param memberVisitor      the MemberVisitor to be applied to matching
      *                           class members.
      */
     private static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification,
-                                                           ClassFileVisitor   classFileVisitor,
-                                                           MemberInfoVisitor  memberInfoVisitor)
+                                                           ClassVisitor       classVisitor,
+                                                           MemberVisitor      memberVisitor)
     {
-        // The class file visitor for class files and their members.
-        MultiClassFileVisitor multiClassFileVisitor = new MultiClassFileVisitor();
-
-        // If specified, let the class file visitor visit the class file itself.
-        if ((classSpecification.markClassFiles ||
-             classSpecification.markConditionally) &&
-            classFileVisitor != null)
-        {
-            multiClassFileVisitor.addClassFileVisitor(classFileVisitor);
-        }
-
-        // If specified, let the member info visitor visit the class members.
-        if ((classSpecification.fieldSpecifications  != null ||
-             classSpecification.methodSpecifications != null) &&
-            memberInfoVisitor != null)
-        {
-            multiClassFileVisitor.addClassFileVisitor(
-                createClassFileVisitor(classSpecification, memberInfoVisitor));
-        }
-
-        // This visitor is the starting point.
-        ClassFileVisitor composedClassFileVisitor = multiClassFileVisitor;
-
-        // If specified, let the marker visit the class file and its class
-        // members conditionally.
-        if (classSpecification.markConditionally)
-        {
-            composedClassFileVisitor =
-                createClassFileMemberInfoTester(classSpecification,
-                                                composedClassFileVisitor);
-        }
+        // Combine both visitors.
+        ClassVisitor composedClassVisitor =
+            createCombinedClassVisitor(classSpecification,
+                                       classVisitor,
+                                       memberVisitor);
 
-        // By default, start visiting from the class name, if it's specified.
+        // By default, start visiting from the named class name, if specified.
         String className = classSpecification.className;
 
-        // If wildcarded, only visit class files with matching names.
+        // Although we may have to start from the extended class.
+        String extendsAnnotationType = classSpecification.extendsAnnotationType;
+        String extendsClassName      = classSpecification.extendsClassName;
+
+        // If wildcarded, only visit classes with matching names.
         if (className != null &&
-            containsWildCards(className))
+            (extendsAnnotationType != null ||
+             extendsClassName           != null ||
+             containsWildCards(className)))
         {
-            composedClassFileVisitor =
-                new ClassFileNameFilter(composedClassFileVisitor,
-                                        className);
+            composedClassVisitor =
+                new ClassNameFilter(className, composedClassVisitor);
 
             // We'll have to visit all classes now.
             className = null;
         }
 
-        // If specified, only visit class files with the right access flags.
+        // If specified, only visit classes with the right annotation.
+        String annotationType = classSpecification.annotationType;
+
+        if (annotationType != null)
+        {
+            composedClassVisitor =
+                new AllAttributeVisitor(
+                new AllAnnotationVisitor(
+                new AnnotationTypeFilter(annotationType,
+                new AnnotatedClassVisitor(composedClassVisitor))));
+        }
+
+        // If specified, only visit classes with the right access flags.
         if (classSpecification.requiredSetAccessFlags   != 0 ||
             classSpecification.requiredUnsetAccessFlags != 0)
         {
-            composedClassFileVisitor =
-                new ClassFileAccessFilter(classSpecification.requiredSetAccessFlags,
-                                          classSpecification.requiredUnsetAccessFlags,
-                                          composedClassFileVisitor);
+            composedClassVisitor =
+                new ClassAccessFilter(classSpecification.requiredSetAccessFlags,
+                                      classSpecification.requiredUnsetAccessFlags,
+                                      composedClassVisitor);
         }
 
         // If it's specified, start visiting from the extended class.
-        String extendsClassName = classSpecification.extendsClassName;
-
-        if (className        == null &&
-            extendsClassName != null)
+        if (extendsAnnotationType != null ||
+            extendsClassName      != null)
         {
-            composedClassFileVisitor =
-                new ClassFileHierarchyTraveler(false, false, false, true,
-                                               composedClassFileVisitor);
+            // Start visiting from the extended class.
+            composedClassVisitor =
+                new ClassHierarchyTraveler(false, false, false, true,
+                                           composedClassVisitor);
 
-            // If wildcarded, only visit class files with matching names.
-            if (containsWildCards(extendsClassName))
+            // If specified, only visit extended classes with the right annotation.
+            if (extendsAnnotationType != null)
             {
-                composedClassFileVisitor =
-                    new ClassFileNameFilter(composedClassFileVisitor,
-                                            extendsClassName);
+                composedClassVisitor =
+                    new AllAttributeVisitor(
+                    new AllAnnotationVisitor(
+                    new AnnotationTypeFilter(extendsAnnotationType,
+                    new AnnotatedClassVisitor(composedClassVisitor))));
             }
-            else
+
+            // If specified, only visit extended classes with matching names.
+            if (extendsClassName != null)
             {
-                // Start visiting from the extended class name.
-                className = extendsClassName;
+                // If wildcarded, only visit extended classes with matching names.
+                if (containsWildCards(extendsClassName))
+                {
+                    composedClassVisitor =
+                        new ClassNameFilter(extendsClassName,
+                                            composedClassVisitor);
+                }
+                else
+                {
+                    // Start visiting from the named extended class.
+                    className = extendsClassName;
+                }
             }
         }
 
         // If specified, visit a single named class, otherwise visit all classes.
         return className != null ?
-            (ClassPoolVisitor)new NamedClassFileVisitor(composedClassFileVisitor, className) :
-            (ClassPoolVisitor)new AllClassFileVisitor(composedClassFileVisitor);
+            (ClassPoolVisitor)new NamedClassVisitor(composedClassVisitor, className) :
+            (ClassPoolVisitor)new AllClassVisitor(composedClassVisitor);
+    }
+
+
+    /**
+     * Constructs a ClassVisitor to efficiently travel to the specified
+     * classes and class members.
+     *
+     * @param classSpecification 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 ClassVisitor createCombinedClassVisitor(ClassSpecification classSpecification,
+                                                           ClassVisitor       classVisitor,
+                                                           MemberVisitor      memberVisitor)
+    {
+        // Don't visit any members if there aren't any member specifications.
+        if (classSpecification.fieldSpecifications  == null &&
+            classSpecification.methodSpecifications == null)
+        {
+            memberVisitor = null;
+        }
+
+        // The class visitor for classes and their members.
+        MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
+
+        // If specified, let the class visitor visit the class itself.
+        if (classVisitor != null)
+        {
+            // This class visitor may be the only one.
+            if (memberVisitor == null)
+            {
+                return classVisitor;
+            }
+
+            multiClassVisitor.addClassVisitor(classVisitor);
+        }
+
+        // If specified, let the member info visitor visit the class members.
+        if (memberVisitor != null)
+        {
+            ClassVisitor memberClassVisitor =
+                createClassVisitor(classSpecification, memberVisitor);
+
+            // This class visitor may be the only one.
+            if (classVisitor == null)
+            {
+                return memberClassVisitor;
+            }
+
+            multiClassVisitor.addClassVisitor(memberClassVisitor);
+        }
+
+        return multiClassVisitor;
     }
 
 
     /**
-     * Creates a new ClassPoolVisitor to efficiently travel to the specified class
+     * Constructs a ClassVisitor to efficiently travel to the specified class
      * members.
      *
      * @param classSpecification the specifications of the class members to visit.
-     * @param memberInfoVisitor   the MemberInfoVisitor to be applied to matching
-     *                            class members.
+     * @param memberVisitor      the MemberVisitor to be applied to matching
+     *                           class members.
      */
-    private static ClassFileVisitor createClassFileVisitor(ClassSpecification classSpecification,
-                                                           MemberInfoVisitor  memberInfoVisitor)
+    private static ClassVisitor createClassVisitor(ClassSpecification classSpecification,
+                                                   MemberVisitor      memberVisitor)
     {
-        MultiClassFileVisitor multiClassFileVisitor = new MultiClassFileVisitor();
+        MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
 
-        addMemberInfoVisitors(classSpecification.fieldSpecifications,  true,  multiClassFileVisitor, memberInfoVisitor);
-        addMemberInfoVisitors(classSpecification.methodSpecifications, false, multiClassFileVisitor, memberInfoVisitor);
+        addMemberVisitors(classSpecification.fieldSpecifications,  true,  multiClassVisitor, memberVisitor);
+        addMemberVisitors(classSpecification.methodSpecifications, false, multiClassVisitor, memberVisitor);
 
         // Mark the class member in this class and in super classes.
-        return new ClassFileHierarchyTraveler(true, true, false, false,
-                                              multiClassFileVisitor);
+        return new ClassHierarchyTraveler(true, true, false, false,
+                                          multiClassVisitor);
     }
 
 
     /**
-     * Adds elements to the given MultiClassFileVisitor, to apply the given
-     * MemberInfoVisitor to all class members that match the given List
+     * Adds elements to the given MultiClassVisitor, to apply the given
+     * MemberVisitor to all class members that match the given List
      * of options (of the given type).
      */
-    private static void addMemberInfoVisitors(List                  classMemberSpecifications,
-                                              boolean               isField,
-                                              MultiClassFileVisitor multiClassFileVisitor,
-                                              MemberInfoVisitor     memberInfoVisitor)
+    private static void addMemberVisitors(List              memberSpecifications,
+                                          boolean           isField,
+                                          MultiClassVisitor multiClassVisitor,
+                                          MemberVisitor     memberVisitor)
     {
-        if (classMemberSpecifications != null)
+        if (memberSpecifications != null)
         {
-            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            for (int index = 0; index < memberSpecifications.size(); index++)
             {
-                ClassMemberSpecification classMemberSpecification =
-                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+                MemberSpecification memberSpecification =
+                    (MemberSpecification)memberSpecifications.get(index);
 
-                multiClassFileVisitor.addClassFileVisitor(
-                    createClassFileVisitor(classMemberSpecification,
-                                           isField,
-                                           memberInfoVisitor));
+                multiClassVisitor.addClassVisitor(
+                    createClassVisitor(memberSpecification,
+                                       isField,
+                                       memberVisitor));
             }
         }
     }
 
 
     /**
-     * Constructs a ClassFileVisitor that conditionally applies the given
-     * ClassFileVisitor to all classes that contain the given class members.
+     * Constructs a ClassVisitor that conditionally applies the given
+     * ClassVisitor to all classes that contain the given class members.
      */
-    private static ClassFileVisitor createClassFileMemberInfoTester(ClassSpecification classSpecification,
-                                                                    ClassFileVisitor   classFileVisitor)
+    private static ClassVisitor createClassMemberTester(ClassSpecification classSpecification,
+                                                        ClassVisitor       classVisitor)
     {
         // Create a linked list of conditional visitors, for fields and for
         // methods.
-        return createClassFileMemberInfoTester(classSpecification.fieldSpecifications,
-                                               true,
-               createClassFileMemberInfoTester(classSpecification.methodSpecifications,
-                                               false,
-                                               classFileVisitor));
+        return createClassMemberTester(classSpecification.fieldSpecifications,
+                                       true,
+               createClassMemberTester(classSpecification.methodSpecifications,
+                                       false,
+                                       classVisitor));
     }
 
 
     /**
-     * Constructs a ClassFileVisitor that conditionally applies the given
-     * ClassFileVisitor to all classes that contain the given List of class
+     * Constructs a ClassVisitor that conditionally applies the given
+     * ClassVisitor to all classes that contain the given List of class
      * members (of the given type).
      */
-    private static ClassFileVisitor createClassFileMemberInfoTester(List             classMemberSpecifications,
-                                                                    boolean          isField,
-                                                                    ClassFileVisitor classFileVisitor)
+    private static ClassVisitor createClassMemberTester(List         memberSpecifications,
+                                                        boolean      isField,
+                                                        ClassVisitor classVisitor)
     {
         // Create a linked list of conditional visitors.
-        if (classMemberSpecifications != null)
+        if (memberSpecifications != null)
         {
-            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            for (int index = 0; index < memberSpecifications.size(); index++)
             {
-                ClassMemberSpecification classMemberSpecification =
-                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+                MemberSpecification memberSpecification =
+                    (MemberSpecification)memberSpecifications.get(index);
 
-                classFileVisitor =
-                    createClassFileVisitor(classMemberSpecification,
-                                           isField,
-                                           new ClassFileMemberInfoVisitor(classFileVisitor));
+                classVisitor =
+                    createClassVisitor(memberSpecification,
+                                       isField,
+                                       new MemberToClassVisitor(classVisitor));
             }
         }
 
-        return classFileVisitor;
+        return classVisitor;
     }
 
 
     /**
-     * Creates a new ClassFileVisitor to efficiently travel to the specified class
+     * Creates a new ClassVisitor to efficiently travel to the specified class
      * members.
      *
-     * @param classMemberSpecification the specification of the class member(s)
-     *                                 to visit.
-     * @param memberInfoVisitor        the MemberInfoVisitor to be applied to
-     *                                 matching class member(s).
+     * @param memberSpecification the specification of the class member(s) to
+     *                            visit.
+     * @param memberVisitor       the MemberVisitor to be applied to matching
+     *                            class member(s).
      */
-    private static ClassFileVisitor createClassFileVisitor(ClassMemberSpecification classMemberSpecification,
-                                                           boolean                  isField,
-                                                           MemberInfoVisitor        memberInfoVisitor)
+    private static ClassVisitor createClassVisitor(MemberSpecification memberSpecification,
+                                                   boolean             isField,
+                                                   MemberVisitor       memberVisitor)
     {
-        String name       = classMemberSpecification.name;
-        String descriptor = classMemberSpecification.descriptor;
+        String name       = memberSpecification.name;
+        String descriptor = memberSpecification.descriptor;
 
         // If name or descriptor are not fully specified, only visit matching
         // class members.
@@ -314,36 +445,46 @@ public class ClassSpecificationVisitorFactory
         {
             if (descriptor != null)
             {
-                memberInfoVisitor =
-                    new MemberInfoDescriptorFilter(descriptor, memberInfoVisitor);
+                memberVisitor =
+                    new MemberDescriptorFilter(descriptor, memberVisitor);
             }
 
             if (name != null)
             {
-                memberInfoVisitor =
-                    new MemberInfoNameFilter(name, memberInfoVisitor);
+                memberVisitor =
+                    new MemberNameFilter(name, memberVisitor);
             }
         }
 
+        // If specified, only visit class members with the right annotation.
+        if (memberSpecification.annotationType != null)
+        {
+            memberVisitor =
+                new AllAttributeVisitor(
+                new AllAnnotationVisitor(
+                new AnnotationTypeFilter(memberSpecification.annotationType,
+                new AnnotationToMemberVisitor(memberVisitor))));
+        }
+
         // If any access flags are specified, only visit matching class members.
-        if (classMemberSpecification.requiredSetAccessFlags   != 0 ||
-            classMemberSpecification.requiredUnsetAccessFlags != 0)
+        if (memberSpecification.requiredSetAccessFlags   != 0 ||
+            memberSpecification.requiredUnsetAccessFlags != 0)
         {
-            memberInfoVisitor =
-                new MemberInfoAccessFilter(classMemberSpecification.requiredSetAccessFlags,
-                                           classMemberSpecification.requiredUnsetAccessFlags,
-                                           memberInfoVisitor);
+            memberVisitor =
+                new MemberAccessFilter(memberSpecification.requiredSetAccessFlags,
+                                       memberSpecification.requiredUnsetAccessFlags,
+                                       memberVisitor);
         }
 
         // Depending on what's specified, visit a single named class member,
         // or all class members, filtering the matching ones.
         return isField ?
             fullySpecified ?
-                (ClassFileVisitor)new NamedFieldVisitor(name, descriptor, memberInfoVisitor) :
-                (ClassFileVisitor)new AllFieldVisitor(memberInfoVisitor) :
+                (ClassVisitor)new NamedFieldVisitor(name, descriptor, memberVisitor) :
+                (ClassVisitor)new AllFieldVisitor(memberVisitor) :
             fullySpecified ?
-                (ClassFileVisitor)new NamedMethodVisitor(name, descriptor, memberInfoVisitor) :
-                (ClassFileVisitor)new AllMethodVisitor(memberInfoVisitor);
+                (ClassVisitor)new NamedMethodVisitor(name, descriptor, memberVisitor) :
+                (ClassVisitor)new AllMethodVisitor(memberVisitor);
     }
 
 
@@ -352,9 +493,10 @@ public class ClassSpecificationVisitorFactory
     private static boolean containsWildCards(String string)
     {
         return string != null &&
-            (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);
     }
 }
diff --git a/src/proguard/Configuration.java b/src/proguard/Configuration.java
index 36251e5..590eaaf 100644
--- a/src/proguard/Configuration.java
+++ b/src/proguard/Configuration.java
@@ -1,6 +1,6 @@
-/* $Id: Configuration.java,v 1.17.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,8 @@
  */
 package proguard;
 
-import java.util.*;
 import java.io.File;
-
+import java.util.List;
 
 /**
  * The ProGuard configuration.
@@ -51,7 +50,7 @@ public class Configuration
      * Specifies whether to skip non-public library classes while reading
      * library jars.
      */
-    public boolean   skipNonPublicLibraryClasses = true;
+    public boolean   skipNonPublicLibraryClasses      = true;
 
     /**
      * Specifies whether to skip non-public library class members while reading
@@ -59,24 +58,33 @@ public class Configuration
      */
     public boolean   skipNonPublicLibraryClassMembers = true;
 
+    /**
+     * Specifies the version number of the output classes, or 0 if the version
+     * number can be left unchanged.
+     */
+    public int       targetClassVersion;
+
+    /**
+     * Specifies the last modification time of this configuration. This time
+     * is necessary to check whether the input has to be processed. Setting it
+     * to Long.MAX_VALUE forces processing, even if the modification times
+     * of the output appear more recent than the modification times of the
+     * input.
+     */
+    public long      lastModified                     = 0L;
+
     ///////////////////////////////////////////////////////////////////////////
     // Keep options.
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * A list of {@link ClassSpecification} instances, whose class names and
-     * class member names are to be kept from shrinking, optimization, and
+     * A list of {@link KeepSpecification} instances, whose class names and
+     * class member names are to be kept from shrinking, optimization, and/or
      * obfuscation.
      */
     public List      keep;
 
     /**
-     * A list of {@link ClassSpecification} instances, whose class names and
-     * class member names are to be kept from obfuscation.
-     */
-    public List      keepNames;
-
-    /**
      * An optional output file for listing the kept seeds.
      * An empty file name means the standard output.
      */
@@ -89,7 +97,7 @@ public class Configuration
     /**
      * Specifies whether the code should be shrunk.
      */
-    public boolean   shrink                      = true;
+    public boolean   shrink                           = true;
 
     /**
      * An optional output file for listing the unused classes and class
@@ -110,7 +118,12 @@ public class Configuration
     /**
      * Specifies whether the code should be optimized.
      */
-    public boolean   optimize                    = true;
+    public boolean   optimize                         = true;
+
+    /**
+     * Specifies the number of optimization passes.
+     */
+    public int       optimizationPasses               = 1;
 
     /**
      * A list of {@link ClassSpecification} instances, whose methods are
@@ -121,7 +134,7 @@ public class Configuration
     /**
      * Specifies whether the access of class members can be modified.
      */
-    public boolean   allowAccessModification     = false;
+    public boolean   allowAccessModification          = false;
 
     ///////////////////////////////////////////////////////////////////////////
     // Obfuscation options.
@@ -130,7 +143,7 @@ public class Configuration
     /**
      * Specifies whether the code should be obfuscated.
      */
-    public boolean   obfuscate                   = true;
+    public boolean   obfuscate                        = true;
 
     /**
      * An optional output file for listing the obfuscation mapping.
@@ -151,23 +164,29 @@ public class Configuration
     /**
      * Specifies whether to apply aggressive name overloading on class members.
      */
-    public boolean   overloadAggressively        = false;
+    public boolean   overloadAggressively             = false;
 
     /**
      * Specifies whether to generate globally unique class member names.
      */
-    public boolean   useUniqueClassMemberNames   = false;
+    public boolean   useUniqueClassMemberNames        = false;
 
     /**
-     * An optional default package to which all classes whose name is obfuscated
-     * can be moved.
+     * Specifies whether obfuscated packages and classes can get mixed-case names.
      */
-    public String    defaultPackage;
+    public boolean   useMixedCaseClassNames           = true;
 
     /**
-     * Specifies whether to use mixed case class names.
+     * An optional base package if the obfuscated package hierarchy is to be
+     * flattened, <code>null</code> otherwise.
      */
-    public boolean   useMixedCaseClassNames      = true;
+    public String    flattenPackageHierarchy;
+
+    /**
+     * An optional base package if the obfuscated classes are to be repackaged
+     * into a single package, <code>null</code> otherwise.
+     */
+    public String    repackageClasses;
 
     /**
      * A list of <code>String</code>s specifying optional attributes to be kept.
@@ -182,6 +201,34 @@ public class Configuration
      */
     public String    newSourceFileAttribute;
 
+    /**
+     * A list of <code>String</code>s specifying a filter for files whose
+     * names are to be adapted, based on corresponding obfuscated class names.
+     */
+    public List      adaptResourceFileNames;
+
+    /**
+     * A list of <code>String</code>s specifying a filter for files whose
+     * contents are to be adapted, based on obfuscated class names.
+     */
+    public List      adaptResourceFileContents;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Preverification options.
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Specifies whether the code should be preverified.
+     */
+    public boolean   preverify                        = true;
+
+    /**
+     * Specifies whether the code should be preverified for Java Micro Edition
+     * (creating StackMap attributes) instead of for Java Standard Edition
+     * (creating StackMapTable attributes).
+     */
+    public boolean   microEdition                     = false;
+
     ///////////////////////////////////////////////////////////////////////////
     // General options.
     ///////////////////////////////////////////////////////////////////////////
@@ -189,22 +236,29 @@ public class Configuration
     /**
      * Specifies whether to print verbose messages.
      */
-    public boolean   verbose                     = false;
+    public boolean   verbose                          = false;
 
     /**
      * Specifies whether to print any notes.
      */
-    public boolean   note                        = true;
+    public boolean   note                             = true;
 
     /**
      * Specifies whether to print any warnings.
      */
-    public boolean   warn                        = true;
+    public boolean   warn                             = true;
 
     /**
      * Specifies whether to ignore any warnings.
      */
-    public boolean   ignoreWarnings              = false;
+    public boolean   ignoreWarnings                   = false;
+
+    /**
+     * An optional output file for printing out the configuration that ProGuard
+     * is using (with included files and replaced variables).
+     * An empty file name means the standard output.
+     */
+    public File      printConfiguration;
 
     /**
      * An optional output file for printing out the processed code in a more
diff --git a/src/proguard/ConfigurationConstants.java b/src/proguard/ConfigurationConstants.java
index bd14cfa..3cbed61 100644
--- a/src/proguard/ConfigurationConstants.java
+++ b/src/proguard/ConfigurationConstants.java
@@ -1,6 +1,6 @@
-/* $Id: ConfigurationConstants.java,v 1.12.2.1 2006/06/07 22:36:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,71 +27,88 @@ package proguard;
  */
 class ConfigurationConstants
 {
-    public static final String OPTION_PREFIX                                     = "-";
-    public static final String AT_DIRECTIVE                                      = "@";
-    public static final String INCLUDE_DIRECTIVE                                 = "-include";
-    public static final String BASE_DIRECTORY_DIRECTIVE                          = "-basedirectory";
+    public static final String OPTION_PREFIX            = "-";
+    public static final String AT_DIRECTIVE             = "@";
+    public static final String INCLUDE_DIRECTIVE        = "-include";
+    public static final String BASE_DIRECTORY_DIRECTIVE = "-basedirectory";
+
+    public static final String INJARS_OPTION       = "-injars";
+    public static final String OUTJARS_OPTION      = "-outjars";
+    public static final String LIBRARYJARS_OPTION  = "-libraryjars";
+    public static final String RESOURCEJARS_OPTION = "-resourcejars";
 
-    public static final String INJARS_OPTION                                     = "-injars";
-    public static final String OUTJARS_OPTION                                    = "-outjars";
-    public static final String LIBRARYJARS_OPTION                                = "-libraryjars";
-    public static final String RESOURCEJARS_OPTION                               = "-resourcejars";
+    public static final String KEEP_OPTION                           = "-keep";
+    public static final String KEEP_CLASS_MEMBERS_OPTION             = "-keepclassmembers";
+    public static final String KEEP_CLASSES_WITH_MEMBERS_OPTION      = "-keepclasseswithmembers";
+    public static final String KEEP_NAMES_OPTION                     = "-keepnames";
+    public static final String KEEP_CLASS_MEMBER_NAMES_OPTION        = "-keepclassmembernames";
+    public static final String KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION = "-keepclasseswithmembernames";
+    public static final String ALLOW_SHRINKING_SUBOPTION             = "allowshrinking";
+    public static final String ALLOW_OPTIMIZATION_SUBOPTION          = "allowoptimization";
+    public static final String ALLOW_OBFUSCATION_SUBOPTION           = "allowobfuscation";
+    public static final String PRINT_SEEDS_OPTION                    = "-printseeds";
 
-    public static final String KEEP_OPTION                                       = "-keep";
-    public static final String KEEP_CLASS_MEMBERS_OPTION                         = "-keepclassmembers";
-    public static final String KEEP_CLASSES_WITH_MEMBERS_OPTION                  = "-keepclasseswithmembers";
-    public static final String KEEP_NAMES_OPTION                                 = "-keepnames";
-    public static final String KEEP_CLASS_MEMBER_NAMES_OPTION                    = "-keepclassmembernames";
-    public static final String KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION             = "-keepclasseswithmembernames";
-    public static final String PRINT_SEEDS_OPTION                                = "-printseeds";
+    public static final String DONT_SHRINK_OPTION         = "-dontshrink";
+    public static final String PRINT_USAGE_OPTION         = "-printusage";
+    public static final String WHY_ARE_YOU_KEEPING_OPTION = "-whyareyoukeeping";
 
-    public static final String DONT_SHRINK_OPTION                                = "-dontshrink";
-    public static final String PRINT_USAGE_OPTION                                = "-printusage";
-    public static final String WHY_ARE_YOU_KEEPING_OPTION                        = "-whyareyoukeeping";
+    public static final String DONT_OPTIMIZE_OPTION             = "-dontoptimize";
+    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";
 
-    public static final String DONT_OPTIMIZE_OPTION                              = "-dontoptimize";
-    public static final String ASSUME_NO_SIDE_EFFECTS_OPTION                     = "-assumenosideeffects";
-    public static final String ALLOW_ACCESS_MODIFICATION_OPTION                  = "-allowaccessmodification";
+    public static final String DONT_OBFUSCATE_OPTION                  = "-dontobfuscate";
+    public static final String PRINT_MAPPING_OPTION                   = "-printmapping";
+    public static final String APPLY_MAPPING_OPTION                   = "-applymapping";
+    public static final String OBFUSCATION_DICTIONARY_OPTION          = "-obfuscationdictionary";
+    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 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_RESOURCE_FILE_NAMES_OPTION       = "-adaptresourcefilenames";
+    public static final String ADAPT_RESOURCE_FILE_CONTENTS_OPTION    = "-adaptresourcefilecontents";
 
-    public static final String DONT_OBFUSCATE_OPTION                             = "-dontobfuscate";
-    public static final String PRINT_MAPPING_OPTION                              = "-printmapping";
-    public static final String APPLY_MAPPING_OPTION                              = "-applymapping";
-    public static final String OBFUSCATION_DICTIONARY_OPTION                     = "-obfuscationdictionary";
-    public static final String OVERLOAD_AGGRESSIVELY_OPTION                      = "-overloadaggressively";
-    public static final String USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION              = "-useuniqueclassmembernames";
-    public static final String DEFAULT_PACKAGE_OPTION                            = "-defaultpackage";
-    public static final String DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION            = "-dontusemixedcaseclassnames";
-    public static final String KEEP_ATTRIBUTES_OPTION                            = "-keepattributes";
-    public static final String RENAME_SOURCE_FILE_ATTRIBUTE_OPTION               = "-renamesourcefileattribute";
+    public static final String DONT_PREVERIFY_OPTION = "-dontpreverify";
+    public static final String MICRO_EDITION_OPTION  = "-microedition";
 
     public static final String VERBOSE_OPTION                                    = "-verbose";
     public static final String DONT_NOTE_OPTION                                  = "-dontnote";
     public static final String DONT_WARN_OPTION                                  = "-dontwarn";
     public static final String IGNORE_WARNINGS_OPTION                            = "-ignorewarnings";
+    public static final String PRINT_CONFIGURATION_OPTION                        = "-printconfiguration";
     public static final String DUMP_OPTION                                       = "-dump";
     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 FORCE_PROCESSING_OPTION                           = "-forceprocessing";
 
     public static final String ANY_ATTRIBUTE_KEYWORD       = "*";
     public static final String ATTRIBUTE_SEPARATOR_KEYWORD = ",";
 
     public static final String JAR_SEPARATOR_KEYWORD   = System.getProperty("path.separator");
 
-    public static final char   OPEN_SYSTEM_PROPERTY    = '<';
-    public static final char   CLOSE_SYSTEM_PROPERTY   = '>';
+    public static final char OPEN_SYSTEM_PROPERTY  = '<';
+    public static final char CLOSE_SYSTEM_PROPERTY = '>';
 
-    public static final String NEGATOR_KEYWORD         = "!";
-    public static final String CLASS_KEYWORD           = "class";
-    public static final String ANY_CLASS_KEYWORD       = "*";
-    public static final String IMPLEMENTS_KEYWORD      = "implements";
-    public static final String EXTENDS_KEYWORD         = "extends";
-    public static final String OPEN_KEYWORD            = "{";
-    public static final String ANY_CLASS_MEMBER_KEYWORD  = "*";
-    public static final String ANY_FIELD_KEYWORD       = "<fields>";
-    public static final String ANY_METHOD_KEYWORD      = "<methods>";
-    public static final String OPEN_ARGUMENTS_KEYWORD  = "(";
+    public static final String ANNOTATION_KEYWORD         = "@";
+    public static final String NEGATOR_KEYWORD            = "!";
+    public static final String CLASS_KEYWORD              = "class";
+    public static final String ANY_CLASS_KEYWORD          = "*";
+    public static final String ANY_TYPE_KEYWORD           = "***";
+    public static final String IMPLEMENTS_KEYWORD         = "implements";
+    public static final String EXTENDS_KEYWORD            = "extends";
+    public static final String OPEN_KEYWORD               = "{";
+    public static final String ANY_CLASS_MEMBER_KEYWORD   = "*";
+    public static final String ANY_FIELD_KEYWORD          = "<fields>";
+    public static final String ANY_METHOD_KEYWORD         = "<methods>";
+    public static final String OPEN_ARGUMENTS_KEYWORD     = "(";
     public static final String ARGUMENT_SEPARATOR_KEYWORD = ",";
-    public static final String CLOSE_ARGUMENTS_KEYWORD = ")";
-    public static final String SEPARATOR_KEYWORD       = ";";
-    public static final String CLOSE_KEYWORD           = "}";
+    public static final String ANY_ARGUMENTS_KEYWORD      = "...";
+    public static final String CLOSE_ARGUMENTS_KEYWORD    = ")";
+    public static final String SEPARATOR_KEYWORD          = ";";
+    public static final String CLOSE_KEYWORD              = "}";
 }
diff --git a/src/proguard/ConfigurationParser.java b/src/proguard/ConfigurationParser.java
index 530d20c..fd77af8 100644
--- a/src/proguard/ConfigurationParser.java
+++ b/src/proguard/ConfigurationParser.java
@@ -1,6 +1,6 @@
-/* $Id: ConfigurationParser.java,v 1.26.2.5 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -25,8 +25,8 @@ import proguard.classfile.util.ClassUtil;
 import proguard.util.ListUtil;
 
 import java.io.*;
-import java.util.*;
 import java.net.URL;
+import java.util.*;
 
 
 /**
@@ -102,7 +102,7 @@ public class ConfigurationParser
 
             // First include directives.
             if      (ConfigurationConstants.AT_DIRECTIVE                                     .startsWith(nextWord) ||
-                     ConfigurationConstants.INCLUDE_DIRECTIVE                                .startsWith(nextWord)) parseIncludeArgument();
+                     ConfigurationConstants.INCLUDE_DIRECTIVE                                .startsWith(nextWord)) configuration.lastModified                     = parseIncludeArgument(configuration.lastModified);
             else if (ConfigurationConstants.BASE_DIRECTORY_DIRECTIVE                         .startsWith(nextWord)) parseBaseDirectoryArgument();
 
             // Then configuration options with or without arguments.
@@ -112,21 +112,24 @@ public class ConfigurationParser
             else if (ConfigurationConstants.RESOURCEJARS_OPTION                              .startsWith(nextWord)) throw new ParseException("The '-resourcejars' option is no longer supported. Please use the '-injars' option for all input");
             else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES_OPTION      .startsWith(nextWord)) configuration.skipNonPublicLibraryClasses      = parseNoArgument(false);
             else if (ConfigurationConstants.DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS_OPTION.startsWith(nextWord)) configuration.skipNonPublicLibraryClassMembers = parseNoArgument(false);
-
-            else if (ConfigurationConstants.KEEP_OPTION                                      .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, true,  false);
-            else if (ConfigurationConstants.KEEP_CLASS_MEMBERS_OPTION                        .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, false, false);
-            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION                 .startsWith(nextWord)) configuration.keep                             = parseClassSpecificationArguments(configuration.keep, false, true);
-            else if (ConfigurationConstants.KEEP_NAMES_OPTION                                .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, true,  false);
-            else if (ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION                   .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, false, false);
-            else if (ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION            .startsWith(nextWord)) configuration.keepNames                        = parseClassSpecificationArguments(configuration.keepNames, false, true);
+            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.PRINT_SEEDS_OPTION                               .startsWith(nextWord)) configuration.printSeeds                       = parseOptionalFile();
 
             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, true, false);
+            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.ASSUME_NO_SIDE_EFFECTS_OPTION                    .startsWith(nextWord)) configuration.assumeNoSideEffects              = parseClassSpecificationArguments(configuration.assumeNoSideEffects, false, false);
+            else if (ConfigurationConstants.OPTIMIZATION_PASSES                              .startsWith(nextWord)) configuration.optimizationPasses               = parseIntegerArgument();
+            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.DONT_OBFUSCATE_OPTION                            .startsWith(nextWord)) configuration.obfuscate                        = parseNoArgument(false);
@@ -135,15 +138,23 @@ public class ConfigurationParser
             else if (ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION                    .startsWith(nextWord)) configuration.obfuscationDictionary            = parseFile();
             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.DEFAULT_PACKAGE_OPTION                           .startsWith(nextWord)) configuration.defaultPackage                   = ClassUtil.internalClassName(parseOptionalArgument());
             else if (ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION           .startsWith(nextWord)) configuration.useMixedCaseClassNames           = parseNoArgument(false);
-            else if (ConfigurationConstants.KEEP_ATTRIBUTES_OPTION                           .startsWith(nextWord)) configuration.keepAttributes                   = parseKeepAttributesArguments(configuration.keepAttributes);
+            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.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.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.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();
             else
             {
@@ -167,14 +178,17 @@ public class ConfigurationParser
     }
 
 
-    private void parseIncludeArgument() throws ParseException, IOException
+    private long parseIncludeArgument(long lastModified) throws ParseException, IOException
     {
         // Read the configuation file name.
         readNextWord("configuration file name");
 
-        reader.includeWordReader(new FileWordReader(file(nextWord)));
+        File file = file(nextWord);
+        reader.includeWordReader(new FileWordReader(file));
 
         readNextWord();
+
+        return Math.max(lastModified, file.lastModified());
     }
 
 
@@ -223,7 +237,8 @@ public class ConfigurationParser
                     // Read the filter.
                     filters[counter++] =
                         ListUtil.commaSeparatedString(
-                        parseCommaSeparatedList("filter", true, false, true));
+                        parseCommaSeparatedList("filter", true,
+                                                false, true, false, true, null));
                 }
                 while (counter < filters.length &&
                        ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord));
@@ -277,55 +292,43 @@ public class ConfigurationParser
     }
 
 
-    private List parseKeepAttributesArguments(List keepAttributes)
+    private int parseClassVersion()
     throws ParseException, IOException
     {
-        // Create a new List if necessary.
-        if (keepAttributes == null)
+        // Read the obligatory target.
+        readNextWord("java version");
+
+        int classVersion = ClassUtil.internalClassVersion(nextWord);
+        if (classVersion == 0)
         {
-            keepAttributes = new ArrayList();
+            throw new ParseException("Unsupported java version " + reader.locationDescription());
         }
 
-        // Read the first attribute name.
         readNextWord();
 
-        // Should we keep all attributes?
-        if (configurationEnd())
-        {
-            keepAttributes.clear();
-            return keepAttributes;
-        }
+        return classVersion;
+    }
 
-        if (nextWord.equals(ConfigurationConstants.ANY_ATTRIBUTE_KEYWORD))
-        {
-            keepAttributes.clear();
-            readNextWord();
-            return keepAttributes;
-        }
 
-        while (true)
+    private int parseIntegerArgument()
+    throws ParseException, IOException
+    {
+        try
         {
-            // Add the attribute name to the list.
-            keepAttributes.add(nextWord);
+            // Read the obligatory integer.
+            readNextWord("integer");
 
-            // Read the separator, if any.
-            readNextWord();
-            if (configurationEnd())
-            {
-                break;
-            }
+            int integer = Integer.parseInt(nextWord);
 
-            if (!nextWord.equals(ConfigurationConstants.ATTRIBUTE_SEPARATOR_KEYWORD))
-            {
-                throw new ParseException("Expecting attribute name separator '" + ConfigurationConstants.ATTRIBUTE_SEPARATOR_KEYWORD +
-                                         "' before " + reader.locationDescription());
-            }
+            readNextWord();
 
-            // Read the next attribute name.
-            readNextWord("attribute name");
+            return integer;
+        }
+        catch (NumberFormatException e)
+        {
+            throw new ParseException("Expecting integer argument instead of '" + nextWord +
+                                     "' before " + reader.locationDescription());
         }
-
-        return keepAttributes;
     }
 
 
@@ -392,9 +395,86 @@ public class ConfigurationParser
     }
 
 
-    private List parseClassSpecificationArguments(List    classSpecifications,
-                                                  boolean markClassFiles,
-                                                  boolean markConditionally)
+    private long parseNoArgument(long value) throws IOException
+    {
+        readNextWord();
+
+        return value;
+    }
+
+
+    private List parseKeepSpecificationArguments(List    keepSpecifications,
+                                                 boolean markClasses,
+                                                 boolean markConditionally,
+                                                 boolean allowShrinking)
+    throws ParseException, IOException
+    {
+        // Create a new List if necessary.
+        if (keepSpecifications == null)
+        {
+            keepSpecifications = new ArrayList();
+        }
+
+        //boolean allowShrinking    = false;
+        boolean allowOptimization = false;
+        boolean allowObfuscation  = false;
+
+        // Read the keep modifiers.
+        while (true)
+        {
+            readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD +
+                         "' or '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'", true);
+
+            if (!ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD.equals(nextWord))
+            {
+                // Not a comma. Stop parsing the keep modifiers.
+                break;
+            }
+
+            readNextWord("keyword '" +
+                         ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + "', '" +
+                         ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + "', or '" +
+                         ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + "'");
+
+            if      (ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION   .startsWith(nextWord))
+            {
+                allowShrinking    = true;
+            }
+            else if (ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION.startsWith(nextWord))
+            {
+                allowOptimization = true;
+            }
+            else if (ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION .startsWith(nextWord))
+            {
+                allowObfuscation  = true;
+            }
+            else
+            {
+                throw new ParseException("Expecting keyword '" +
+                                         ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION + "', '" +
+                                         ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION + "', or '" +
+                                         ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION + "' before " +
+                                         reader.locationDescription());
+            }
+        }
+
+        // Read the class configuration.
+        ClassSpecification classSpecification =
+            parseClassSpecificationArguments();
+
+        // Create and add the keep configuration.
+        keepSpecifications.add(new KeepSpecification(markClasses,
+                                                     markConditionally,
+                                                     allowShrinking,
+                                                     allowOptimization,
+                                                     allowObfuscation,
+                                                     classSpecification));
+
+        return keepSpecifications;
+    }
+
+
+    private List parseClassSpecificationArguments(List classSpecifications)
     throws ParseException, IOException
     {
         // Create a new List if necessary.
@@ -403,34 +483,39 @@ public class ConfigurationParser
             classSpecifications = new ArrayList();
         }
 
-        // Read and add the keep configuration.
-        classSpecifications.add(parseClassSpecificationArguments(markClassFiles,
-                                                                 markConditionally));
+        // Read and add the class configuration.
+        readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD +
+                     "' or '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'", true);
+
+        classSpecifications.add(parseClassSpecificationArguments());
 
         return classSpecifications;
     }
 
 
-    private ClassSpecification parseClassSpecificationArguments(boolean markClassFiles,
-                                                                boolean markConditionally)
+    private ClassSpecification parseClassSpecificationArguments()
     throws ParseException, IOException
     {
-        // Remember the comments preceeding this option.
-        String comments = lastComments;
+        // Clear the annotation type.
+        String annotationType = null;
 
-        // Parse the class access modifiers, if any.
+        // Clear the class access modifiers.
         int requiredSetClassAccessFlags   = 0;
         int requiredUnsetClassAccessFlags = 0;
 
-        while (true)
+        // Parse the class annotations and access modifiers until the class keyword.
+        while (!ConfigurationConstants.CLASS_KEYWORD.equals(nextWord))
         {
-            readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD + "'" +
-                         " or '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'");
-
-            if (ConfigurationConstants.CLASS_KEYWORD.equals(nextWord))
+            // Parse the annotation type, if any.
+            if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord))
             {
-                // The class keyword. Stop parsing the class access modifiers.
-                break;
+                annotationType =
+                    ClassUtil.internalType(
+                    ListUtil.commaSeparatedString(
+                    parseCommaSeparatedList("annotation type",
+                                            true, false, false, true, false, null)));
+
+                continue;
             }
 
             // Strip the negating sign, if any.
@@ -438,13 +523,14 @@ public class ConfigurationParser
                 nextWord.substring(1) :
                 nextWord;
 
+            // Parse the class access modifiers.
             int accessFlag =
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)    ? ClassConstants.INTERNAL_ACC_PUBLIC    :
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_FINAL)     ? ClassConstants.INTERNAL_ACC_FINAL     :
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_INTERFACE) ? ClassConstants.INTERNAL_ACC_INTERFACE :
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_ABSTRACT)  ? ClassConstants.INTERNAL_ACC_ABSTRACT  :
                                                                              unknownAccessFlag();
-            if (strippedWord == nextWord)
+            if (strippedWord.equals(nextWord))
             {
                 requiredSetClassAccessFlags   |= accessFlag;
             }
@@ -466,13 +552,16 @@ public class ConfigurationParser
                 // The interface keyword. Stop parsing the class flags.
                 break;
             }
+
+            readNextWord("keyword '" + ConfigurationConstants.CLASS_KEYWORD +
+                         "' or '" + ClassConstants.EXTERNAL_ACC_INTERFACE + "'");
         }
 
        // Parse the class name part.
         String externalClassName =
             ListUtil.commaSeparatedString(
             parseCommaSeparatedList("class name or interface name",
-                                    false, true, false));
+                                    true, false, false, true, false, null));
 
         // For backward compatibility, allow a single "*" wildcard to match any
         // class.
@@ -480,7 +569,9 @@ public class ConfigurationParser
             null :
             ClassUtil.internalClassName(externalClassName);
 
-        String extendsClassName = null;
+        // Clear the annotation type and the class name of the extends part.
+        String extendsAnnotationType = null;
+        String extendsClassName           = null;
 
         if (!configurationEnd())
         {
@@ -488,26 +579,41 @@ public class ConfigurationParser
             if (ConfigurationConstants.IMPLEMENTS_KEYWORD.equals(nextWord) ||
                 ConfigurationConstants.EXTENDS_KEYWORD.equals(nextWord))
             {
-                extendsClassName =
-                    ClassUtil.internalClassName(
+                readNextWord("class name or interface name", true);
+
+                // Parse the annotation type, if any.
+                if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord))
+                {
+                    extendsAnnotationType =
+                        ClassUtil.internalType(
+                        ListUtil.commaSeparatedString(
+                        parseCommaSeparatedList("annotation type",
+                                                true, false, false, true, false, null)));
+                }
+
+                String externalExtendsClassName =
                     ListUtil.commaSeparatedString(
                     parseCommaSeparatedList("class name or interface name",
-                                            false, true, false)));
+                                            false, false, false, true, false, null));
+
+                extendsClassName = ConfigurationConstants.ANY_CLASS_KEYWORD.equals(externalExtendsClassName) ?
+                    null :
+                    ClassUtil.internalClassName(externalExtendsClassName);
             }
         }
 
         // Create the basic class specification.
         ClassSpecification classSpecification =
-            new ClassSpecification(requiredSetClassAccessFlags,
+            new ClassSpecification(lastComments,
+                                   requiredSetClassAccessFlags,
                                    requiredUnsetClassAccessFlags,
+                                   annotationType,
                                    className,
-                                   extendsClassName,
-                                   markClassFiles,
-                                   markConditionally,
-                                   comments);
+                                   extendsAnnotationType,
+                                   extendsClassName);
 
 
-        // Now modify this ClassSpecification, adding any class members.
+        // Now add any class members to this class specification.
         if (!configurationEnd())
         {
             // Check the class member opening part.
@@ -518,41 +624,58 @@ public class ConfigurationParser
             }
 
             // Parse all class members.
-            while (parseClassMemberSpecificationArguments(externalClassName,
-                                                          classSpecification));
+            while (true)
+            {
+                readNextWord("class member description" +
+                             " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'", true);
+
+                if (nextWord.equals(ConfigurationConstants.CLOSE_KEYWORD))
+                {
+                    // The closing brace. Stop parsing the class members.
+                    readNextWord();
+
+                    break;
+                }
+
+                parseMemberSpecificationArguments(externalClassName,
+                                                  classSpecification);
+            }
         }
 
         return classSpecification;
     }
 
 
-    private boolean parseClassMemberSpecificationArguments(String             externalClassName,
-                                                           ClassSpecification classSpecification)
+    private void parseMemberSpecificationArguments(String             externalClassName,
+                                                   ClassSpecification classSpecification)
     throws ParseException, IOException
     {
+        // Clear the annotation name.
+        String annotationType = null;
+
         // Parse the class member access modifiers, if any.
         int requiredSetMemberAccessFlags   = 0;
         int requiredUnsetMemberAccessFlags = 0;
 
-        while (true)
+        while (!configurationEnd(true))
         {
-            readNextWord("class member description" +
-                         " or closing '" + ConfigurationConstants.CLOSE_KEYWORD + "'");
-
-            if (requiredSetMemberAccessFlags   == 0 &&
-                requiredUnsetMemberAccessFlags == 0 &&
-                ConfigurationConstants.CLOSE_KEYWORD.equals(nextWord))
+            // Parse the annotation type, if any.
+            if (ConfigurationConstants.ANNOTATION_KEYWORD.equals(nextWord))
             {
-                // The closing brace. Stop parsing the class members.
-                readNextWord();
+                annotationType =
+                    ClassUtil.internalType(
+                    ListUtil.commaSeparatedString(
+                    parseCommaSeparatedList("annotation type",
+                                            true, false, false, true, false, null)));
 
-                return false;
+                continue;
             }
 
             String strippedWord = nextWord.startsWith("!") ?
                 nextWord.substring(1) :
                 nextWord;
 
+            // Parse the class member access modifiers.
             int accessFlag =
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_PUBLIC)       ? ClassConstants.INTERNAL_ACC_PUBLIC       :
                 strippedWord.equals(ClassConstants.EXTERNAL_ACC_PRIVATE)      ? ClassConstants.INTERNAL_ACC_PRIVATE      :
@@ -572,7 +695,7 @@ public class ConfigurationParser
                 break;
             }
 
-            if (strippedWord == nextWord)
+            if (strippedWord.equals(nextWord))
             {
                 requiredSetMemberAccessFlags   |= accessFlag;
             }
@@ -589,6 +712,8 @@ public class ConfigurationParser
                 throw new ParseException("Conflicting class member access modifiers for " +
                                          reader.locationDescription());
             }
+
+            readNextWord("class member description");
         }
 
         // Parse the class member type and name part.
@@ -607,15 +732,17 @@ public class ConfigurationParser
                                        requiredUnsetMemberAccessFlags);
 
                 classSpecification.addField(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 null,
-                                                 null));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            null,
+                                            null));
                 classSpecification.addMethod(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 null,
-                                                 null));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            null,
+                                            null));
             }
             else if (ConfigurationConstants.ANY_FIELD_KEYWORD.equals(nextWord))
             {
@@ -623,10 +750,11 @@ public class ConfigurationParser
                                       requiredUnsetMemberAccessFlags);
 
                 classSpecification.addField(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 null,
-                                                 null));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            null,
+                                            null));
             }
             else if (ConfigurationConstants.ANY_METHOD_KEYWORD.equals(nextWord))
             {
@@ -634,10 +762,11 @@ public class ConfigurationParser
                                        requiredUnsetMemberAccessFlags);
 
                 classSpecification.addMethod(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 null,
-                                                 null));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            null,
+                                            null));
             }
 
             // We still have to read the closing separator.
@@ -700,10 +829,11 @@ public class ConfigurationParser
 
                 // Add the field.
                 classSpecification.addField(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 name,
-                                                 descriptor));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            name,
+                                            descriptor));
             }
             else if (ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD.equals(nextWord))
             {
@@ -714,7 +844,7 @@ public class ConfigurationParser
                 // Parse the method arguments.
                 String descriptor =
                     ClassUtil.internalMethodDescriptor(type,
-                                                       parseCommaSeparatedList("argument", true, true, false));
+                                                       parseCommaSeparatedList("argument", true, true, true, true, false, null));
 
                 if (!ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord))
                 {
@@ -734,10 +864,11 @@ public class ConfigurationParser
 
                 // Add the method.
                 classSpecification.addMethod(
-                    new ClassMemberSpecification(requiredSetMemberAccessFlags,
-                                                 requiredUnsetMemberAccessFlags,
-                                                 name,
-                                                 descriptor));
+                    new MemberSpecification(requiredSetMemberAccessFlags,
+                                            requiredUnsetMemberAccessFlags,
+                                            annotationType,
+                                            name,
+                                            descriptor));
             }
             else
             {
@@ -747,8 +878,6 @@ public class ConfigurationParser
                                          "' before " + reader.locationDescription());
             }
         }
-
-        return true;
     }
 
 
@@ -758,20 +887,44 @@ public class ConfigurationParser
      * or semi-colon.
      */
     private List parseCommaSeparatedList(String  expectedDescription,
+                                         boolean readFirstWord,
                                          boolean allowEmptyList,
+                                         boolean expectClosingParenthesis,
                                          boolean checkJavaIdentifiers,
-                                         boolean replaceSystemProperties)
+                                         boolean replaceSystemProperties,
+                                         List    list)
     throws ParseException, IOException
     {
-        List arguments = new ArrayList();
+        if (list == null)
+        {
+            list = new ArrayList();
+        }
 
-        while (true)
+        if (readFirstWord)
         {
-            // Read an argument.
-            readNextWord(expectedDescription);
+            if (expectClosingParenthesis || !allowEmptyList)
+            {
+                // Read the first list entry.
+                readNextWord(expectedDescription);
+            }
+            else
+            {
+                // Read the first list entry, if there is any.
+                readNextWord();
 
-            if (allowEmptyList        &&
-                arguments.size() == 0 &&
+                // Check if the list is empty.
+                if (configurationEnd() ||
+                    nextWord.equals(ConfigurationConstants.ANY_ATTRIBUTE_KEYWORD))
+                {
+                    return list;
+                }
+            }
+        }
+
+        while (true)
+        {
+            if (expectClosingParenthesis &&
+                list.size() == 0         &&
                 (ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD.equals(nextWord) ||
                  ConfigurationConstants.SEPARATOR_KEYWORD.equals(nextWord)))
             {
@@ -788,9 +941,9 @@ public class ConfigurationParser
                 nextWord = replaceSystemProperties(nextWord);
             }
 
-            arguments.add(nextWord);
+            list.add(nextWord);
 
-            if (allowEmptyList)
+            if (expectClosingParenthesis)
             {
                 // Read a comma (or a closing parenthesis, or a different word).
                 readNextWord("separating '" + ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
@@ -807,9 +960,12 @@ public class ConfigurationParser
             {
                 break;
             }
+
+            // Read the next list entry.
+            readNextWord(expectedDescription);
         }
 
-        return arguments;
+        return list;
     }
 
 
@@ -843,6 +999,7 @@ public class ConfigurationParser
         }
         catch (IOException ex)
         {
+            // Just keep the original representation.
         }
 
         return file;
@@ -896,8 +1053,20 @@ public class ConfigurationParser
     private void readNextWord(String expectedDescription)
     throws ParseException, IOException
     {
+        readNextWord(expectedDescription, false);
+    }
+
+
+    /**
+     * Reads the next word of the configuration in the 'nextWord' field,
+     * throwing an exception if there is no next word.
+     */
+    private void readNextWord(String  expectedDescription,
+                              boolean expectingAtCharacter)
+    throws ParseException, IOException
+    {
         readNextWord();
-        if (configurationEnd())
+        if (configurationEnd(expectingAtCharacter))
         {
             throw new ParseException("Expecting " + expectedDescription +
                                      " before " + reader.locationDescription());
@@ -919,9 +1088,19 @@ public class ConfigurationParser
      */
     private boolean configurationEnd()
     {
+        return configurationEnd(false);
+    }
+
+
+    /**
+     * Returns whether the end of the configuration has been reached.
+     */
+    private boolean configurationEnd(boolean expectingAtCharacter)
+    {
         return nextWord == null ||
                nextWord.startsWith(ConfigurationConstants.OPTION_PREFIX) ||
-               nextWord.equals(ConfigurationConstants.AT_DIRECTIVE);
+               (!expectingAtCharacter &&
+                nextWord.equals(ConfigurationConstants.AT_DIRECTIVE));
     }
 
 
diff --git a/src/proguard/ConfigurationWriter.java b/src/proguard/ConfigurationWriter.java
index 3ac8941..16a5182 100644
--- a/src/proguard/ConfigurationWriter.java
+++ b/src/proguard/ConfigurationWriter.java
@@ -1,6 +1,6 @@
-/* $Id: ConfigurationWriter.java,v 1.18.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,12 @@
  */
 package proguard;
 
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.util.*;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
+import proguard.util.ListUtil;
 
 import java.io.*;
-import java.util.*;
+import java.util.List;
 
 
 /**
@@ -35,13 +35,6 @@ import java.util.*;
  */
 public class ConfigurationWriter
 {
-    private static final String[] KEEP_NAMES_OPTIONS = new String[]
-    {
-        ConfigurationConstants.KEEP_NAMES_OPTION,
-        ConfigurationConstants.KEEP_CLASS_MEMBER_NAMES_OPTION,
-        ConfigurationConstants.KEEP_CLASSES_WITH_MEMBER_NAMES_OPTION
-    };
-
     private static final String[] KEEP_OPTIONS = new String[]
     {
         ConfigurationConstants.KEEP_OPTION,
@@ -49,22 +42,8 @@ public class ConfigurationWriter
         ConfigurationConstants.KEEP_CLASSES_WITH_MEMBERS_OPTION
     };
 
-    private static final String[] WHY_ARE_YOU_KEEPING_OPTIONS = new String[]
-    {
-        ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION,
-        ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION,
-        ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION
-    };
-
-    private static final String[] ASSUME_NO_SIDE_EFFECT_OPTIONS = new String[]
-    {
-        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION,
-        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION,
-        ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION
-    };
 
-
-    private PrintWriter writer;
+    private final PrintWriter writer;
     private File        baseDir;
 
 
@@ -128,44 +107,57 @@ 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.DONT_SHRINK_OPTION,                                !configuration.shrink);
-        writeOption(ConfigurationConstants.PRINT_USAGE_OPTION,                                configuration.printUsage);
-
-        writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION ,                             !configuration.optimize);
-        writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION,                  configuration.allowAccessModification);
-
-        writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION,                             !configuration.obfuscate);
-        writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION,                              configuration.printMapping);
-        writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION,                              configuration.applyMapping);
-        writeOption(ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION,                     configuration.obfuscationDictionary);
-        writeOption(ConfigurationConstants.OVERLOAD_AGGRESSIVELY_OPTION,                      configuration.overloadAggressively);
-        writeOption(ConfigurationConstants.USE_UNIQUE_CLASS_MEMBER_NAMES_OPTION,              configuration.useUniqueClassMemberNames);
-        writeOption(ConfigurationConstants.DEFAULT_PACKAGE_OPTION,                            configuration.defaultPackage);
-        writeOption(ConfigurationConstants.DONT_USE_MIXED_CASE_CLASS_NAMES_OPTION,            !configuration.useMixedCaseClassNames);
-        writeOption(ConfigurationConstants.KEEP_ATTRIBUTES_OPTION,                            ListUtil.commaSeparatedString(configuration.keepAttributes));
-        writeOption(ConfigurationConstants.RENAME_SOURCE_FILE_ATTRIBUTE_OPTION,               configuration.newSourceFileAttribute);
-
-        writeOption(ConfigurationConstants.VERBOSE_OPTION,                                    configuration.verbose);
-        writeOption(ConfigurationConstants.DONT_NOTE_OPTION,                                  !configuration.note);
-        writeOption(ConfigurationConstants.DONT_WARN_OPTION,                                  !configuration.warn);
-        writeOption(ConfigurationConstants.IGNORE_WARNINGS_OPTION,                            configuration.ignoreWarnings);
-
-        writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION,                                configuration.printSeeds);
+        writeOption(ConfigurationConstants.TARGET_OPTION,                                     ClassUtil.externalClassVersion(configuration.targetClassVersion));
+        writeOption(ConfigurationConstants.FORCE_PROCESSING_OPTION,                           configuration.lastModified == Long.MAX_VALUE);
+
+        writeOption(ConfigurationConstants.DONT_SHRINK_OPTION, !configuration.shrink);
+        writeOption(ConfigurationConstants.PRINT_USAGE_OPTION, configuration.printUsage);
+
+        writeOption(ConfigurationConstants.DONT_OPTIMIZE_OPTION,             !configuration.optimize);
+        writeOption(ConfigurationConstants.OPTIMIZATION_PASSES,              configuration.optimizationPasses);
+        writeOption(ConfigurationConstants.ALLOW_ACCESS_MODIFICATION_OPTION, configuration.allowAccessModification);
+
+        writeOption(ConfigurationConstants.DONT_OBFUSCATE_OPTION,                  !configuration.obfuscate);
+        writeOption(ConfigurationConstants.PRINT_MAPPING_OPTION,                   configuration.printMapping);
+        writeOption(ConfigurationConstants.APPLY_MAPPING_OPTION,                   configuration.applyMapping);
+        writeOption(ConfigurationConstants.OBFUSCATION_DICTIONARY_OPTION,          configuration.obfuscationDictionary);
+        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.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.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.IGNORE_WARNINGS_OPTION,     configuration.ignoreWarnings);
+        writeOption(ConfigurationConstants.PRINT_CONFIGURATION_OPTION, configuration.printConfiguration);
+        writeOption(ConfigurationConstants.DUMP_OPTION,                configuration.dump);
+
+        writeOption(ConfigurationConstants.PRINT_SEEDS_OPTION,     configuration.printSeeds);
         writer.println();
 
         // Write the "why are you keeping" options.
-        writeOptions(WHY_ARE_YOU_KEEPING_OPTIONS, configuration.whyAreYouKeeping);
+        writeOptions(ConfigurationConstants.WHY_ARE_YOU_KEEPING_OPTION, configuration.whyAreYouKeeping);
 
         // Write the keep options.
         writeOptions(KEEP_OPTIONS, configuration.keep);
 
-        // Write the keep names options.
-        writeOptions(KEEP_NAMES_OPTIONS, configuration.keepNames);
-
         // Write the "no side effect methods" options.
-        writeOptions(ASSUME_NO_SIDE_EFFECT_OPTIONS, configuration.assumeNoSideEffects);
-}
+        writeOptions(ConfigurationConstants.ASSUME_NO_SIDE_EFFECTS_OPTION, configuration.assumeNoSideEffects);
+
+        if (writer.checkError())
+        {
+            throw new IOException("Can't write configuration");
+        }
+    }
 
 
     private void writeJarOptions(String    inputEntryOptionName,
@@ -181,7 +173,9 @@ public class ConfigurationWriter
                      outputEntryOptionName :
                      inputEntryOptionName;
 
-                writer.print(optionName + " " + relativeFileName(entry.getFile()));
+                writer.print(optionName);
+                writer.print(' ');
+                writer.print(relativeFileName(entry.getFile()));
 
                 // Append the filters, if any.
                 boolean filtered = false;
@@ -235,11 +229,24 @@ public class ConfigurationWriter
     }
 
 
+    private void writeOption(String optionName, int argument)
+    {
+        if (argument != 1)
+        {
+            writer.print(optionName);
+            writer.print(' ');
+            writer.println(argument);
+        }
+    }
+
+
     private void writeOption(String optionName, String arguments)
     {
         if (arguments != null)
         {
-            writer.println(optionName + " " + quotedString(arguments));
+            writer.print(optionName);
+            writer.print(' ');
+            writer.println(quotedString(arguments));
         }
     }
 
@@ -250,7 +257,9 @@ public class ConfigurationWriter
         {
             if (file.getPath().length() > 0)
             {
-                writer.println(optionName + " " + relativeFileName(file));
+                writer.print(optionName);
+                writer.print(' ');
+                writer.println(relativeFileName(file));
             }
             else
             {
@@ -261,19 +270,63 @@ public class ConfigurationWriter
 
 
     private void writeOptions(String[] optionNames,
-                              List     classSpecifications)
+                              List     keepSpecifications)
+    {
+        if (keepSpecifications != null)
+        {
+            for (int index = 0; index < keepSpecifications.size(); index++)
+            {
+                writeOption(optionNames, (KeepSpecification)keepSpecifications.get(index));
+            }
+        }
+    }
+
+
+    private void writeOption(String[]          optionNames,
+                             KeepSpecification keepSpecification)
+    {
+        // Compose the option name.
+        String optionName = optionNames[keepSpecification.markConditionally ? 2 :
+                                        keepSpecification.markClasses       ? 0 :
+                                                                              1];
+
+        if (keepSpecification.allowShrinking)
+        {
+            optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                          ConfigurationConstants.ALLOW_SHRINKING_SUBOPTION;
+        }
+
+        if (keepSpecification.allowOptimization)
+        {
+            optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                          ConfigurationConstants.ALLOW_OPTIMIZATION_SUBOPTION;
+        }
+
+        if (keepSpecification.allowObfuscation)
+        {
+            optionName += ConfigurationConstants.ARGUMENT_SEPARATOR_KEYWORD +
+                          ConfigurationConstants.ALLOW_OBFUSCATION_SUBOPTION;
+        }
+
+        // Write out the option with the proper class specification.
+        writeOption(optionName, keepSpecification);
+    }
+
+
+    private void writeOptions(String optionName,
+                              List   classSpecifications)
     {
         if (classSpecifications != null)
         {
             for (int index = 0; index < classSpecifications.size(); index++)
             {
-                writeOption(optionNames, (ClassSpecification)classSpecifications.get(index));
+                writeOption(optionName, (ClassSpecification)classSpecifications.get(index));
             }
         }
     }
 
 
-    private void writeOption(String[]           optionNames,
+    private void writeOption(String             optionName,
                              ClassSpecification classSpecification)
     {
         writer.println();
@@ -281,12 +334,16 @@ public class ConfigurationWriter
         // Write out the comments for this option.
         writeComments(classSpecification.comments);
 
-        // Write out the proper class specification option name.
-        writer.print(optionNames[classSpecification.markConditionally ? 2 :
-                                 classSpecification.markClassFiles    ? 0 :
-                                                                        1]);
+        writer.print(optionName);
+        writer.print(' ');
 
-        writer.print(" ");
+        // Write out the required annotation, if any.
+        if (classSpecification.annotationType != null)
+        {
+            writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
+            writer.print(ClassUtil.externalType(classSpecification.annotationType));
+            writer.print(' ');
+        }
 
         // Write out the class access flags.
         writer.print(ClassUtil.externalClassAccessFlags(classSpecification.requiredUnsetAccessFlags,
@@ -300,10 +357,10 @@ public class ConfigurationWriter
               classSpecification.requiredUnsetAccessFlags) &
              ClassConstants.INTERNAL_ACC_INTERFACE) == 0)
         {
-            writer.print("class");
+            writer.print(ConfigurationConstants.CLASS_KEYWORD);
         }
 
-        writer.print(" ");
+        writer.print(' ');
 
         // Write out the class name.
         writer.print(classSpecification.className != null ?
@@ -311,20 +368,38 @@ public class ConfigurationWriter
             ConfigurationConstants.ANY_CLASS_KEYWORD);
 
         // Write out the extends template, if any.
-        if (classSpecification.extendsClassName != null)
+        if (classSpecification.extendsAnnotationType != null ||
+            classSpecification.extendsClassName      != null)
         {
-            writer.print(" extends " + ClassUtil.externalClassName(classSpecification.extendsClassName));
+            writer.print(' ');
+            writer.print(ConfigurationConstants.EXTENDS_KEYWORD);
+            writer.print(' ');
+
+            // Write out the required extends annotation, if any.
+            if (classSpecification.extendsAnnotationType != null)
+            {
+                writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
+                writer.print(ClassUtil.externalType(classSpecification.extendsAnnotationType));
+                writer.print(' ');
+            }
+
+            // Write out the extended class name.
+            writer.print(classSpecification.extendsClassName != null ?
+                ClassUtil.externalClassName(classSpecification.extendsClassName) :
+                ConfigurationConstants.ANY_CLASS_KEYWORD);
         }
 
         // Write out the keep field and keep method options, if any.
         if (classSpecification.fieldSpecifications  != null ||
             classSpecification.methodSpecifications != null)
         {
-            writer.println(" {");
+            writer.print(' ');
+            writer.println(ConfigurationConstants.OPEN_KEYWORD);
 
             writeFieldSpecification( classSpecification.fieldSpecifications);
             writeMethodSpecification(classSpecification.methodSpecifications);
-            writer.println("}");
+
+            writer.println(ConfigurationConstants.CLOSE_KEYWORD);
         }
         else
         {
@@ -348,6 +423,12 @@ public class ConfigurationWriter
                 }
 
                 writer.print('#');
+
+                if (comments.charAt(index) != ' ')
+                {
+                    writer.print(' ');
+                }
+
                 writer.println(comments.substring(index, breakIndex));
 
                 index = breakIndex + 1;
@@ -356,78 +437,86 @@ public class ConfigurationWriter
     }
 
 
-    private void writeFieldSpecification(List classMemberSpecifications)
+    private void writeFieldSpecification(List memberSpecifications)
     {
-        if (classMemberSpecifications != null)
+        if (memberSpecifications != null)
         {
-            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            for (int index = 0; index < memberSpecifications.size(); index++)
             {
-                ClassMemberSpecification classMemberSpecification =
-                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+                MemberSpecification memberSpecification =
+                    (MemberSpecification)memberSpecifications.get(index);
 
                 writer.print("    ");
 
+                // Write out the required annotation, if any.
+                if (memberSpecification.annotationType != null)
+                {
+                    writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
+                    writer.println(ClassUtil.externalType(memberSpecification.annotationType));
+                    writer.print("    ");
+                }
+
                 // Write out the field access flags.
-                writer.print(ClassUtil.externalFieldAccessFlags(classMemberSpecification.requiredUnsetAccessFlags,
+                writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredUnsetAccessFlags,
                                                                 ConfigurationConstants.NEGATOR_KEYWORD));
 
-                writer.print(ClassUtil.externalFieldAccessFlags(classMemberSpecification.requiredSetAccessFlags));
+                writer.print(ClassUtil.externalFieldAccessFlags(memberSpecification.requiredSetAccessFlags));
 
                 // Write out the field name and descriptor.
-                String name       = classMemberSpecification.name;
-                String descriptor = classMemberSpecification.descriptor;
+                String name       = memberSpecification.name;
+                String descriptor = memberSpecification.descriptor;
 
-                if (name == null)
-                {
-                    name = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD;
-                }
-
-                writer.print(descriptor != null ?
+                writer.print(descriptor == null ? name == null ?
+                    ConfigurationConstants.ANY_FIELD_KEYWORD             :
+                    ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name :
                     ClassUtil.externalFullFieldDescription(0,
-                                                           name,
-                                                           descriptor) :
-                    ConfigurationConstants.ANY_FIELD_KEYWORD);
+                                                           name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name,
+                                                           descriptor));
 
-                writer.println(";");
+                writer.println(ConfigurationConstants.SEPARATOR_KEYWORD);
             }
         }
     }
 
 
-    private void writeMethodSpecification(List classMemberSpecifications)
+    private void writeMethodSpecification(List memberSpecifications)
     {
-        if (classMemberSpecifications != null)
+        if (memberSpecifications != null)
         {
-            for (int index = 0; index < classMemberSpecifications.size(); index++)
+            for (int index = 0; index < memberSpecifications.size(); index++)
             {
-                ClassMemberSpecification classMemberSpecification =
-                    (ClassMemberSpecification)classMemberSpecifications.get(index);
+                MemberSpecification memberSpecification =
+                    (MemberSpecification)memberSpecifications.get(index);
 
                 writer.print("    ");
 
+                // Write out the required annotation, if any.
+                if (memberSpecification.annotationType != null)
+                {
+                    writer.print(ConfigurationConstants.ANNOTATION_KEYWORD);
+                    writer.println(ClassUtil.externalType(memberSpecification.annotationType));
+                    writer.print("    ");
+                }
+
                 // Write out the method access flags.
-                writer.print(ClassUtil.externalMethodAccessFlags(classMemberSpecification.requiredUnsetAccessFlags,
+                writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredUnsetAccessFlags,
                                                                  ConfigurationConstants.NEGATOR_KEYWORD));
 
-                writer.print(ClassUtil.externalMethodAccessFlags(classMemberSpecification.requiredSetAccessFlags));
+                writer.print(ClassUtil.externalMethodAccessFlags(memberSpecification.requiredSetAccessFlags));
 
                 // Write out the method name and descriptor.
-                String name       = classMemberSpecification.name;
-                String descriptor = classMemberSpecification.descriptor;
-
-                if (name == null)
-                {
-                    name = ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD;
-                }
+                String name       = memberSpecification.name;
+                String descriptor = memberSpecification.descriptor;
 
-                writer.print(descriptor != null ?
+                writer.print(descriptor == null ? name == null ?
+                    ConfigurationConstants.ANY_METHOD_KEYWORD :
+                    ConfigurationConstants.ANY_TYPE_KEYWORD + ' ' + name + ConfigurationConstants.OPEN_ARGUMENTS_KEYWORD + ConfigurationConstants.ANY_ARGUMENTS_KEYWORD + ConfigurationConstants.CLOSE_ARGUMENTS_KEYWORD :
                     ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT,
                                                             0,
-                                                            name,
-                                                            descriptor) :
-                    ConfigurationConstants.ANY_METHOD_KEYWORD);
+                                                            name == null ? ConfigurationConstants.ANY_CLASS_MEMBER_KEYWORD : name,
+                                                            descriptor));
 
-                writer.println(";");
+                writer.println(ConfigurationConstants.SEPARATOR_KEYWORD);
             }
         }
     }
diff --git a/src/proguard/DataEntryReaderFactory.java b/src/proguard/DataEntryReaderFactory.java
index b4b4afc..ad7a4e6 100644
--- a/src/proguard/DataEntryReaderFactory.java
+++ b/src/proguard/DataEntryReaderFactory.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryReaderFactory.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -39,7 +39,7 @@ public class DataEntryReaderFactory
      * @param messagePrefix  a prefix for messages that are printed out.
      * @param classPathEntry the input class path entry.
      * @param reader         a data entry reader to which the reading of actual
-     *                       class files and resource files can be delegated.
+     *                       classes and resource files can be delegated.
      * @return a DataEntryReader for reading the given class path entry.
      */
     public static DataEntryReader createDataEntryReader(String          messagePrefix,
@@ -47,10 +47,10 @@ public class DataEntryReaderFactory
                                                         DataEntryReader reader)
     {
         String entryName = classPathEntry.getName();
-        boolean isJar = entryName.endsWith(".jar");
-        boolean isWar = entryName.endsWith(".war");
-        boolean isEar = entryName.endsWith(".ear");
-        boolean isZip = entryName.endsWith(".zip");
+        boolean isJar = endsWithIgnoreCase(entryName, ".jar");
+        boolean isWar = endsWithIgnoreCase(entryName, ".war");
+        boolean isEar = endsWithIgnoreCase(entryName, ".ear");
+        boolean isZip = endsWithIgnoreCase(entryName, ".zip");
 
         String filter    = classPathEntry.getFilter();
         String jarFilter = classPathEntry.getJarFilter();
@@ -74,8 +74,10 @@ public class DataEntryReaderFactory
         // Add a filter, if specified.
         if (filter != null)
         {
-            reader = new FilteredDataEntryReader(new DataEntryNameFilter(new FileNameListMatcher(filter)),
-                                                 reader);
+            reader = new FilteredDataEntryReader(
+                     new DataEntryNameFilter(
+                     new ListParser(new FileNameParser()).parse(filter)),
+                         reader);
         }
 
         // Unzip any jars, if necessary.
@@ -123,7 +125,7 @@ public class DataEntryReaderFactory
             {
                 jarReader = new FilteredDataEntryReader(
                             new DataEntryNameFilter(
-                            new FileNameListMatcher(jarFilter)),
+                            new ListParser(new FileNameParser()).parse(jarFilter)),
                                 jarReader);
             }
 
@@ -135,4 +137,17 @@ public class DataEntryReaderFactory
                        reader);
         }
     }
+
+
+    /**
+     * Returns whether the given string ends with the given suffix, ignoring its
+     * case.
+     */
+    private static boolean endsWithIgnoreCase(String string, String suffix)
+    {
+        int stringLength = string.length();
+        int suffixLength = suffix.length();
+
+        return string.regionMatches(true, stringLength - suffixLength, suffix, 0, suffixLength);
+    }
 }
diff --git a/src/proguard/DataEntryWriterFactory.java b/src/proguard/DataEntryWriterFactory.java
index 7a1be3d..40bdc31 100644
--- a/src/proguard/DataEntryWriterFactory.java
+++ b/src/proguard/DataEntryWriterFactory.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryWriterFactory.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,9 +23,6 @@ package proguard;
 import proguard.io.*;
 import proguard.util.*;
 
-import java.io.*;
-
-
 /**
  * This class can create DataEntryWriter instances based on class paths. The
  * writers will wrap the output in the proper jars, wars, ears, and zips.
@@ -67,10 +64,10 @@ public class DataEntryWriterFactory
                                                               DataEntryWriter alternativeWriter)
     {
         String entryName = classPathEntry.getName();
-        boolean isJar = entryName.endsWith(".jar");
-        boolean isWar = entryName.endsWith(".war");
-        boolean isEar = entryName.endsWith(".ear");
-        boolean isZip = entryName.endsWith(".zip");
+        boolean isJar = endsWithIgnoreCase(entryName, ".jar");
+        boolean isWar = endsWithIgnoreCase(entryName, ".war");
+        boolean isEar = endsWithIgnoreCase(entryName, ".ear");
+        boolean isZip = endsWithIgnoreCase(entryName, ".zip");
 
         String filter    = classPathEntry.getFilter();
         String jarFilter = classPathEntry.getJarFilter();
@@ -81,7 +78,7 @@ public class DataEntryWriterFactory
         System.out.println("Preparing output " +
                            (isJar ? "jar" :
                             isWar ? "war" :
-                            isWar ? "ear" :
+                            isEar ? "ear" :
                             isZip ? "zip" :
                                     "directory") +
                            " [" + entryName + "]" +
@@ -107,7 +104,7 @@ public class DataEntryWriterFactory
         writer = filter != null?
             new FilteredDataEntryWriter(
             new DataEntryNameFilter(
-            new FileNameListMatcher(filter)),
+            new ListParser(new FileNameParser()).parse(filter)),
                 writer) :
             writer;
 
@@ -130,14 +127,14 @@ public class DataEntryWriterFactory
         // Zip up jars, if necessary.
         DataEntryWriter jarWriter = dontWrap ?
             (DataEntryWriter)new ParentDataEntryWriter(writer) :
-            (DataEntryWriter)new JarWriter(writer, null, ProGuard.VERSION);
+            (DataEntryWriter)new JarWriter(writer);
 
         // Add a filter, if specified.
         DataEntryWriter filteredJarWriter = jarFilter != null?
             new FilteredDataEntryWriter(
             new DataEntryParentFilter(
             new DataEntryNameFilter(
-            new FileNameListMatcher(jarFilter))),
+            new ListParser(new FileNameParser()).parse(jarFilter))),
                  jarWriter) :
             jarWriter;
 
@@ -149,4 +146,17 @@ public class DataEntryWriterFactory
                    filteredJarWriter,
                    isJar ? jarWriter : writer);
     }
+
+
+    /**
+     * Returns whether the given string ends with the given suffix, ignoring its
+     * case.
+     */
+    private static boolean endsWithIgnoreCase(String string, String suffix)
+    {
+        int stringLength = string.length();
+        int suffixLength = suffix.length();
+
+        return string.regionMatches(true, stringLength - suffixLength, suffix, 0, suffixLength);
+    }
 }
diff --git a/src/proguard/DescriptorKeepChecker.java b/src/proguard/DescriptorKeepChecker.java
new file mode 100644
index 0000000..18cc1a5
--- /dev/null
+++ b/src/proguard/DescriptorKeepChecker.java
@@ -0,0 +1,161 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.optimize.KeepMarker;
+
+import java.util.List;
+
+
+/**
+ * This class checks whether classes referenced by class members that are
+ * marked to be kept are marked to be kept too.
+ *
+ * @author Eric Lafortune
+ */
+public class DescriptorKeepChecker
+extends      SimplifiedVisitor
+implements   MemberVisitor,
+             ClassVisitor
+{
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter notePrinter;
+
+    // Some fields acting as parameters for the class visitor.
+    private Clazz   referencingClass;
+    private Member  referencingMember;
+    private boolean isField;
+
+
+    /**
+     * Creates a new DescriptorKeepChecker.
+     */
+    public DescriptorKeepChecker(ClassPool      programClassPool,
+                                 ClassPool      libraryClassPool,
+                                 WarningPrinter notePrinter)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.notePrinter      = notePrinter;
+    }
+
+
+    /**
+     * Checks the classes mentioned in the given keep specifications, printing
+     * notes if necessary. Returns the number of notes printed.
+     */
+    public void checkClassSpecifications(List keepSpecifications)
+    {
+        // Clean up any old visitor info.
+        programClassPool.classesAccept(new ClassCleaner());
+        libraryClassPool.classesAccept(new ClassCleaner());
+
+        // Create a visitor for marking the seeds.
+        KeepMarker keepMarker = new KeepMarker();
+        ClassPoolVisitor classPoolvisitor =
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(keepSpecifications,
+                                                                    keepMarker,
+                                                                    keepMarker,
+                                                                    false,
+                                                                    true,
+                                                                    true);
+        // Mark the seeds.
+        programClassPool.accept(classPoolvisitor);
+        libraryClassPool.accept(classPoolvisitor);
+
+        // Print out notes about argument types that are not being kept.
+        programClassPool.classesAccept(new AllMemberVisitor(this));
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (KeepMarker.isKept(programField))
+        {
+            referencingClass  = programClass;
+            referencingMember = programField;
+            isField           = true;
+
+            // Don't check the type, because it is not required for introspection.
+            //programField.referencedClassesAccept(this);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (KeepMarker.isKept(programMethod))
+        {
+            referencingClass  = programClass;
+            referencingMember = programMethod;
+            isField           = false;
+
+            // Don't check the return type, because it is not required for
+            // introspection (e.g. the return type of the special Enum methods).
+            //programMethod.referencedClassesAccept(this);
+
+            Clazz[] referencedClasses = programMethod.referencedClasses;
+            if (referencedClasses != null)
+            {
+                for (int index = 0; index < referencedClasses.length-1; index++)
+                {
+                    if (referencedClasses[index] != null)
+                    {
+                        referencedClasses[index].accept(this);
+                    }
+                }
+            }
+        }
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (!KeepMarker.isKept(programClass))
+        {
+            notePrinter.print("Note: the configuration keeps the entry point '" +
+                              ClassUtil.externalClassName(referencingClass.getName()) +
+                              " { " +
+                              (isField ?
+                                   ClassUtil.externalFullFieldDescription(0,
+                                                                          referencingMember.getName(referencingClass),
+                                                                          referencingMember.getDescriptor(referencingClass)) :
+                                   ClassUtil.externalFullMethodDescription(referencingClass.getName(),
+                                                                           0,
+                                                                           referencingMember.getName(referencingClass),
+                                                                           referencingMember.getDescriptor(referencingClass))) +
+                              "; }', but not the descriptor class '" +
+                              ClassUtil.externalClassName(programClass.getName()) +
+                              "'");
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass) {}
+}
diff --git a/src/proguard/DuplicateClassFilePrinter.java b/src/proguard/DuplicateClassPrinter.java
similarity index 63%
rename from src/proguard/DuplicateClassFilePrinter.java
rename to src/proguard/DuplicateClassPrinter.java
index e5a8a79..33295f3 100644
--- a/src/proguard/DuplicateClassFilePrinter.java
+++ b/src/proguard/DuplicateClassPrinter.java
@@ -1,6 +1,6 @@
-/* $Id: DuplicateClassFilePrinter.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,42 +20,42 @@
  */
 package proguard;
 
-import proguard.classfile.visitor.ClassFileVisitor;
 import proguard.classfile.*;
 import proguard.classfile.util.*;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassFileVisitor writes out notes about the class files that it visits
+ * This ClassVisitor writes out notes about the class files that it visits
  * being duplicates.
  *
  * @author Eric Lafortune
  */
-public class DuplicateClassFilePrinter implements ClassFileVisitor
+public class DuplicateClassPrinter implements ClassVisitor
 {
-    private WarningPrinter notePrinter;
+    private final WarningPrinter notePrinter;
 
 
     /**
-     * Creates a new DuplicateClassFileVisitor.
+     * Creates a new DuplicateClassVisitor.
      */
-    public DuplicateClassFilePrinter(WarningPrinter notePrinter)
+    public DuplicateClassPrinter(WarningPrinter notePrinter)
     {
         this.notePrinter = notePrinter;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         notePrinter.print("Note: duplicate definition of program class [" +
-                          ClassUtil.externalClassName(programClassFile.getName()) + "]");
+                          ClassUtil.externalClassName(programClass.getName()) + "]");
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         notePrinter.print("Note: duplicate definition of library class [" +
-                          ClassUtil.externalClassName(libraryClassFile.getName()) + "]");
+                          ClassUtil.externalClassName(libraryClass.getName()) + "]");
     }
 }
diff --git a/src/proguard/FileWordReader.java b/src/proguard/FileWordReader.java
index ffa64ab..0f8cd57 100644
--- a/src/proguard/FileWordReader.java
+++ b/src/proguard/FileWordReader.java
@@ -1,6 +1,6 @@
-/* $Id: FileWordReader.java,v 1.12.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import java.net.URL;
  */
 public class FileWordReader extends WordReader
 {
-    private String           name;
+    private final String           name;
     private LineNumberReader reader;
 
 
@@ -56,8 +56,8 @@ public class FileWordReader extends WordReader
     {
         super(null);
 
-        this.name    = url.toString();
-        this.reader  = new LineNumberReader(
+        this.name   = url.toString();
+        this.reader = new LineNumberReader(
                        new BufferedReader(
                        new InputStreamReader(url.openStream())));
     }
diff --git a/src/proguard/FullyQualifiedClassNameChecker.java b/src/proguard/FullyQualifiedClassNameChecker.java
new file mode 100644
index 0000000..c58bcf7
--- /dev/null
+++ b/src/proguard/FullyQualifiedClassNameChecker.java
@@ -0,0 +1,188 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.util.List;
+
+/**
+ * This class checks if the user has forgotten to fully qualify any classes
+ * in the configuration.
+ *
+ * @author Eric Lafortune
+ */
+public class FullyQualifiedClassNameChecker
+extends      SimplifiedVisitor
+implements   ClassVisitor
+{
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter notePrinter;
+
+
+    /**
+     * Creates a new DescriptorKeepChecker.
+     */
+    public FullyQualifiedClassNameChecker(ClassPool      programClassPool,
+                                          ClassPool      libraryClassPool,
+                                          WarningPrinter notePrinter)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.notePrinter      = notePrinter;
+    }
+
+
+    /**
+     * Checks the classes mentioned in the given class specifications, printing
+     * notes if necessary. Returns the number of notes printed.
+     */
+    public void checkClassSpecifications(List classSpecifications)
+    {
+        if (classSpecifications != null)
+        {
+            for (int index = 0; index < classSpecifications.size(); index++)
+            {
+                ClassSpecification classSpecification =
+                    (ClassSpecification)classSpecifications.get(index);
+
+                checkType(classSpecification.annotationType);
+                checkClassName(classSpecification.className);
+                checkType(classSpecification.extendsAnnotationType);
+                checkClassName(classSpecification.extendsClassName);
+
+                checkMemberSpecifications(classSpecification.fieldSpecifications,  true);
+                checkMemberSpecifications(classSpecification.methodSpecifications, false);
+            }
+        }
+    }
+
+
+    /**
+     * Checks the classes mentioned in the given class member specifications,
+     * printing notes if necessary.
+     */
+    private void checkMemberSpecifications(List memberSpecifications, boolean isField)
+    {
+        if (memberSpecifications != null)
+        {
+            for (int index = 0; index < memberSpecifications.size(); index++)
+            {
+                MemberSpecification memberSpecification =
+                    (MemberSpecification)memberSpecifications.get(index);
+
+                checkType(memberSpecification.annotationType);
+
+                if (isField)
+                {
+                     checkType(memberSpecification.descriptor);
+                }
+                else
+                {
+                    checkDescriptor(memberSpecification.descriptor);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Checks the classes mentioned in the given class member descriptor,
+     * printing notes if necessary.
+     */
+    private void checkDescriptor(String descriptor)
+    {
+        if (descriptor != null)
+        {
+            InternalTypeEnumeration internalTypeEnumeration =
+                new InternalTypeEnumeration(descriptor);
+
+            checkType(internalTypeEnumeration.returnType());
+
+            while (internalTypeEnumeration.hasMoreTypes())
+            {
+                checkType(internalTypeEnumeration.nextType());
+            }
+        }
+    }
+
+
+    /**
+     * Checks the class mentioned in the given type (if any),
+     * printing notes if necessary.
+     */
+    private void checkType(String type)
+    {
+        if (type != null)
+        {
+            checkClassName(ClassUtil.internalClassNameFromType(type));
+        }
+    }
+
+
+    /**
+     * Checks the specified class (if any),
+     * printing notes if necessary.
+     */
+    private void checkClassName(String className)
+    {
+        if (className != null                            &&
+            !containsWildCards(className)                &&
+            programClassPool.getClass(className) == null &&
+            libraryClassPool.getClass(className) == null)
+        {
+            notePrinter.print("Note: the configuration refers to the unknown class '" +
+                              ClassUtil.externalClassName(className) + "'");
+
+            String fullyQualifiedClassName =
+                "**" + ClassConstants.INTERNAL_PACKAGE_SEPARATOR +
+                className.substring(className.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR)+1);
+
+            ClassNameFilter classNameFilter =
+                new ClassNameFilter(fullyQualifiedClassName, this);
+
+            programClassPool.classesAccept(classNameFilter);
+            libraryClassPool.classesAccept(classNameFilter);
+        }
+    }
+
+
+    private static boolean containsWildCards(String string)
+    {
+        return string != null &&
+            (string.indexOf('*')   >= 0 ||
+             string.indexOf('?')   >= 0 ||
+             string.indexOf(',')   >= 0 ||
+             string.indexOf("///") >= 0);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitAnyClass(Clazz clazz)
+    {
+        System.out.println("      Maybe you meant the fully qualified name '" +
+                           ClassUtil.externalClassName(clazz.getName()) + "'?");
+    }
+}
diff --git a/src/proguard/GPL.java b/src/proguard/GPL.java
index 4884f12..f7249bb 100644
--- a/src/proguard/GPL.java
+++ b/src/proguard/GPL.java
@@ -1,6 +1,6 @@
-/* $Id: GPL.java,v 1.5.2.6 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -90,6 +90,7 @@ public class GPL
         }
         catch (IOException ex)
         {
+            // We'll just stop looking for more names.
         }
 
         return packageNames;
@@ -166,6 +167,7 @@ public class GPL
                packageName.startsWith("org.eclipse")            ||
                packageName.startsWith("org.netbeans")           ||
                packageName.startsWith("com.sun.kvem")           ||
+               packageName.startsWith("eclipseme")              ||
                packageName.startsWith("jg.j2me")                ||
                packageName.startsWith("jg.common")              ||
                packageName.startsWith("jg.buildengine");
diff --git a/src/proguard/Initializer.java b/src/proguard/Initializer.java
index a51a46c..e7e6d3b 100644
--- a/src/proguard/Initializer.java
+++ b/src/proguard/Initializer.java
@@ -1,6 +1,6 @@
-/* $Id: Initializer.java,v 1.2.2.11 2007/02/03 09:05:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,12 @@
  */
 package proguard;
 
-import proguard.classfile.*;
-import proguard.classfile.attribute.AllAttrInfoVisitor;
-import proguard.classfile.instruction.AllInstructionVisitor;
+import proguard.classfile.ClassPool;
+import proguard.classfile.attribute.visitor.AllAttributeVisitor;
+import proguard.classfile.instruction.visitor.AllInstructionVisitor;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
-import proguard.util.ClassNameListMatcher;
+import proguard.util.*;
 
 import java.io.IOException;
 import java.util.*;
@@ -37,7 +37,7 @@ import java.util.*;
  */
 public class Initializer
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -59,104 +59,195 @@ public class Initializer
     {
         int originalLibraryClassPoolSize = libraryClassPool.size();
 
-        // Initialize the class hierarchy for program classes.
+        // Initialize the superclass hierarchy for program classes.
         WarningPrinter hierarchyWarningPrinter = configuration.warn ?
             new WarningPrinter(System.err) :
             null;
 
-        programClassPool.classFilesAccept(
-            new ClassFileHierarchyInitializer(programClassPool,
-                                              libraryClassPool,
-                                              hierarchyWarningPrinter));
+        programClassPool.classesAccept(
+            new ClassSuperHierarchyInitializer(programClassPool,
+                                               libraryClassPool,
+                                               hierarchyWarningPrinter));
 
-        // Initialize the class hierarchy for library classes.
-        libraryClassPool.classFilesAccept(
-            new ClassFileHierarchyInitializer(programClassPool,
-                                              libraryClassPool,
-                                              null));
+        // Initialize the superclass hierarchy for library classes.
+        libraryClassPool.classesAccept(
+            new ClassSuperHierarchyInitializer(programClassPool,
+                                               libraryClassPool,
+                                               null));
 
         // Initialize the Class.forName and .class references.
         WarningPrinter classForNameNotePrinter = configuration.note ?
             new WarningPrinter(System.out) :
             null;
 
-        programClassPool.classFilesAccept(
+        programClassPool.classesAccept(
             new AllMethodVisitor(
-            new AllAttrInfoVisitor(
+            new AllAttributeVisitor(
             new AllInstructionVisitor(
-            new ClassFileClassForNameReferenceInitializer(programClassPool,
-                                                          libraryClassPool,
-                                                          classForNameNotePrinter,
-                                                          createNoteExceptionMatcher(configuration.keep))))));
+            new DynamicClassReferenceInitializer(programClassPool,
+                                                 libraryClassPool,
+                                                 classForNameNotePrinter,
+                                                 createClassNoteExceptionMatcher(configuration.keep))))));
 
-        // Initialize the class references from program class members and
-        // attributes.
+        // Initialize the class references of program class members and attributes.
         WarningPrinter referenceWarningPrinter = configuration.warn ?
             new WarningPrinter(System.err) :
             null;
 
-        programClassPool.classFilesAccept(
-            new ClassFileReferenceInitializer(programClassPool,
-                                              libraryClassPool,
-                                              referenceWarningPrinter));
+        programClassPool.classesAccept(
+            new ClassReferenceInitializer(programClassPool,
+                                          libraryClassPool,
+                                          referenceWarningPrinter));
 
-        if (configuration.applyMapping == null &&
-            !configuration.useUniqueClassMemberNames)
+        // Initialize the Class.get[Declared]{Field,Method} references.
+        WarningPrinter getMemberNotePrinter = configuration.note ?
+            new WarningPrinter(System.out) :
+            null;
+
+        programClassPool.classesAccept(
+            new AllMethodVisitor(
+            new AllAttributeVisitor(
+            new AllInstructionVisitor(
+            new DynamicMemberReferenceInitializer(programClassPool,
+                                                  libraryClassPool,
+                                                  getMemberNotePrinter,
+                                                  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)
         {
-            // Reconstruct a library class pool with only those library classes
-            // whose hierarchies are referenced by the program classes.
-            libraryClassPool.clear();
-            programClassPool.classFilesAccept(
-                new ReferencedClassFileVisitor(
-                new LibraryClassFileFilter(
-                new ClassFileHierarchyTraveler(true, true, true, false,
-                new LibraryClassFileFilter(
-                new ClassPoolFiller(libraryClassPool))))));
+            new FullyQualifiedClassNameChecker(programClassPool,
+                                               libraryClassPool,
+                                               fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep);
+        }
+
+        if (descriptorKeepNotePrinter != null)
+        {
+            new DescriptorKeepChecker(programClassPool,
+                                      libraryClassPool,
+                                      descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep);
+        }
+
+        // Reconstruct a library class pool with only those library classes
+        // whose hierarchies are referenced by the program classes. We can't do
+        // this if we later have to come up with the obfuscated class member
+        // names that are globally unique.
+        if (configuration.useUniqueClassMemberNames)
+        {
+            // Initialize the class references of library class members.
+            libraryClassPool.classesAccept(
+                new ClassReferenceInitializer(programClassPool,
+                                              libraryClassPool,
+                                              null));
         }
+        else
+        {
+            ClassPool referencedLibraryClassPool = new ClassPool();
 
-        // Initialize the class references from library class members.
-        libraryClassPool.classFilesAccept(
-            new ClassFileReferenceInitializer(programClassPool,
+            // Collect the library classes that are referenced by program
+            // classes.
+            programClassPool.classesAccept(
+                new ReferencedClassVisitor(
+                new LibraryClassFilter(
+                new ClassHierarchyTraveler(true, true, true, false,
+                new LibraryClassFilter(
+                new ClassPoolFiller(referencedLibraryClassPool))))));
+
+            // Initialize the class references of library class members.
+            referencedLibraryClassPool.classesAccept(
+                new ClassReferenceInitializer(programClassPool,
                                               libraryClassPool,
                                               null));
 
+            // Reset the library class pool.
+            libraryClassPool.clear();
+
+            // Copy the library classes that are referenced directly by program
+            // classes and the library classes that are referenced by referenced
+            // library classes.
+            referencedLibraryClassPool.classesAccept(
+                new MultiClassVisitor(new ClassVisitor[]
+                {
+                    new ClassHierarchyTraveler(true, true, true, false,
+                    new LibraryClassFilter(
+                    new ClassPoolFiller(libraryClassPool))),
+
+                    new ReferencedClassVisitor(
+                    new LibraryClassFilter(
+                    new ClassHierarchyTraveler(true, true, true, false,
+                    new LibraryClassFilter(
+                    new ClassPoolFiller(libraryClassPool)))))
+                }));
+        }
+
+        // Initialize the subclass hierarchies.
+        programClassPool.classesAccept(new ClassSubHierarchyInitializer());
+        libraryClassPool.classesAccept(new ClassSubHierarchyInitializer());
+
+        // Share strings between the classes, to reduce heap memory usage.
+        programClassPool.classesAccept(new StringSharer());
+        libraryClassPool.classesAccept(new StringSharer());
+
         // Print out a summary of the notes, if necessary.
-        if (configuration.note)
+        if (fullyQualifiedClassNameNotePrinter != null)
+        {
+            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.");
+            }
+        }
+
+        if (descriptorKeepNotePrinter != null)
+        {
+            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').");
+            }
+        }
+
+        if (classForNameNotePrinter != null)
         {
             int classForNameNoteCount = classForNameNotePrinter.getWarningCount();
             if (classForNameNoteCount > 0)
             {
-                System.err.println("Note: there were " + classForNameNoteCount +
+                System.out.println("Note: there were " + classForNameNoteCount +
                                    " class casts of dynamically created class instances.");
-                System.err.println("      You might consider explicitly keeping the mentioned classes and/or");
-                System.err.println("      their implementations (using '-keep').");
+                System.out.println("      You might consider explicitly keeping the mentioned classes and/or");
+                System.out.println("      their implementations (using '-keep').");
             }
+        }
 
-            if (configuration.optimize ||
-                configuration.obfuscate)
+        if (getMemberNotePrinter != null)
+        {
+            int getmemberNoteCount = getMemberNotePrinter.getWarningCount();
+            if (getmemberNoteCount > 0)
             {
-                // Check for Java 6 files.
-                ClassFileVersionCounter classFileVersionCounter =
-                    new ClassFileVersionCounter(ClassConstants.MAJOR_VERSION_MAX,
-                                                ClassConstants.MINOR_VERSION_MAX);
-
-                programClassPool.classFilesAccept(classFileVersionCounter);
-
-                int java6count = classFileVersionCounter.getCount();
-                if (java6count > 0)
-                {
-                    System.err.println("Note: there were " + java6count +
-                                       " Java 6 program classes.");
-                    System.err.println("      In order to obtain all of the improved start-up performance of Java 6,");
-                    System.err.println("      they should be preverified after having been optimized or obfuscated.");
-                    System.err.println("      Keep any eye on ProGuard version 4.0 for preverification support,");
-                    System.err.println("      at http://proguard.sourceforge.net/");
-                }
+                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 (configuration.warn)
+        if (hierarchyWarningPrinter != null &&
+            referenceWarningPrinter != null)
         {
             int hierarchyWarningCount = hierarchyWarningPrinter.getWarningCount();
             if (hierarchyWarningCount > 0)
@@ -183,8 +274,6 @@ public class Initializer
                  referenceWarningCount > 0) &&
                 !configuration.ignoreWarnings)
             {
-                System.err.println("         If you are sure the mentioned classes are not used anyway,");
-                System.err.println("         you could try your luck using the '-ignorewarnings' option.");
                 throw new IOException("Please correct the above warnings first.");
             }
         }
@@ -200,28 +289,28 @@ public class Initializer
 
 
     /**
-     * Extracts a list of exceptions for which not to print notes, from the
-     * keep configuration.
+     * Extracts a list of exceptions of classes for which not to print notes,
+     * from the keep configuration.
      */
-    private ClassNameListMatcher createNoteExceptionMatcher(List noteExceptions)
+    private StringMatcher createClassNoteExceptionMatcher(List noteExceptions)
     {
         if (noteExceptions != null)
         {
             List noteExceptionNames = new ArrayList(noteExceptions.size());
             for (int index = 0; index < noteExceptions.size(); index++)
             {
-                ClassSpecification classSpecification = (ClassSpecification)noteExceptions.get(index);
-                if (classSpecification.markClassFiles)
+                KeepSpecification keepSpecification = (KeepSpecification)noteExceptions.get(index);
+                if (keepSpecification.markClasses)
                 {
                     // If the class itself is being kept, it's ok.
-                    String className = classSpecification.className;
+                    String className = keepSpecification.className;
                     if (className != null)
                     {
                         noteExceptionNames.add(className);
                     }
 
                     // If all of its extensions are being kept, it's ok too.
-                    String extendsClassName = classSpecification.extendsClassName;
+                    String extendsClassName = keepSpecification.extendsClassName;
                     if (extendsClassName != null)
                     {
                         noteExceptionNames.add(extendsClassName);
@@ -231,7 +320,50 @@ public class Initializer
 
             if (noteExceptionNames.size() > 0)
             {
-                return new ClassNameListMatcher(noteExceptionNames);
+                return new ListParser(new ClassNameParser()).parse(noteExceptionNames);
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Extracts a list of exceptions of field or method names for which not to
+     * print notes, from the keep configuration.
+     */
+    private StringMatcher createClassMemberNoteExceptionMatcher(List    noteExceptions,
+                                                                boolean isField)
+    {
+        if (noteExceptions != null)
+        {
+            List noteExceptionNames = new ArrayList();
+            for (int index = 0; index < noteExceptions.size(); index++)
+            {
+                KeepSpecification keepSpecification = (KeepSpecification)noteExceptions.get(index);
+                List memberSpecifications = isField ?
+                    keepSpecification.fieldSpecifications :
+                    keepSpecification.methodSpecifications;
+
+                if (memberSpecifications != null)
+                {
+                    for (int index2 = 0; index2 < memberSpecifications.size(); index2++)
+                    {
+                        MemberSpecification memberSpecification =
+                            (MemberSpecification)memberSpecifications.get(index2);
+
+                        String memberName = memberSpecification.name;
+                        if (memberName != null)
+                        {
+                            noteExceptionNames.add(memberName);
+                        }
+                    }
+                }
+            }
+
+            if (noteExceptionNames.size() > 0)
+            {
+                return new ListParser(new ClassNameParser()).parse(noteExceptionNames);
             }
         }
 
diff --git a/src/proguard/InputReader.java b/src/proguard/InputReader.java
index 9ae0e2f..eda47c8 100644
--- a/src/proguard/InputReader.java
+++ b/src/proguard/InputReader.java
@@ -1,6 +1,6 @@
-/* $Id: InputReader.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -34,7 +34,7 @@ import java.io.IOException;
  */
 public class InputReader
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -54,12 +54,6 @@ public class InputReader
     public void execute(ClassPool programClassPool,
                         ClassPool libraryClassPool) throws IOException
     {
-        // Check if we have at least some program jars.
-        if (configuration.programJars == null)
-        {
-            throw new IOException("The input is empty. You have to specify one or more '-injars' options.");
-        }
-
         WarningPrinter warningPrinter = configuration.warn ?
             new WarningPrinter(System.err) :
             null;
@@ -68,22 +62,28 @@ public class InputReader
             new WarningPrinter(System.out) :
             null;
 
-        DuplicateClassFilePrinter duplicateClassFilePrinter = configuration.note ?
-            new DuplicateClassFilePrinter(notePrinter) :
+        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");
+        }
+
         // Read the program class files.
         // Prepare a data entry reader to filter all classes,
         // which are then decoded to classes by a class reader,
         // which are then put in the class pool by a class pool filler.
         readInput("Reading program ",
                   configuration.programJars,
-                  new ClassFileFilter(
-                  new ClassFileReader(false,
-                                      configuration.skipNonPublicLibraryClasses,
-                                      configuration.skipNonPublicLibraryClassMembers,
-                                      warningPrinter,
-                  new ClassFilePresenceFilter(programClassPool, duplicateClassFilePrinter,
+                  new ClassFilter(
+                  new ClassReader(false,
+                                  configuration.skipNonPublicLibraryClasses,
+                                  configuration.skipNonPublicLibraryClassMembers,
+                                  warningPrinter,
+                  new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
                   new ClassPoolFiller(programClassPool)))));
 
         // Check if we have at least some input classes.
@@ -100,18 +100,18 @@ public class InputReader
             // which are then put in the class pool by a class pool filler.
             readInput("Reading library ",
                       configuration.libraryJars,
-                      new ClassFileFilter(
-                      new ClassFileReader(true,
-                                          configuration.skipNonPublicLibraryClasses,
-                                          configuration.skipNonPublicLibraryClassMembers,
-                                          warningPrinter,
-                      new ClassFilePresenceFilter(programClassPool, duplicateClassFilePrinter,
-                      new ClassFilePresenceFilter(libraryClassPool, duplicateClassFilePrinter,
+                      new ClassFilter(
+                      new ClassReader(true,
+                                      configuration.skipNonPublicLibraryClasses,
+                                      configuration.skipNonPublicLibraryClassMembers,
+                                      warningPrinter,
+                      new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
+                      new ClassPresenceFilter(libraryClassPool, duplicateClassPrinter,
                       new ClassPoolFiller(libraryClassPool))))));
         }
 
         // Print out a summary of the notes, if necessary.
-        if (configuration.note)
+        if (notePrinter != null)
         {
             int noteCount = notePrinter.getWarningCount();
             if (noteCount > 0)
@@ -122,7 +122,7 @@ public class InputReader
         }
 
         // Print out a summary of the warnings, if necessary.
-        if (configuration.warn)
+        if (warningPrinter != null)
         {
             int warningCount = warningPrinter.getWarningCount();
             if (warningCount > 0)
diff --git a/src/proguard/KeepSpecification.java b/src/proguard/KeepSpecification.java
new file mode 100644
index 0000000..dcd4ec8
--- /dev/null
+++ b/src/proguard/KeepSpecification.java
@@ -0,0 +1,138 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+/**
+ * This class stores a specification of keep option, with a purpose and a class
+ * specification.
+ *
+ * @author Eric Lafortune
+ */
+public class KeepSpecification extends ClassSpecification
+{
+    public final boolean markClasses;
+    public final boolean markConditionally;
+    public final boolean allowShrinking;
+    public final boolean allowOptimization;
+    public final boolean allowObfuscation;
+
+
+    /**
+     * Creates a new KeepSpecification 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.
+     * @param markConditionally  specifies whether to mark the classes and
+     *                           class members conditionally. If true, classes
+     *                           and class members are marked, on the condition
+     *                           that all specified class members are present.
+     * @param allowShrinking     specifies whether shrinking is allowed.
+     * @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)
+    {
+        this.markClasses       = markClasses;
+        this.markConditionally = markConditionally;
+        this.allowShrinking    = allowShrinking;
+        this.allowOptimization = allowOptimization;
+        this.allowObfuscation  = allowObfuscation;
+    }
+
+
+    /**
+     * Creates a new KeepSpecification.
+     * @param markClasses        specifies whether to mark the classes.
+     *                           If false, only class members are marked.
+     *                           If true, the classes are marked as well.
+     * @param markConditionally  specifies whether to mark the classes and
+     *                           class members conditionally. If true, classes
+     *                           and class members are marked, on the condition
+     *                           that all specified class members are present.
+     * @param allowShrinking     specifies whether shrinking is allowed.
+     * @param allowOptimization  specifies whether optimization is allowed.
+     * @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)
+    {
+        super(classSpecification);
+
+        this.markClasses       = markClasses;
+        this.markConditionally = markConditionally;
+        this.allowShrinking    = allowShrinking;
+        this.allowOptimization = allowOptimization;
+        this.allowObfuscation  = allowObfuscation;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        KeepSpecification other = (KeepSpecification)object;
+        return
+            this.markClasses       == other.markClasses       &&
+            this.markConditionally == other.markConditionally &&
+            this.allowShrinking    == other.allowShrinking    &&
+            this.allowOptimization == other.allowOptimization &&
+            this.allowObfuscation  == other.allowObfuscation  &&
+            super.equals(other);
+    }
+
+    public int hashCode()
+    {
+        return
+            (markClasses       ? 0 :  1) ^
+            (markConditionally ? 0 :  2) ^
+            (allowShrinking    ? 0 :  4) ^
+            (allowOptimization ? 0 :  8) ^
+            (allowObfuscation  ? 0 : 16) ^
+            super.hashCode();
+    }
+
+    public Object clone()
+    {
+//        try
+//        {
+            return super.clone();
+//        }
+//        catch (CloneNotSupportedException e)
+//        {
+//            return null;
+//        }
+    }
+}
diff --git a/src/proguard/ClassMemberSpecification.java b/src/proguard/MemberSpecification.java
similarity index 56%
rename from src/proguard/ClassMemberSpecification.java
rename to src/proguard/MemberSpecification.java
index 0e4d0ef..388fd37 100644
--- a/src/proguard/ClassMemberSpecification.java
+++ b/src/proguard/MemberSpecification.java
@@ -1,6 +1,6 @@
-/* $Id: ClassMemberSpecification.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,22 +27,24 @@ package proguard;
  *
  * @author Eric Lafortune
  */
-public class ClassMemberSpecification
+public class MemberSpecification
 {
     public int    requiredSetAccessFlags;
     public int    requiredUnsetAccessFlags;
-    public String name;
-    public String descriptor;
+    public final String annotationType;
+    public final String name;
+    public final String descriptor;
 
 
     /**
-     * Creates a new option to keep the specified class member(s).
+     * Creates a new option to keep all possible class members.
      */
-    public ClassMemberSpecification()
+    public MemberSpecification()
     {
         this(0,
              0,
              null,
+             null,
              null);
     }
 
@@ -54,6 +56,10 @@ public class ClassMemberSpecification
      *                                 in order for the class to apply.
      * @param requiredUnsetAccessFlags the class access flags that must be unset
      *                                 in order for the class to apply.
+     * @param annotationType           the name of the class that must be an
+     *                                 annotation in order for the class member
+     *                                 to apply. The name may be null to specify
+     *                                 that no annotation is required.
      * @param name                     the class member name. The name may be
      *                                 null to specify any class member or it
      *                                 may contain "*" or "?" wildcards.
@@ -62,13 +68,15 @@ public class ClassMemberSpecification
      *                                 class member or it may contain
      *                                 "**", "*", or "?" wildcards.
      */
-    public ClassMemberSpecification(int    requiredSetAccessFlags,
-                                 int    requiredUnsetAccessFlags,
-                                 String name,
-                                 String descriptor)
+    public MemberSpecification(int    requiredSetAccessFlags,
+                               int    requiredUnsetAccessFlags,
+                               String annotationType,
+                               String name,
+                               String descriptor)
     {
         this.requiredSetAccessFlags   = requiredSetAccessFlags;
         this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
+        this.annotationType           = annotationType;
         this.name                     = name;
         this.descriptor               = descriptor;
     }
@@ -79,25 +87,28 @@ public class ClassMemberSpecification
 
     public boolean equals(Object object)
     {
-        if (this.getClass() != object.getClass())
+        if (object == null ||
+            this.getClass() != object.getClass())
         {
             return false;
         }
 
-        ClassMemberSpecification other = (ClassMemberSpecification)object;
+        MemberSpecification other = (MemberSpecification)object;
         return
-            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags  ) &&
-            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags) &&
-            (this.name       == null ? other.name       == null : this.name.equals(other.name)            ) &&
-            (this.descriptor == null ? other.descriptor == null : this.descriptor.equals(other.descriptor));
+            (this.requiredSetAccessFlags   == other.requiredSetAccessFlags                                                                      ) &&
+            (this.requiredUnsetAccessFlags == other.requiredUnsetAccessFlags                                                                    ) &&
+            (this.annotationType == null ? other.annotationType == null : this.annotationType.equals(other.annotationType)) &&
+            (this.name           == null ? other.name           == null : this.name.equals(other.name)                    ) &&
+            (this.descriptor     == null ? other.descriptor     == null : this.descriptor.equals(other.descriptor)        );
     }
 
     public int hashCode()
     {
         return
-            requiredSetAccessFlags                           ^
-            requiredUnsetAccessFlags                         ^
-            (name       == null ? 0 : name.hashCode()      ) ^
-            (descriptor == null ? 0 : descriptor.hashCode());
+            (requiredSetAccessFlags                                ) ^
+            (requiredUnsetAccessFlags                              ) ^
+            (annotationType == null ? 0 : annotationType.hashCode()) ^
+            (name           == null ? 0 : name.hashCode()          ) ^
+            (descriptor     == null ? 0 : descriptor.hashCode()    );
     }
 }
diff --git a/src/proguard/OutputWriter.java b/src/proguard/OutputWriter.java
index f1a62ee..3e231ad 100644
--- a/src/proguard/OutputWriter.java
+++ b/src/proguard/OutputWriter.java
@@ -1,6 +1,6 @@
-/* $Id: OutputWriter.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,6 +22,7 @@ package proguard;
 
 import proguard.classfile.ClassPool;
 import proguard.io.*;
+import proguard.util.*;
 
 import java.io.IOException;
 
@@ -32,7 +33,7 @@ import java.io.IOException;
  */
 public class OutputWriter
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -148,12 +149,48 @@ public class OutputWriter
                                                              fromOutputIndex,
                                                              toOutputIndex);
 
-            // Create the reader that can write classes and copy resource
+            // The writer will be used to write possibly obfuscated class files.
+            DataEntryReader classRewriter =
+                new ClassRewriter(programClassPool, writer);
+
+            // The writer will also be used to write resource files.
+            DataEntryReader resourceRewriter =
+                new DataEntryCopier(writer);
+
+            // 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;
+            }
+
+            // 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;
+            }
+
+            // Create the reader that can write class files and copy resource
             // files to the above writer.
             DataEntryReader reader =
-                new ClassFileFilter(new ClassFileRewriter(programClassPool,
-                                                          writer),
-                                    new DataEntryCopier(writer));
+                new ClassFilter(classRewriter, resourceRewriter);
 
             // Go over the specified input entries and write their processed
             // versions.
diff --git a/src/proguard/ParseException.java b/src/proguard/ParseException.java
index a4e5c24..98f1889 100644
--- a/src/proguard/ParseException.java
+++ b/src/proguard/ParseException.java
@@ -1,6 +1,6 @@
-/* $Id: ParseException.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -40,8 +40,8 @@ public class ParseException extends Exception {
     /**
      * Constructs a <code>ParseException</code> with the specified detail
      * message. The error message string <code>s</code> can later be
-     * retrieved by the <code>{@link java.lang.Throwable#getMessage}</code>
-     * method of class <code>java.lang.Throwable</code>.
+     * retrieved by the <code>{@link Throwable#getMessage}</code>
+     * method of class <code>Throwable</code>.
      *
      * @param s the detail message.
      */
diff --git a/src/proguard/ProGuard.java b/src/proguard/ProGuard.java
index 8b39a65..a10dadf 100644
--- a/src/proguard/ProGuard.java
+++ b/src/proguard/ProGuard.java
@@ -1,6 +1,6 @@
-/* $Id: ProGuard.java,v 1.101.2.16 2007/02/28 23:26:08 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -9,7 +9,6 @@
  * 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
@@ -22,26 +21,27 @@
 package proguard;
 
 import proguard.classfile.ClassPool;
-import proguard.classfile.editor.ConstantPoolSorter;
+import proguard.classfile.editor.ClassElementSorter;
 import proguard.classfile.visitor.*;
 import proguard.obfuscate.Obfuscator;
 import proguard.optimize.Optimizer;
+import proguard.preverify.*;
 import proguard.shrink.Shrinker;
 
 import java.io.*;
 
 /**
- * Tool for shrinking, optimizing, and obfuscating Java class files.
+ * Tool for shrinking, optimizing, obfuscating, and preverifying Java classes.
  *
  * @author Eric Lafortune
  */
 public class ProGuard
 {
-    public static final String VERSION = "ProGuard, version 3.9";
+    public static final String VERSION = "ProGuard, version 4.1";
 
-    private Configuration configuration;
-    private ClassPool     programClassPool = new ClassPool();
-    private ClassPool     libraryClassPool = new ClassPool();
+    private final Configuration configuration;
+    private       ClassPool     programClassPool = new ClassPool();
+    private final ClassPool     libraryClassPool = new ClassPool();
 
 
     /**
@@ -63,21 +63,33 @@ public class ProGuard
 
         GPL.check();
 
-        readInput();
+        if (configuration.printConfiguration != null)
+        {
+            printConfiguration();
+        }
 
-        // The defaultPackage option implies the allowAccessModification option.
-        if (configuration.defaultPackage != null)
+        if (configuration.programJars != null     &&
+            configuration.programJars.hasOutput() &&
+            new UpToDateChecker(configuration).check())
         {
-            configuration.allowAccessModification = true;
+            return;
         }
 
-        if (configuration.shrink   ||
-            configuration.optimize ||
-            configuration.obfuscate)
+        readInput();
+
+        if (configuration.shrink    ||
+            configuration.optimize  ||
+            configuration.obfuscate ||
+            configuration.preverify)
         {
             initialize();
         }
 
+        if (configuration.targetClassVersion != 0)
+        {
+            target();
+        }
+
         if (configuration.printSeeds != null)
         {
             printSeeds();
@@ -88,18 +100,32 @@ public class ProGuard
             shrink();
         }
 
-        if (configuration.optimize)
+        if (configuration.preverify)
         {
-            optimize();
+            inlineSubroutines();
+        }
 
-            // Shrink again, if we may.
-            if (configuration.shrink)
+        if (configuration.optimize)
+        {
+            for (int optimizationPass = 0;
+                 optimizationPass < configuration.optimizationPasses;
+                 optimizationPass++)
             {
-                // Don't print any usage this time around.
-                configuration.printUsage       = null;
-                configuration.whyAreYouKeeping = null;
-
-                shrink();
+                if (!optimize())
+                {
+                    // Stop optimizing if the code doesn't improve any further.
+                    break;
+                }
+
+                // Shrink again, if we may.
+                if (configuration.shrink)
+                {
+                    // Don't print any usage this time around.
+                    configuration.printUsage       = null;
+                    configuration.whyAreYouKeeping = null;
+
+                    shrink();
+                }
             }
         }
 
@@ -108,11 +134,17 @@ public class ProGuard
             obfuscate();
         }
 
-        if (configuration.shrink   ||
-            configuration.optimize ||
-            configuration.obfuscate)
+        if (configuration.preverify)
         {
-            sortConstantPools();
+            preverify();
+        }
+
+        if (configuration.shrink    ||
+            configuration.optimize  ||
+            configuration.obfuscate ||
+            configuration.preverify)
+        {
+            sortClassElements();
         }
 
         if (configuration.programJars.hasOutput())
@@ -128,6 +160,28 @@ public class ProGuard
 
 
     /**
+     * Prints out the configuration that ProGuard is using.
+     */
+    private void printConfiguration() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Printing configuration to [" + fileName(configuration.printConfiguration) + "]...");
+        }
+
+        PrintStream ps = createPrintStream(configuration.printConfiguration);
+        try
+        {
+            new ConfigurationWriter(ps).write(configuration);
+        }
+        finally
+        {
+            closePrintStream(ps);
+        }
+    }
+
+
+    /**
      * Reads the input class files.
      */
     private void readInput() throws IOException
@@ -158,6 +212,20 @@ public class ProGuard
 
 
     /**
+     * Sets that target versions of the program classes.
+     */
+    private void target() throws IOException
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Setting target versions...");
+        }
+
+        new Targeter(configuration).execute(programClassPool);
+    }
+
+
+    /**
      * Prints out classes and class members that are used as seeds in the
      * shrinking and obfuscation steps.
      */
@@ -174,25 +242,28 @@ public class ProGuard
             throw new IOException("You have to specify '-keep' options for the shrinking step.");
         }
 
-        PrintStream ps = isFile(configuration.printSeeds) ?
-            new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printSeeds))) :
-            System.out;
-
-        // Create a visitor for printing out the seeds. Note that we're only
-        // printing out the program elements that are preserved against shrinking.
-        SimpleClassFilePrinter printer = new SimpleClassFilePrinter(false, ps);
-        ClassPoolVisitor classPoolvisitor =
-            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
-                                                                    new ProgramClassFileFilter(printer),
-                                                                    new ProgramMemberInfoFilter(printer));
-
-        // Print out the seeds.
-        programClassPool.accept(classPoolvisitor);
-        libraryClassPool.accept(classPoolvisitor);
-
-        if (ps != System.out)
+        PrintStream ps = createPrintStream(configuration.printSeeds);
+        try
+        {
+            // Create a visitor for printing out the seeds. We're  printing out
+            // the program elements that are preserved against shrinking,
+            // optimization, or obfuscation.
+            SimpleClassPrinter printer = new SimpleClassPrinter(false, ps);
+            ClassPoolVisitor classPoolvisitor =
+                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                        new ProgramClassFilter(printer),
+                                                                        new ProgramMemberFilter(printer),
+                                                                        true,
+                                                                        true,
+                                                                        true);
+
+            // Print out the seeds.
+            programClassPool.accept(classPoolvisitor);
+            libraryClassPool.accept(classPoolvisitor);
+        }
+        finally
         {
-            ps.close();
+            closePrintStream(ps);
         }
     }
 
@@ -215,61 +286,43 @@ public class ProGuard
             // We'll print out the usage, if requested.
             if (configuration.printUsage != null)
             {
-                System.out.println("Printing usage" +
-                                   (isFile(configuration.printUsage) ?
-                                       " to [" + configuration.printUsage.getAbsolutePath() + "]" :
-                                       "..."));
+                System.out.println("Printing usage to [" + fileName(configuration.printUsage) + "]...");
             }
         }
 
-        // Check if we have at least some keep commands.
-        if (configuration.keep == null)
-        {
-            throw new IOException("You have to specify '-keep' options for the shrinking step.");
-        }
-
-        int originalProgramClassPoolSize = programClassPool.size();
-
         // Perform the actual shrinking.
-        programClassPool = new Shrinker(configuration).execute(programClassPool, libraryClassPool);
+        programClassPool =
+            new Shrinker(configuration).execute(programClassPool, libraryClassPool);
+    }
 
-        // Check if we have at least some output class files.
-        int newProgramClassPoolSize = programClassPool.size();
-        if (newProgramClassPoolSize == 0)
-        {
-            throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
-        }
 
+    /**
+     * Performs the subroutine inlining step.
+     */
+    private void inlineSubroutines()
+    {
         if (configuration.verbose)
         {
-            System.out.println("Removing unused program classes and class elements...");
-            System.out.println("  Original number of program classes: " + originalProgramClassPoolSize);
-            System.out.println("  Final number of program classes:    " + newProgramClassPoolSize);
+            System.out.println("Inlining subroutines...");
         }
+
+        // Perform the actual inlining.
+        new SubroutineInliner(configuration).execute(programClassPool);
     }
 
 
     /**
      * Performs the optimization step.
      */
-    private void optimize() throws IOException
+    private boolean optimize() throws IOException
     {
         if (configuration.verbose)
         {
             System.out.println("Optimizing...");
         }
 
-        // Check if we have at least some keep commands.
-        if (configuration.keep         == null &&
-            configuration.keepNames    == null &&
-            configuration.applyMapping == null &&
-            configuration.printMapping == null)
-        {
-            throw new IOException("You have to specify '-keep' options for the optimization step.");
-        }
-
         // Perform the actual optimization.
-        new Optimizer(configuration).execute(programClassPool, libraryClassPool);
+        return new Optimizer(configuration).execute(programClassPool, libraryClassPool);
     }
 
 
@@ -285,16 +338,13 @@ public class ProGuard
             // We'll apply a mapping, if requested.
             if (configuration.applyMapping != null)
             {
-                System.out.println("Applying mapping [" + configuration.applyMapping.getAbsolutePath() + "]");
+                System.out.println("Applying mapping [" + fileName(configuration.applyMapping) + "]");
             }
 
             // We'll print out the mapping, if requested.
             if (configuration.printMapping != null)
             {
-                System.out.println("Printing mapping" +
-                                   (isFile(configuration.printMapping) ?
-                                       " to [" + configuration.printMapping.getAbsolutePath() + "]" :
-                                       "..."));
+                System.out.println("Printing mapping to [" + fileName(configuration.printMapping) + "]...");
             }
         }
 
@@ -304,12 +354,26 @@ public class ProGuard
 
 
     /**
-     * Sorts the constant pools of all program class files.
+     * Performs the preverification step.
+     */
+    private void preverify()
+    {
+        if (configuration.verbose)
+        {
+            System.out.println("Preverifying...");
+        }
+
+        // Perform the actual preverification.
+        new Preverifier(configuration).execute(programClassPool);
+    }
+
+
+    /**
+     * Sorts the elements of all program classes.
      */
-    private void sortConstantPools()
+    private void sortClassElements()
     {
-        // TODO: Avoid duplicate constant pool entries.
-        programClassPool.classFilesAccept(new ConstantPoolSorter(1024));
+        programClassPool.classesAccept(new ClassElementSorter());
     }
 
 
@@ -329,32 +393,70 @@ public class ProGuard
 
 
     /**
-     * Prints out the contents of the program class files.
+     * Prints out the contents of the program classes.
      */
     private void dump() throws IOException
     {
         if (configuration.verbose)
         {
-            System.out.println("Printing classes" +
-                               (isFile(configuration.dump) ?
-                                   " to [" + configuration.dump.getAbsolutePath() + "]" :
-                                   "..."));
+            System.out.println("Printing classes to [" + fileName(configuration.dump) + "]...");
         }
 
-        PrintStream ps = isFile(configuration.dump) ?
-            new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.dump))) :
+        PrintStream ps = createPrintStream(configuration.dump);
+        try
+        {
+            programClassPool.classesAccept(new ClassPrinter(ps));
+        }
+        finally
+        {
+            closePrintStream(ps);
+        }
+    }
+
+
+    /**
+     * Returns a print stream for the given file, or the standard output if
+     * the file name is empty.
+     */
+    private PrintStream createPrintStream(File file)
+    throws FileNotFoundException
+    {
+        return isFile(file) ?
+            new PrintStream(new BufferedOutputStream(new FileOutputStream(file))) :
             System.out;
+    }
 
-        programClassPool.classFilesAccept(new ClassFilePrinter(ps));
 
-        if (isFile(configuration.dump))
+    /**
+     * Closes the given print stream, or closes it if is the standard output.
+     * @param printStream
+     */
+    private void closePrintStream(PrintStream printStream)
+    {
+        if (printStream == System.out)
+        {
+            printStream.flush();
+        }
+        else
         {
-            ps.close();
+            printStream.close();
         }
     }
 
 
     /**
+     * Returns the absolute file name for the given file, or the standard output
+     * if the file name is empty.
+     */
+    private String fileName(File file)
+    {
+        return isFile(file) ?
+            file.getAbsolutePath() :
+            "standard output";
+    }
+
+
+    /**
      * Returns whether the given file is actually a file, or just a placeholder
      * for the standard output.
      */
@@ -387,14 +489,14 @@ public class ProGuard
             try
             {
                 parser.parse(configuration);
-
-                // Execute ProGuard with these options.
-                new ProGuard(configuration).execute();
             }
             finally
             {
                 parser.close();
             }
+
+            // Execute ProGuard with these options.
+            new ProGuard(configuration).execute();
         }
         catch (Exception ex)
         {
diff --git a/src/proguard/SubclassedClassFileFilter.java b/src/proguard/SubclassedClassFileFilter.java
deleted file mode 100644
index 15a8445..0000000
--- a/src/proguard/SubclassedClassFileFilter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/* $Id: SubclassedClassFileFilter.java,v 1.11.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This ClassFileVisitor delegates all its method calls to another
- * ClassFileVisitor, but only for ClassFile objects that are being subclassed.
- *
- * @author Eric Lafortune
- */
-class SubclassedClassFileFilter
-implements ClassFileVisitor
-{
-    ClassFileVisitor classFileVisitor;
-
-
-    public SubclassedClassFileFilter(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        if (programClassFile.subClasses != null)
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        if (libraryClassFile.subClasses != null)
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/NamedClassFileVisitor.java b/src/proguard/SubclassedClassFilter.java
similarity index 50%
rename from src/proguard/classfile/visitor/NamedClassFileVisitor.java
rename to src/proguard/SubclassedClassFilter.java
index 93c1edc..5ee9b1c 100644
--- a/src/proguard/classfile/visitor/NamedClassFileVisitor.java
+++ b/src/proguard/SubclassedClassFilter.java
@@ -1,6 +1,6 @@
-/* $Id: NamedClassFileVisitor.java,v 1.9.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,38 +18,45 @@
  * 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.visitor;
+package proguard;
 
 import proguard.classfile.*;
-
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This class visits ClassFile objects with the given name.
+ * This ClassVisitor delegates all its method calls to another ClassVisitor,
+ * but only for Clazz objects that are being subclassed.
  *
  * @author Eric Lafortune
  */
-public class NamedClassFileVisitor implements ClassPoolVisitor
+final class SubclassedClassFilter
+implements ClassVisitor
 {
-    private ClassFileVisitor classFileVisitor;
-    private String           name;
+    private final ClassVisitor classVisitor;
 
 
-    public NamedClassFileVisitor(ClassFileVisitor classFileVisitor,
-                                 String           name)
+    public SubclassedClassFilter(ClassVisitor classVisitor)
     {
-        this.classFileVisitor = classFileVisitor;
-        this.name             = name;
+        this.classVisitor = classVisitor;
     }
 
 
-    public void visitClassPool(ClassPool classPool)
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
     {
-        try
+        if (programClass.subClasses != null)
         {
-            classPool.classFileAccept(classFileVisitor, name);
+            classVisitor.visitProgramClass(programClass);
         }
-        catch (IllegalArgumentException ex)
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        if (libraryClass.subClasses != null)
         {
+            classVisitor.visitLibraryClass(libraryClass);
         }
     }
 }
diff --git a/src/proguard/Targeter.java b/src/proguard/Targeter.java
new file mode 100644
index 0000000..1217a51
--- /dev/null
+++ b/src/proguard/Targeter.java
@@ -0,0 +1,88 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import proguard.classfile.ClassPool;
+import proguard.classfile.util.ClassUtil;
+import proguard.classfile.visitor.ClassVersionSetter;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * This class sets the target version on program classes.
+ *
+ * @author Eric Lafortune
+ */
+public class Targeter
+{
+    private final Configuration configuration;
+
+
+    /**
+     * Creates a new Targeter to set the target version on program classes
+     * according to the given configuration.
+     */
+    public Targeter(Configuration configuration)
+    {
+        this.configuration = configuration;
+    }
+
+
+    /**
+     * Sets the target version on classes in the given program class pool.
+     */
+    public void execute(ClassPool programClassPool) throws IOException
+    {
+        Set newerClassVersions = configuration.warn ? new HashSet() : null;
+
+        programClassPool.classesAccept(new ClassVersionSetter(configuration.targetClassVersion,
+                                                              newerClassVersions));
+
+        if (newerClassVersions != null &&
+            newerClassVersions.size() > 0)
+        {
+            System.err.print("Warning: some classes have more recent versions (");
+
+            Iterator iterator = newerClassVersions.iterator();
+            while (iterator.hasNext())
+            {
+                Integer classVersion = (Integer)iterator.next();
+                System.err.print(ClassUtil.externalClassVersion(classVersion.intValue()));
+
+                if (iterator.hasNext())
+                {
+                    System.err.print(",");
+                }
+            }
+
+            System.err.println(")");
+            System.err.println("         than the target version ("+ClassUtil.externalClassVersion(configuration.targetClassVersion)+").");
+
+            if (!configuration.ignoreWarnings)
+            {
+                System.err.println("         If you are sure this is not a problem,");
+                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/UpToDateChecker.java b/src/proguard/UpToDateChecker.java
new file mode 100644
index 0000000..4a2a964
--- /dev/null
+++ b/src/proguard/UpToDateChecker.java
@@ -0,0 +1,153 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard;
+
+import java.io.File;
+
+/**
+ * This class checks whether the output is up to date.
+ *
+ * @author Eric Lafortune
+ */
+public class UpToDateChecker
+{
+    private final Configuration configuration;
+
+
+    /**
+     * Creates a new UpToDateChecker with the given configuration.
+     */
+    public UpToDateChecker(Configuration configuration)
+    {
+        this.configuration = configuration;
+    }
+
+
+    /**
+     * Returns whether the output is up to date, based on the modification times
+     * of the input jars, output jars, and library jars (or directories).
+     */
+    public boolean check()
+    {
+        long inputLastModified  = configuration.lastModified;
+        long outputLastModified = Long.MAX_VALUE;
+
+        ClassPath programJars = configuration.programJars;
+        ClassPath libraryJars = configuration.libraryJars;
+
+        // Check the dates of the program jars, if any.
+        if (programJars != null)
+        {
+            for (int index = 0; index < programJars.size(); index++)
+            {
+                // Break early, if possible.
+                if (inputLastModified >= outputLastModified)
+                {
+                    break;
+                }
+
+                // Update the input and output modification times.
+                ClassPathEntry classPathEntry = programJars.get(index);
+                if (classPathEntry.isOutput())
+                {
+                    long lastModified = lastModified(classPathEntry.getFile(), true);
+                    if (outputLastModified > lastModified)
+                    {
+                        outputLastModified = lastModified;
+                    }
+                }
+                else
+                {
+                    long lastModified = lastModified(classPathEntry.getFile(), false);
+                    if (inputLastModified < lastModified)
+                    {
+                        inputLastModified = lastModified;
+                    }
+                }
+            }
+        }
+
+        // Check the dates of the library jars, if any.
+        if (libraryJars != null)
+        {
+            for (int index = 0; index < libraryJars.size(); index++)
+            {
+                // Break early, if possible.
+                if (inputLastModified >= outputLastModified)
+                {
+                    break;
+                }
+
+                // Update the input modification time.
+                ClassPathEntry classPathEntry = libraryJars.get(index);
+                long lastModified = lastModified(classPathEntry.getFile(), false);
+                if (inputLastModified < lastModified)
+                {
+                    inputLastModified = lastModified;
+                }
+            }
+        }
+
+        boolean outputUpToDate = inputLastModified < outputLastModified;
+        if (outputUpToDate)
+        {
+            System.out.println("The output is up to date");
+        }
+
+        return outputUpToDate;
+    }
+
+
+    /**
+     * Returns the minimum or maximum modification time of the given file or
+     * of the files in the given directory (recursively).
+     */
+    private long lastModified(File file, boolean minimum)
+    {
+        // Is it a directory?
+        if (file.isDirectory())
+        {
+            // Ignore the directory's modification time; just recurse on its files.
+            File[] files = file.listFiles();
+
+            // Still, an empty output directory is probably a sign that it is
+            // not up to date.
+            long lastModified = files.length != 0 && minimum ?
+                Long.MAX_VALUE : 0L;
+
+            for (int index = 0; index < files.length; index++)
+            {
+                long fileLastModified = lastModified(files[index], minimum);
+                if ((lastModified < fileLastModified) ^ minimum)
+                {
+                    lastModified = fileLastModified;
+                }
+            }
+
+            return lastModified;
+        }
+        else
+        {
+            // Return the file's modification time.
+            return file.lastModified();
+        }
+    }
+}
diff --git a/src/proguard/WordReader.java b/src/proguard/WordReader.java
index 3fb7f1f..f3b7c50 100644
--- a/src/proguard/WordReader.java
+++ b/src/proguard/WordReader.java
@@ -1,6 +1,6 @@
-/* $Id: WordReader.java,v 1.20.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -30,6 +30,7 @@ import java.io.*;
  * Comments (everything starting with '#' on a single line) are ignored.
  *
  * @author Eric Lafortune
+ * @noinspection TailRecursion
  */
 public abstract class WordReader
 {
diff --git a/src/proguard/ant/ClassPathElement.java b/src/proguard/ant/ClassPathElement.java
index ad8bcec..42724bd 100644
--- a/src/proguard/ant/ClassPathElement.java
+++ b/src/proguard/ant/ClassPathElement.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPathElement.java,v 1.9.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,12 +22,9 @@ package proguard.ant;
 
 import org.apache.tools.ant.*;
 import org.apache.tools.ant.types.*;
-
 import proguard.*;
 
-import java.io.*;
-import java.lang.ref.Reference;
-import java.util.Stack;
+import java.io.File;
 
 /**
  * This FileSet represents a class path entry (or a set of class path entries)
@@ -103,13 +100,13 @@ public class ClassPathElement extends Path
                 throw new BuildException("The <outjar> element must specify exactly one file or directory ["+fileNames.length+"]");
             }
         }
-        else
-        {
-            if (fileNames.length < 1)
-            {
-                throw new BuildException("The <injar> element must specify at least one file or directory");
-            }
-        }
+        //else
+        //{
+        //    if (fileNames.length < 1)
+        //    {
+        //        throw new BuildException("The <injar> element must specify at least one file or directory");
+        //    }
+        //}
 
         for (int index = 0; index < fileNames.length; index++)
         {
@@ -168,25 +165,25 @@ public class ClassPathElement extends Path
     }
 
 
-    public void setJarFilter(String jarFilter)
+    public void setJarfilter(String jarFilter)
     {
         this.jarFilter = jarFilter;
     }
 
 
-    public void setWarFilter(String warFilter)
+    public void setWarfilter(String warFilter)
     {
         this.warFilter = warFilter;
     }
 
 
-    public void setEarFilter(String earFilter)
+    public void setEarfilter(String earFilter)
     {
         this.earFilter = earFilter;
     }
 
 
-    public void setZipFilter(String zipFilter)
+    public void setZipfilter(String zipFilter)
     {
         this.zipFilter = zipFilter;
     }
diff --git a/src/proguard/ant/ClassSpecificationElement.java b/src/proguard/ant/ClassSpecificationElement.java
index 217f179..8910362 100644
--- a/src/proguard/ant/ClassSpecificationElement.java
+++ b/src/proguard/ant/ClassSpecificationElement.java
@@ -1,6 +1,6 @@
-/* $Id: ClassSpecificationElement.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,11 @@
  */
 package proguard.ant;
 
-import org.apache.tools.ant.*;
-import org.apache.tools.ant.types.*;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
 import proguard.*;
-import proguard.classfile.*;
-import proguard.classfile.util.*;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
 
 import java.util.*;
 
@@ -38,8 +38,10 @@ public class ClassSpecificationElement extends DataType
     private static final String ANY_CLASS_KEYWORD  = "*";
 
     private String access;
+    private String annotation;
     private String type;
     private String name;
+    private String extendsAnnotation;
     private String extends_;
     private List   fieldSpecifications  = new ArrayList();
     private List   methodSpecifications = new ArrayList();
@@ -48,13 +50,8 @@ public class ClassSpecificationElement extends DataType
     /**
      * Adds the contents of this class specification element to the given list.
      * @param classSpecifications the class specifications to be extended.
-     * @param markClassFiles      specifies whether to mark the class files.
-     * @param markConditionally   specifies whether to mark the class files
-     *                            and class members conditionally.
      */
-    public void appendTo(List    classSpecifications,
-                         boolean markClassFiles,
-                         boolean markConditionally)
+    public void appendTo(List classSpecifications)
     {
         // Get the referenced file set, or else this one.
         ClassSpecificationElement classSpecificationElement = isReference() ?
@@ -62,11 +59,26 @@ public class ClassSpecificationElement extends DataType
                                                      this.getClass().getName()) :
             this;
 
-        // Create a new class specification.
-        String access   = classSpecificationElement.access;
-        String type     = classSpecificationElement.type;
-        String name     = classSpecificationElement.name;
-        String extends_ = classSpecificationElement.extends_;
+        ClassSpecification classSpecification =
+            createClassSpecification(classSpecificationElement);
+
+        // Add it to the list.
+        classSpecifications.add(classSpecification);
+    }
+
+
+    /**
+     * Creates a new class specification corresponding to the contents of this
+     * class specification element.
+     */
+    protected ClassSpecification createClassSpecification(ClassSpecificationElement classSpecificationElement)
+    {
+        String access            = classSpecificationElement.access;
+        String annotation        = classSpecificationElement.annotation;
+        String type              = classSpecificationElement.type;
+        String name              = classSpecificationElement.name;
+        String extendsAnnotation = classSpecificationElement.extendsAnnotation;
+        String extends_          = classSpecificationElement.extends_;
 
         // For backward compatibility, allow a single "*" wildcard to match
         // any class.
@@ -77,25 +89,25 @@ public class ClassSpecificationElement extends DataType
         }
 
         ClassSpecification classSpecification =
-            new ClassSpecification(requiredAccessFlags(true,  access, type),
+            new ClassSpecification(null,
+                                   requiredAccessFlags(true,  access, type),
                                    requiredAccessFlags(false, access, type),
-                                   name     != null ? ClassUtil.internalClassName(name)     : null,
-                                   extends_ != null ? ClassUtil.internalClassName(extends_) : null,
-                                   markClassFiles,
-                                   markConditionally);
+                                   annotation        != null ? ClassUtil.internalType(annotation)   : null,
+                                   name              != null ? ClassUtil.internalClassName(name)         : null,
+                                   extendsAnnotation != null ? ClassUtil.internalType(extendsAnnotation) : null,
+                                   extends_          != null ? ClassUtil.internalClassName(extends_)     : null);
 
         for (int index = 0; index < fieldSpecifications.size(); index++)
         {
-            classSpecification.addField((ClassMemberSpecification)fieldSpecifications.get(index));
+            classSpecification.addField((MemberSpecification)fieldSpecifications.get(index));
         }
 
         for (int index = 0; index < methodSpecifications.size(); index++)
         {
-            classSpecification.addMethod((ClassMemberSpecification)methodSpecifications.get(index));
+            classSpecification.addMethod((MemberSpecification)methodSpecifications.get(index));
         }
 
-        // Add it to the list.
-        classSpecifications.add(classSpecification);
+        return classSpecification;
     }
 
 
@@ -107,6 +119,12 @@ public class ClassSpecificationElement extends DataType
     }
 
 
+    public void setAnnotation(String annotation)
+    {
+        this.annotation = annotation;
+    }
+
+
     public void setType(String type)
     {
         this.type = type;
@@ -119,6 +137,12 @@ public class ClassSpecificationElement extends DataType
     }
 
 
+    public void setExtendsannotation(String extendsAnnotation)
+    {
+        this.extendsAnnotation = extendsAnnotation;
+    }
+
+
     public void setExtends(String extends_)
     {
         this.extends_ = extends_;
@@ -133,42 +157,42 @@ public class ClassSpecificationElement extends DataType
 
     // Ant task nested elements.
 
-    public void addConfiguredField(ClassMemberSpecificationElement classMemberSpecificationElement)
+    public void addConfiguredField(MemberSpecificationElement memberSpecificationElement)
     {
         if (fieldSpecifications == null)
         {
             fieldSpecifications = new ArrayList();
         }
 
-        classMemberSpecificationElement.appendTo(fieldSpecifications,
-                                                 false,
-                                                 false);
+        memberSpecificationElement.appendTo(fieldSpecifications,
+                                            false,
+                                            false);
     }
 
 
-    public void addConfiguredMethod(ClassMemberSpecificationElement classMemberSpecificationElement)
+    public void addConfiguredMethod(MemberSpecificationElement memberSpecificationElement)
     {
         if (methodSpecifications == null)
         {
             methodSpecifications = new ArrayList();
         }
 
-        classMemberSpecificationElement.appendTo(methodSpecifications,
-                                                 true,
-                                                 false);
+        memberSpecificationElement.appendTo(methodSpecifications,
+                                            true,
+                                            false);
     }
 
 
-    public void addConfiguredConstructor(ClassMemberSpecificationElement classMemberSpecificationElement)
+    public void addConfiguredConstructor(MemberSpecificationElement memberSpecificationElement)
     {
         if (methodSpecifications == null)
         {
             methodSpecifications = new ArrayList();
         }
 
-        classMemberSpecificationElement.appendTo(methodSpecifications,
-                                                 true,
-                                                 true);
+        memberSpecificationElement.appendTo(methodSpecifications,
+                                            true,
+                                            true);
     }
 
 
diff --git a/src/proguard/ant/ConfigurationElement.java b/src/proguard/ant/ConfigurationElement.java
index 3aaa4e9..7332444 100644
--- a/src/proguard/ant/ConfigurationElement.java
+++ b/src/proguard/ant/ConfigurationElement.java
@@ -1,6 +1,6 @@
-/* $Id: ConfigurationElement.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,15 +20,9 @@
  */
 package proguard.ant;
 
-import proguard.*;
-
-import org.apache.tools.ant.*;
-import org.apache.tools.ant.types.*;
-
-import java.util.*;
-
-import java.io.*;
-import java.util.*;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import proguard.Configuration;
 
 /**
  * This DataType represents a reference to a ProGuard configuration in Ant.
diff --git a/src/proguard/ant/ConfigurationTask.java b/src/proguard/ant/ConfigurationTask.java
index 87c9ad7..2c8d094 100644
--- a/src/proguard/ant/ConfigurationTask.java
+++ b/src/proguard/ant/ConfigurationTask.java
@@ -1,6 +1,6 @@
-/* $Id: ConfigurationTask.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,10 @@
  */
 package proguard.ant;
 
-import proguard.*;
-
 import org.apache.tools.ant.*;
+import proguard.*;
 
-import java.io.*;
+import java.io.IOException;
 import java.util.*;
 
 /**
@@ -34,7 +33,7 @@ import java.util.*;
  */
 public class ConfigurationTask extends Task
 {
-    protected Configuration configuration = new Configuration();
+    protected final Configuration configuration = new Configuration();
 
 
     /**
@@ -53,11 +52,14 @@ public class ConfigurationTask extends Task
         configuration.keep = extendClassSpecifications(configuration.keep,
                                                        this.configuration.keep);
 
-        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
-                                                            this.configuration.keepNames);
+        configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping,
+                                                                   this.configuration.whyAreYouKeeping);
 
-        configuration.keepAttributes = extendAttributes(configuration.keepAttributes,
-                                                        this.configuration.keepAttributes);
+        configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects,
+                                                                      this.configuration.assumeNoSideEffects);
+
+        configuration.keepAttributes = extendList(configuration.keepAttributes,
+                                                  this.configuration.keepAttributes);
     }
 
 
@@ -87,75 +89,80 @@ public class ConfigurationTask extends Task
     }
 
 
-    public void addConfiguredKeep(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeep(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keep = extendClassSpecifications(configuration.keep,
-                                                       classSpecificationElement,
-                                                       true,
-                                                       false);
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      true,
+                                                      false);
     }
 
 
-    public void addConfiguredKeepclassmembers(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeepclassmembers(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keep = extendClassSpecifications(configuration.keep,
-                                                       classSpecificationElement,
-                                                       false,
-                                                       false);
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      false,
+                                                      false);
     }
 
 
-    public void addConfiguredKeepclasseswithmembers(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeepclasseswithmembers(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keep = extendClassSpecifications(configuration.keep,
-                                                       classSpecificationElement,
-                                                       true,
-                                                       true);
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      true,
+                                                      true);
     }
 
 
-    public void addConfiguredKeepnames(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeepnames(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
-                                                            classSpecificationElement,
-                                                            true,
-                                                            false);
+        // Set the shrinking flag, based on the name (backward compatibility).
+        keepSpecificationElement.setAllowshrinking(true);
+
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      true,
+                                                      false);
     }
 
 
-    public void addConfiguredKeepclassmembernames(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeepclassmembernames(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
-                                                            classSpecificationElement,
-                                                            false,
-                                                            false);
+        // Set the shrinking flag, based on the name (backward compatibility).
+        keepSpecificationElement.setAllowshrinking(true);
+
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      false,
+                                                      false);
     }
 
 
-    public void addConfiguredKeepclasseswithmembernames(ClassSpecificationElement classSpecificationElement)
+    public void addConfiguredKeepclasseswithmembernames(KeepSpecificationElement keepSpecificationElement)
     {
-        configuration.keepNames = extendClassSpecifications(configuration.keepNames,
-                                                            classSpecificationElement,
-                                                            true,
-                                                            true);
+        // Set the shrinking flag, based on the name (backward compatibility).
+        keepSpecificationElement.setAllowshrinking(true);
+
+        configuration.keep = extendKeepSpecifications(configuration.keep,
+                                                      keepSpecificationElement,
+                                                      true,
+                                                      true);
     }
 
 
     public void addConfiguredWhyareyoukeeping(ClassSpecificationElement classSpecificationElement)
     {
         configuration.whyAreYouKeeping = extendClassSpecifications(configuration.whyAreYouKeeping,
-                                                                   classSpecificationElement,
-                                                                   true,
-                                                                   false);
+                                                                   classSpecificationElement);
     }
 
 
     public void addConfiguredAssumenosideeffects(ClassSpecificationElement classSpecificationElement)
     {
         configuration.assumeNoSideEffects = extendClassSpecifications(configuration.assumeNoSideEffects,
-                                                                      classSpecificationElement,
-                                                                      false,
-                                                                      false);
+                                                                      classSpecificationElement);
     }
 
 
@@ -166,6 +173,20 @@ public class ConfigurationTask extends Task
     }
 
 
+    public void addConfiguredAdaptResourceFileNames(FilterElement filterElement)
+    {
+        configuration.adaptResourceFileNames = extendFilter(configuration.adaptResourceFileNames,
+                                                            filterElement);
+    }
+
+
+    public void addConfiguredAdaptResourceFileContents(FilterElement filterElement)
+    {
+        configuration.adaptResourceFileContents = extendFilter(configuration.adaptResourceFileContents,
+                                                               filterElement);
+    }
+
+
     public void addConfiguredConfiguration(ConfigurationElement configurationElement)
     {
         configurationElement.appendTo(configuration);
@@ -205,19 +226,6 @@ public class ConfigurationTask extends Task
 
     // Small utility methods.
 
-    private String optionalFileName(String fileName)
-    {
-        return
-            fileName.equalsIgnoreCase("false") ||
-            fileName.equalsIgnoreCase("no")    ||
-            fileName.equalsIgnoreCase("off")    ? null :
-            fileName.equalsIgnoreCase("true")  ||
-            fileName.equalsIgnoreCase("yes")   ||
-            fileName.equalsIgnoreCase("on")     ? ""   :
-                                                  fileName;
-    }
-
-
     private ClassPath extendClassPath(ClassPath        classPath,
                                       ClassPathElement classPathElement,
                                       boolean          output)
@@ -251,19 +259,33 @@ public class ConfigurationTask extends Task
     }
 
 
+    private List extendKeepSpecifications(List                     keepSpecifications,
+                                          KeepSpecificationElement keepSpecificationElement,
+                                          boolean                  markClasses,
+                                          boolean                  markClassesConditionally)
+    {
+        if (keepSpecifications == null)
+        {
+            keepSpecifications = new ArrayList();
+        }
+
+        keepSpecificationElement.appendTo(keepSpecifications,
+                                          markClasses,
+                                          markClassesConditionally);
+
+        return keepSpecifications;
+    }
+
+
     private List extendClassSpecifications(List                      classSpecifications,
-                                           ClassSpecificationElement classSpecificationElement,
-                                           boolean                   markClassFiles,
-                                           boolean                   markClassFilesConditionally)
+                                           ClassSpecificationElement classSpecificationElement)
     {
         if (classSpecifications == null)
         {
             classSpecifications = new ArrayList();
         }
 
-        classSpecificationElement.appendTo(classSpecifications,
-                                           markClassFiles,
-                                           markClassFilesConditionally);
+        classSpecificationElement.appendTo(classSpecifications);
 
         return classSpecifications;
     }
@@ -300,19 +322,33 @@ public class ConfigurationTask extends Task
     }
 
 
-    private List extendAttributes(List attributes,
-                                  List additionalAttributes)
+    private List extendFilter(List          filter,
+                              FilterElement filterElement)
+    {
+        if (filter == null)
+        {
+            filter = new ArrayList();
+        }
+
+        filterElement.appendTo(filter);
+
+        return filter;
+    }
+
+
+    private List extendList(List list,
+                            List additionalList)
     {
-        if (additionalAttributes != null)
+        if (additionalList != null)
         {
-            if (attributes == null)
+            if (list == null)
             {
-                attributes = new ArrayList();
+                list = new ArrayList();
             }
 
-            attributes.addAll(additionalAttributes);
+            list.addAll(additionalList);
         }
 
-        return attributes;
+        return list;
     }
 }
diff --git a/src/proguard/ant/KeepAttributeElement.java b/src/proguard/ant/FilterElement.java
similarity index 50%
copy from src/proguard/ant/KeepAttributeElement.java
copy to src/proguard/ant/FilterElement.java
index 5a2e61b..6c8fd3b 100644
--- a/src/proguard/ant/KeepAttributeElement.java
+++ b/src/proguard/ant/FilterElement.java
@@ -1,6 +1,6 @@
-/* $Id: KeepAttributeElement.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,51 +20,52 @@
  */
 package proguard.ant;
 
-import org.apache.tools.ant.types.*;
+import org.apache.tools.ant.types.DataType;
+import proguard.util.ListUtil;
 
-import java.util.*;
+import java.util.List;
 
 /**
- * This DataType represents a named attribute in Ant.
+ * This DataType represents a name filter in Ant.
  *
  * @author Eric Lafortune
  */
-public class KeepAttributeElement extends DataType
+public class FilterElement extends DataType
 {
-    private String name;
+    private String filter;
 
 
     /**
-     * Adds the contents of this element to the given list of attributes.
-     * @param keepAttributes the list of attributes to be extended.
+     * Adds the contents of this element to the given name filter.
+     * @param filter the list of attributes to be extended.
      */
-    public void appendTo(List keepAttributes)
+    public void appendTo(List filter)
     {
         // Get the referenced element, or else this one.
-        KeepAttributeElement keepAttributeElement = isReference() ?
-            (KeepAttributeElement)getCheckedRef(this.getClass(),
-                                                this.getClass().getName()) :
+        FilterElement filterElement = isReference() ?
+            (FilterElement)getCheckedRef(this.getClass(),
+                                         this.getClass().getName()) :
             this;
 
-        String name = keepAttributeElement.name;
+        String filterString = filterElement.filter;
 
-        if (name == null)
+        if (filterString == null)
         {
-            // Clear the list to keep all attributes.
-            keepAttributes.clear();
+            // Clear the filter to keep all names.
+            filter.clear();
         }
         else
         {
-            // Add the attibute name to the list.
-            keepAttributes.add(name);
+            // Append the filter.
+            filter.addAll(ListUtil.commaSeparatedList(filterString));
         }
     }
 
 
     // Ant task attributes.
 
-    public void setName(String name)
+    public void setFilter(String filter)
     {
-        this.name = name;
+        this.filter = filter;
     }
 }
diff --git a/src/proguard/ant/KeepAttributeElement.java b/src/proguard/ant/KeepAttributeElement.java
index 5a2e61b..b4b8aeb 100644
--- a/src/proguard/ant/KeepAttributeElement.java
+++ b/src/proguard/ant/KeepAttributeElement.java
@@ -1,6 +1,6 @@
-/* $Id: KeepAttributeElement.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,9 @@
  */
 package proguard.ant;
 
-import org.apache.tools.ant.types.*;
+import org.apache.tools.ant.types.DataType;
 
-import java.util.*;
+import java.util.List;
 
 /**
  * This DataType represents a named attribute in Ant.
diff --git a/src/proguard/ant/KeepSpecificationElement.java b/src/proguard/ant/KeepSpecificationElement.java
new file mode 100644
index 0000000..13305ce
--- /dev/null
+++ b/src/proguard/ant/KeepSpecificationElement.java
@@ -0,0 +1,87 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.ant;
+
+import proguard.KeepSpecification;
+
+import java.util.List;
+
+/**
+ * This DataType represents a class specification in Ant.
+ *
+ * @author Eric Lafortune
+ */
+public class KeepSpecificationElement extends ClassSpecificationElement
+{
+    private boolean allowShrinking;
+    private boolean allowOptimization;
+    private boolean allowObfuscation;
+
+
+    /**
+     * Adds the contents of this class specification element to the given list.
+     * @param keepSpecifications the class specifications to be extended.
+     * @param markClasses         specifies whether to mark the classes.
+     * @param markConditionally   specifies whether to mark the classes
+     *                            and class members conditionally.
+     */
+    public void appendTo(List    keepSpecifications,
+                         boolean markClasses,
+                         boolean markConditionally)
+    {
+        // Get the referenced file set, or else this one.
+        KeepSpecificationElement keepSpecificationElement = isReference() ?
+            (KeepSpecificationElement)getCheckedRef(this.getClass(),
+                                                     this.getClass().getName()) :
+            this;
+
+        KeepSpecification keepSpecification =
+            new KeepSpecification(markClasses,
+                                  markConditionally,
+                                  allowShrinking,
+                                  allowOptimization,
+                                  allowObfuscation,
+                                  createClassSpecification(keepSpecificationElement));
+
+        // Add it to the list.
+        keepSpecifications.add(keepSpecification);
+    }
+
+
+    // Ant task attributes.
+
+    public void setAllowshrinking(boolean allowShrinking)
+    {
+        this.allowShrinking = allowShrinking;
+    }
+
+
+    public void setAllowoptimization(boolean allowOptimization)
+    {
+        this.allowOptimization = allowOptimization;
+    }
+
+
+    public void setAllowobfuscation(boolean allowObfuscation)
+    {
+        this.allowObfuscation = allowObfuscation;
+    }
+}
diff --git a/src/proguard/ant/ClassMemberSpecificationElement.java b/src/proguard/ant/MemberSpecificationElement.java
similarity index 77%
rename from src/proguard/ant/ClassMemberSpecificationElement.java
rename to src/proguard/ant/MemberSpecificationElement.java
index 3909d57..a03dfff 100644
--- a/src/proguard/ant/ClassMemberSpecificationElement.java
+++ b/src/proguard/ant/MemberSpecificationElement.java
@@ -1,6 +1,6 @@
-/* $Id: ClassMemberSpecificationElement.java,v 1.5.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,12 @@
  */
 package proguard.ant;
 
-import org.apache.tools.ant.*;
-import org.apache.tools.ant.types.*;
-import proguard.*;
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.util.*;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import proguard.MemberSpecification;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
+import proguard.util.ListUtil;
 
 import java.util.*;
 
@@ -34,9 +34,10 @@ import java.util.*;
  *
  * @author Eric Lafortune
  */
-public class ClassMemberSpecificationElement extends DataType
+public class MemberSpecificationElement extends DataType
 {
     private String access;
+    private String annotation;
     private String type;
     private String name;
     private String parameters;
@@ -45,30 +46,36 @@ public class ClassMemberSpecificationElement extends DataType
     /**
      * Adds the contents of this class member specification element to the given
      * list.
-     * @param classMemberSpecifications the class member specifications to be
+     * @param memberSpecifications the class member specifications to be
      *                                  extended.
      * @param isMethod                  specifies whether this specification
      *                                  refers to a method.
      * @param isConstructor             specifies whether this specification
      *                                  refers to a constructor.
      */
-    public void appendTo(List    classMemberSpecifications,
+    public void appendTo(List    memberSpecifications,
                          boolean isMethod,
                          boolean isConstructor)
     {
         // Get the referenced file set, or else this one.
-        ClassMemberSpecificationElement classSpecificationElement = isReference() ?
-            (ClassMemberSpecificationElement)getCheckedRef(this.getClass(),
-                                                           this.getClass().getName()) :
+        MemberSpecificationElement memberSpecificationElement = isReference() ?
+            (MemberSpecificationElement)getCheckedRef(this.getClass(),
+                                                      this.getClass().getName()) :
             this;
 
         // Create a new class specification.
-        String access     = classSpecificationElement.access;
-        String type       = classSpecificationElement.type;
-        String name       = classSpecificationElement.name;
-        String parameters = classSpecificationElement.parameters;
+        String access     = memberSpecificationElement.access;
+        String type       = memberSpecificationElement.type;
+        String annotation = memberSpecificationElement.annotation;
+        String name       = memberSpecificationElement.name;
+        String parameters = memberSpecificationElement.parameters;
+
+        // Perform some basic conversions and checks on the attributes.
+        if (annotation != null)
+        {
+            annotation = ClassUtil.internalType(annotation);
+        }
 
-        // Perform some basic checks on the attributes.
         if (isMethod)
         {
             if (isConstructor)
@@ -105,14 +112,15 @@ public class ClassMemberSpecificationElement extends DataType
             type       != null ? ClassUtil.internalType(type)                            :
                                  null;
 
-        ClassMemberSpecification classMemberSpecification =
-            new ClassMemberSpecification(requiredAccessFlags(true,  access),
-                                         requiredAccessFlags(false, access),
-                                         name,
-                                         descriptor);
+        MemberSpecification memberSpecification =
+            new MemberSpecification(requiredAccessFlags(true,  access),
+                                    requiredAccessFlags(false, access),
+                                    annotation,
+                                    name,
+                                    descriptor);
 
         // Add it to the list.
-        classMemberSpecifications.add(classMemberSpecification);
+        memberSpecifications.add(memberSpecification);
     }
 
 
@@ -124,6 +132,12 @@ public class ClassMemberSpecificationElement extends DataType
     }
 
 
+    public void setAnnotation(String annotation)
+    {
+        this.annotation = annotation;
+    }
+
+
     public void setType(String type)
     {
         this.type = type;
diff --git a/src/proguard/ant/ProGuardTask.java b/src/proguard/ant/ProGuardTask.java
index 444302c..967ea98 100644
--- a/src/proguard/ant/ProGuardTask.java
+++ b/src/proguard/ant/ProGuardTask.java
@@ -1,6 +1,6 @@
-/* $Id: ProGuardTask.java,v 1.32.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,9 @@
  */
 package proguard.ant;
 
-import org.apache.tools.ant.*;
+import org.apache.tools.ant.BuildException;
 import proguard.*;
-import proguard.classfile.util.*;
+import proguard.classfile.util.ClassUtil;
 
 import java.io.*;
 
@@ -82,6 +82,22 @@ public class ProGuardTask extends ConfigurationTask
     }
 
 
+    public void setTarget(String target)
+    {
+        configuration.targetClassVersion = ClassUtil.internalClassVersion(target);
+        if (configuration.targetClassVersion == 0)
+        {
+            throw new BuildException("Unsupported target '"+target+"'");
+        }
+    }
+
+
+    public void setForceprocessing(boolean forceProcessing)
+    {
+        configuration.lastModified = forceProcessing ? Long.MAX_VALUE : 0;
+    }
+
+
     public void setPrintseeds(File printSeeds)
     {
         configuration.printSeeds = optionalFile(printSeeds);
@@ -106,6 +122,12 @@ public class ProGuardTask extends ConfigurationTask
     }
 
 
+    public void setOptimizationpasses(int optimizationPasses)
+    {
+        configuration.optimizationPasses = optimizationPasses;
+    }
+
+
     public void setAllowaccessmodification(boolean allowAccessModification)
     {
         configuration.allowAccessModification = allowAccessModification;
@@ -148,15 +170,29 @@ public class ProGuardTask extends ConfigurationTask
     }
 
 
-    public void setDefaultpackage(String defaultPackage)
+    public void setUsemixedcaseclassnames(boolean useMixedCaseClassNames)
     {
-        configuration.defaultPackage = ClassUtil.internalClassName(defaultPackage);
+        configuration.useMixedCaseClassNames = useMixedCaseClassNames;
     }
 
 
-    public void setUsemixedcaseclassnames(boolean useMixedCaseClassNames)
+    public void setFlattenpackagehierarchy(String flattenPackageHierarchy)
     {
-        configuration.useMixedCaseClassNames = useMixedCaseClassNames;
+        configuration.flattenPackageHierarchy = ClassUtil.internalClassName(flattenPackageHierarchy);
+    }
+
+
+    public void setRepackageclasses(String repackageClasses)
+    {
+        configuration.repackageClasses = ClassUtil.internalClassName(repackageClasses);
+    }
+
+    /**
+     * @deprecated Use the repackageclasses attribute instead.
+     */
+    public void setDefaultpackage(String defaultPackage)
+    {
+        configuration.repackageClasses = ClassUtil.internalClassName(defaultPackage);
     }
 
 
@@ -166,6 +202,18 @@ public class ProGuardTask extends ConfigurationTask
     }
 
 
+    public void setPreverify(boolean preverify)
+    {
+        configuration.preverify = preverify;
+    }
+
+
+    public void setMicroedition(boolean microEdition)
+    {
+        configuration.microEdition = microEdition;
+    }
+
+
     public void setVerbose(boolean verbose)
     {
         configuration.verbose = verbose;
@@ -190,6 +238,12 @@ public class ProGuardTask extends ConfigurationTask
     }
 
 
+    public void setPrintconfiguration(File printConfiguration)
+    {
+        configuration.printConfiguration = optionalFile(printConfiguration);
+    }
+
+
     public void setDump(File dump)
     {
         configuration.dump = optionalFile(dump);
diff --git a/src/proguard/classfile/ClassConstants.java b/src/proguard/classfile/ClassConstants.java
index de35fcf..908c98e 100644
--- a/src/proguard/classfile/ClassConstants.java
+++ b/src/proguard/classfile/ClassConstants.java
@@ -1,8 +1,7 @@
-/* $Id: ClassConstants.java,v 1.25.2.2 2006/12/11 21:57:29 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -21,11 +20,9 @@
  */
 package proguard.classfile;
 
-
 /**
- * Constants used in representing a Java class file (*.class).
+ * Constants used in representing a Java class (*.class).
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public interface ClassConstants
@@ -34,10 +31,35 @@ public interface ClassConstants
 
     public static final int MAGIC = 0xCAFEBABE;
 
-    public static final int MAJOR_VERSION_MIN = 45;
-    public static final int MINOR_VERSION_MIN = 3;
-    public static final int MAJOR_VERSION_MAX = 50;
-    public static final int MINOR_VERSION_MAX = 0;
+    public static final int INTERNAL_CLASS_VERSION_1_0_MAJOR = 45;
+    public static final int INTERNAL_CLASS_VERSION_1_0_MINOR = 3;
+    public static final int INTERNAL_CLASS_VERSION_1_2_MAJOR = 46;
+    public static final int INTERNAL_CLASS_VERSION_1_2_MINOR = 0;
+    public static final int INTERNAL_CLASS_VERSION_1_3_MAJOR = 47;
+    public static final int INTERNAL_CLASS_VERSION_1_3_MINOR = 0;
+    public static final int INTERNAL_CLASS_VERSION_1_4_MAJOR = 48;
+    public static final int INTERNAL_CLASS_VERSION_1_4_MINOR = 0;
+    public static final int INTERNAL_CLASS_VERSION_1_5_MAJOR = 49;
+    public static final int INTERNAL_CLASS_VERSION_1_5_MINOR = 0;
+    public static final int INTERNAL_CLASS_VERSION_1_6_MAJOR = 50;
+    public static final int INTERNAL_CLASS_VERSION_1_6_MINOR = 0;
+
+    public static final int INTERNAL_CLASS_VERSION_1_0 = (INTERNAL_CLASS_VERSION_1_0_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_0_MINOR;
+    public static final int INTERNAL_CLASS_VERSION_1_2 = (INTERNAL_CLASS_VERSION_1_2_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_2_MINOR;
+    public static final int INTERNAL_CLASS_VERSION_1_3 = (INTERNAL_CLASS_VERSION_1_3_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_3_MINOR;
+    public static final int INTERNAL_CLASS_VERSION_1_4 = (INTERNAL_CLASS_VERSION_1_4_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_4_MINOR;
+    public static final int INTERNAL_CLASS_VERSION_1_5 = (INTERNAL_CLASS_VERSION_1_5_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_5_MINOR;
+    public static final int INTERNAL_CLASS_VERSION_1_6 = (INTERNAL_CLASS_VERSION_1_6_MAJOR << 16) | INTERNAL_CLASS_VERSION_1_6_MINOR;
+
+    public static final String EXTERNAL_CLASS_VERSION_1_0       = "1.0";
+    public static final String EXTERNAL_CLASS_VERSION_1_1       = "1.1";
+    public static final String EXTERNAL_CLASS_VERSION_1_2       = "1.2";
+    public static final String EXTERNAL_CLASS_VERSION_1_3       = "1.3";
+    public static final String EXTERNAL_CLASS_VERSION_1_4       = "1.4";
+    public static final String EXTERNAL_CLASS_VERSION_1_5       = "1.5";
+    public static final String EXTERNAL_CLASS_VERSION_1_6       = "1.6";
+    public static final String EXTERNAL_CLASS_VERSION_1_5_ALIAS = "5";
+    public static final String EXTERNAL_CLASS_VERSION_1_6_ALIAS = "6";
 
     public static final int INTERNAL_ACC_PUBLIC       = 0x0001;
     public static final int INTERNAL_ACC_PRIVATE      = 0x0002;
@@ -114,19 +136,21 @@ public interface ClassConstants
     public static final int CONSTANT_InterfaceMethodref = 11;
     public static final int CONSTANT_NameAndType        = 12;
 
+    public static final String ATTR_SourceFile                           = "SourceFile";
+    public static final String ATTR_SourceDir                            = "SourceDir";
     public static final String ATTR_InnerClasses                         = "InnerClasses";
     public static final String ATTR_EnclosingMethod                      = "EnclosingMethod";
+    public static final String ATTR_Deprecated                           = "Deprecated";
+    public static final String ATTR_Synthetic                            = "Synthetic";
+    public static final String ATTR_Signature                            = "Signature";
     public static final String ATTR_ConstantValue                        = "ConstantValue";
     public static final String ATTR_Exceptions                           = "Exceptions";
     public static final String ATTR_Code                                 = "Code";
+    public static final String ATTR_StackMap                             = "StackMap";
+    public static final String ATTR_StackMapTable                        = "StackMapTable";
     public static final String ATTR_LineNumberTable                      = "LineNumberTable";
     public static final String ATTR_LocalVariableTable                   = "LocalVariableTable";
     public static final String ATTR_LocalVariableTypeTable               = "LocalVariableTypeTable";
-    public static final String ATTR_SourceFile                           = "SourceFile";
-    public static final String ATTR_SourceDir                            = "SourceDir";
-    public static final String ATTR_Deprecated                           = "Deprecated";
-    public static final String ATTR_Synthetic                            = "Synthetic";
-    public static final String ATTR_Signature                            = "Signature";
     public static final String ATTR_RuntimeVisibleAnnotations            = "RuntimeVisibleAnnotations";
     public static final String ATTR_RuntimeInvisibleAnnotations          = "RuntimeInvisibleAnnotations";
     public static final String ATTR_RuntimeVisibleParameterAnnotations   = "RuntimeVisibleParameterAnnotations";
@@ -141,21 +165,24 @@ public interface ClassConstants
 
     public static final char EXTERNAL_PACKAGE_SEPARATOR = '.';
     public static final char INTERNAL_PACKAGE_SEPARATOR = '/';
-
-    public static final char INTERNAL_SPECIAL_CHARACTER = '-';
+    public static final char INNER_CLASS_SEPARATOR      = '$';
+    public static final char SPECIAL_CLASS_CHARACTER    = '-';
+    public static final char SPECIAL_MEMBER_SEPARATOR   = '$';
 
     public static final char EXTERNAL_METHOD_ARGUMENTS_OPEN      = '(';
     public static final char EXTERNAL_METHOD_ARGUMENTS_CLOSE     = ')';
     public static final char EXTERNAL_METHOD_ARGUMENTS_SEPARATOR = ',';
 
-    public static final char INTERNAL_METHOD_ARGUMENTS_OPEN      = '(';
-    public static final char INTERNAL_METHOD_ARGUMENTS_CLOSE     = ')';
+    public static final char INTERNAL_METHOD_ARGUMENTS_OPEN  = '(';
+    public static final char INTERNAL_METHOD_ARGUMENTS_CLOSE = ')';
 
+    public static final String INTERNAL_PACKAGE_JAVA_LANG         = "java/lang/";
     public static final String INTERNAL_NAME_JAVA_LANG_OBJECT     = "java/lang/Object";
     public static final String INTERNAL_TYPE_JAVA_LANG_OBJECT     = "Ljava/lang/Object;";
-    public static final String INTERNAL_PACKAGE_JAVA_LANG         = "java/lang/";
-
     public static final String INTERNAL_NAME_JAVA_LANG_CLONEABLE  = "java/lang/Cloneable";
+    public static final String INTERNAL_NAME_JAVA_LANG_THROWABLE  = "java/lang/Throwable";
+    public static final String INTERNAL_NAME_JAVA_LANG_CLASS      = "java/lang/Class";
+    public static final String INTERNAL_NAME_JAVA_LANG_STRING     = "java/lang/String";
     public static final String INTERNAL_NAME_JAVA_IO_SERIALIZABLE = "java/io/Serializable";
 
     public static final String INTERNAL_METHOD_NAME_INIT   = "<init>";
@@ -163,43 +190,61 @@ public interface ClassConstants
     public static final String INTERNAL_METHOD_NAME_CLINIT = "<clinit>";
     public static final String INTERNAL_METHOD_TYPE_CLINIT = "()V";
 
-    public static final String INTERNAL_CLASS_NAME_JAVA_LANG_CLASS = "java/lang/Class";
-    public static final String INTERNAL_METHOD_NAME_CLASS_FOR_NAME = "forName";
-    public static final String INTERNAL_METHOD_TYPE_CLASS_FOR_NAME = "(Ljava/lang/String;)Ljava/lang/Class;";
-
-    public static final String INTERNAL_METHOD_NAME_DOT_CLASS       = "class$";
+    public static final String INTERNAL_METHOD_NAME_CLASS_FOR_NAME            = "forName";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_FOR_NAME            = "(Ljava/lang/String;)Ljava/lang/Class;";
+    public static final String INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE  = "getComponentType";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE  = "()Ljava/lang/Class;";
+    public static final String INTERNAL_METHOD_NAME_CLASS_GET_FIELD           = "getField";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_GET_FIELD           = "(Ljava/lang/String;)Ljava/lang/reflect/Field;";
+    public static final String INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_FIELD  = "getDeclaredField";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_FIELD  = "(Ljava/lang/String;)Ljava/lang/reflect/Field;";
+    public static final String INTERNAL_METHOD_NAME_CLASS_GET_METHOD          = "getMethod";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_GET_METHOD          = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
+    public static final String INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_METHOD = "getDeclaredMethod";
+    public static final String INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_METHOD = "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
+
+    public static final String INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC = "class$";
     public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC = "(Ljava/lang/String;)Ljava/lang/Class;";
+    public static final String INTERNAL_METHOD_NAME_DOT_CLASS_JIKES = "class";
     public static final String INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES = "(Ljava/lang/String;Z)Ljava/lang/Class;";
 
     public static final String INTERNAL_METHOD_NAME_NEW_INSTANCE = "newInstance";
     public static final String INTERNAL_METHOD_TYPE_NEW_INSTANCE = "()Ljava/lang/Object;";
 
-    public static final char   INTERNAL_TYPE_VOID          = 'V';
-    public static final char   INTERNAL_TYPE_BOOLEAN       = 'Z';
-    public static final char   INTERNAL_TYPE_BYTE          = 'B';
-    public static final char   INTERNAL_TYPE_CHAR          = 'C';
-    public static final char   INTERNAL_TYPE_SHORT         = 'S';
-    public static final char   INTERNAL_TYPE_INT           = 'I';
-    public static final char   INTERNAL_TYPE_FLOAT         = 'F';
-    public static final char   INTERNAL_TYPE_LONG          = 'J';
-    public static final char   INTERNAL_TYPE_DOUBLE        = 'D';
-    public static final char   INTERNAL_TYPE_CLASS_START   = 'L';
-    public static final char   INTERNAL_TYPE_CLASS_END     = ';';
-    public static final char   INTERNAL_TYPE_ARRAY         = '[';
-    public static final char   INTERNAL_TYPE_GENERIC_START = '<';
-    public static final char   INTERNAL_TYPE_GENERIC_END   = '>';
+    public static final char INTERNAL_TYPE_VOID          = 'V';
+    public static final char INTERNAL_TYPE_BOOLEAN       = 'Z';
+    public static final char INTERNAL_TYPE_BYTE          = 'B';
+    public static final char INTERNAL_TYPE_CHAR          = 'C';
+    public static final char INTERNAL_TYPE_SHORT         = 'S';
+    public static final char INTERNAL_TYPE_INT           = 'I';
+    public static final char INTERNAL_TYPE_LONG          = 'J';
+    public static final char INTERNAL_TYPE_FLOAT         = 'F';
+    public static final char INTERNAL_TYPE_DOUBLE        = 'D';
+    public static final char INTERNAL_TYPE_CLASS_START   = 'L';
+    public static final char INTERNAL_TYPE_CLASS_END     = ';';
+    public static final char INTERNAL_TYPE_ARRAY         = '[';
+    public static final char INTERNAL_TYPE_GENERIC_START = '<';
+    public static final char INTERNAL_TYPE_GENERIC_END   = '>';
 
     public static final String EXTERNAL_TYPE_JAVA_LANG_OBJECT = "java.lang.Object";
     public static final String EXTERNAL_PACKAGE_JAVA_LANG     = "java.lang.";
 
-    public static final String EXTERNAL_TYPE_VOID        = "void";
-    public static final String EXTERNAL_TYPE_BOOLEAN     = "boolean";
-    public static final String EXTERNAL_TYPE_BYTE        = "byte";
-    public static final String EXTERNAL_TYPE_CHAR        = "char";
-    public static final String EXTERNAL_TYPE_SHORT       = "short";
-    public static final String EXTERNAL_TYPE_INT         = "int";
-    public static final String EXTERNAL_TYPE_FLOAT       = "float";
-    public static final String EXTERNAL_TYPE_LONG        = "long";
-    public static final String EXTERNAL_TYPE_DOUBLE      = "double";
-    public static final String EXTERNAL_TYPE_ARRAY       = "[]";
+    public static final String EXTERNAL_TYPE_VOID    = "void";
+    public static final String EXTERNAL_TYPE_BOOLEAN = "boolean";
+    public static final String EXTERNAL_TYPE_BYTE    = "byte";
+    public static final String EXTERNAL_TYPE_CHAR    = "char";
+    public static final String EXTERNAL_TYPE_SHORT   = "short";
+    public static final String EXTERNAL_TYPE_INT     = "int";
+    public static final String EXTERNAL_TYPE_FLOAT   = "float";
+    public static final String EXTERNAL_TYPE_LONG    = "long";
+    public static final String EXTERNAL_TYPE_DOUBLE  = "double";
+    public static final String EXTERNAL_TYPE_ARRAY   = "[]";
+
+    public static final int TYPICAL_CONSTANT_POOL_SIZE     = 256;
+    public static final int TYPICAL_FIELD_COUNT            = 64;
+    public static final int TYPICAL_METHOD_COUNT           = 64;
+    public static final int TYPICAL_CODE_LENGTH            = 1024;
+    public static final int TYPICAL_EXCEPTION_TABLE_LENGTH = 16;
+    public static final int TYPICAL_VARIABLES_SIZE         = 64;
+    public static final int TYPICAL_STACK_SIZE             = 16;
 }
diff --git a/src/proguard/classfile/ClassCpInfo.java b/src/proguard/classfile/ClassCpInfo.java
deleted file mode 100644
index bf4b41b..0000000
--- a/src/proguard/classfile/ClassCpInfo.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/* $Id: ClassCpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'class' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ClassCpInfo extends CpInfo
-{
-    public int u2nameIndex;
-
-    /**
-     * An extra field pointing to the referenced ClassFile object.
-     * This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     */
-    public ClassFile referencedClassFile;
-
-
-    protected ClassCpInfo()
-    {
-    }
-
-
-    /**
-     * Creates a new ClassCpInfo with the given name index.
-     * @param u2nameIndex         the index of the name in the constant pool.
-     * @param referencedClassFile the referenced class file.
-     */
-    public ClassCpInfo(int       u2nameIndex,
-                       ClassFile referencedClassFile)
-    {
-        this.u2nameIndex         = u2nameIndex;
-        this.referencedClassFile = referencedClassFile;
-    }
-
-
-    /**
-     * Returns the name index.
-     */
-    protected int getNameIndex()
-    {
-        return u2nameIndex;
-    }
-
-    /**
-     * Sets the name index.
-     */
-    protected void setNameIndex(int index)
-    {
-        u2nameIndex = index;
-    }
-
-
-    /**
-     * Returns the name.
-     */
-    public String getName(ClassFile classFile)
-    {
-        return classFile.getCpString(u2nameIndex);
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Class;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2nameIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2nameIndex);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitClassCpInfo(classFile, this);
-    }
-
-
-    /**
-     * Lets the referenced class file accept the given visitor.
-     */
-    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/ClassPool.java b/src/proguard/classfile/ClassPool.java
index 5757f38..054e7df 100644
--- a/src/proguard/classfile/ClassPool.java
+++ b/src/proguard/classfile/ClassPool.java
@@ -1,8 +1,7 @@
-/* $Id: ClassPool.java,v 1.18.2.7 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -21,20 +20,20 @@
  */
 package proguard.classfile;
 
-import proguard.classfile.util.*;
+import proguard.classfile.util.ClassUtil;
 import proguard.classfile.visitor.*;
 
 import java.util.*;
 
 /**
- * This is a set of representations of class files. They can be enumerated or
- * retrieved by name. They can also be accessed by means of class file visitors.
+ * This is a set of representations of classes. They      can be enumerated or
+ * retrieved by name. They can also be accessed by means of class visitors.
  *
  * @author Eric Lafortune
  */
 public class ClassPool
 {
-    private Map classFiles = new HashMap();
+    private final Map classes = new HashMap();
 
 
     /**
@@ -42,25 +41,25 @@ public class ClassPool
      */
     public void clear()
     {
-        classFiles.clear();
+        classes.clear();
     }
 
 
     /**
-     * Adds the given ClassFile to the class pool.
+     * Adds the given Clazz to the class pool.
      */
-    public void addClass(ClassFile classFile)
+    public void addClass(Clazz clazz)
     {
-        classFiles.put(classFile.getName(), classFile);
+        classes.put(clazz.getName(), clazz);
     }
 
 
     /**
-     * Removes the given ClassFile from the class pool.
+     * Removes the given Clazz from the class pool.
      */
-    public void removeClass(ClassFile classFile)
+    public void removeClass(Clazz clazz)
     {
-        classFiles.remove(classFile.getName());
+        classes.remove(clazz.getName());
     }
 
 
@@ -69,27 +68,27 @@ public class ClassPool
      * <code>null</code> if the class with the given name is not in the class
      * pool. Returns the base class if the class name is an array type.
      */
-    public ClassFile getClass(String className)
+    public Clazz getClass(String className)
     {
-        return (ClassFile)classFiles.get(ClassUtil.internalClassNameFromClassType(className));
+        return (Clazz)classes.get(ClassUtil.internalClassNameFromClassType(className));
     }
 
 
     /**
-     * Returns an Iterator of all class file names in the class pool.
+     * Returns an Iterator of all class names in the class pool.
      */
     public Iterator classNames()
     {
-        return classFiles.keySet().iterator();
+        return classes.keySet().iterator();
     }
 
 
     /**
-     * Returns the number of class files in the class pool.
+     * Returns the number of classes in the class pool.
      */
     public int size()
     {
-        return classFiles.size();
+        return classes.size();
     }
 
 
@@ -103,46 +102,46 @@ public class ClassPool
 
 
     /**
-     * Applies the given ClassFileVisitor to all classes in the class pool,
+     * Applies the given ClassVisitor to all classes in the class pool,
      * in random order.
      */
-    public void classFilesAccept(ClassFileVisitor classFileVisitor)
+    public void classesAccept(ClassVisitor classVisitor)
     {
-        Iterator iterator = classFiles.values().iterator();
+        Iterator iterator = classes.values().iterator();
         while (iterator.hasNext())
         {
-            ClassFile classFile = (ClassFile)iterator.next();
-            classFile.accept(classFileVisitor);
+            Clazz clazz = (Clazz)iterator.next();
+            clazz.accept(classVisitor);
         }
     }
 
 
     /**
-     * Applies the given ClassFileVisitor to all classes in the class pool,
+     * Applies the given ClassVisitor to all classes in the class pool,
      * in sorted order.
      */
-    public void classFilesAcceptAlphabetically(ClassFileVisitor classFileVisitor)
+    public void classesAcceptAlphabetically(ClassVisitor classVisitor)
     {
-        TreeMap sortedClassFiles = new TreeMap(classFiles);
-        Iterator iterator = sortedClassFiles.values().iterator();
+        TreeMap sortedClasses = new TreeMap(classes);
+        Iterator iterator = sortedClasses.values().iterator();
         while (iterator.hasNext())
         {
-            ClassFile classFile = (ClassFile)iterator.next();
-            classFile.accept(classFileVisitor);
+            Clazz clazz = (Clazz)iterator.next();
+            clazz.accept(classVisitor);
         }
     }
 
 
     /**
-     * Applies the given ClassFileVisitor to the class with the given name,
+     * Applies the given ClassVisitor to the class with the given name,
      * if it is present in the class pool.
      */
-    public void classFileAccept(ClassFileVisitor classFileVisitor, String className)
+    public void classAccept(String className, ClassVisitor classVisitor)
     {
-        ClassFile classFile = getClass(className);
-        if (classFile != null)
+        Clazz clazz = getClass(className);
+        if (clazz != null)
         {
-            classFile.accept(classFileVisitor);
+            clazz.accept(classVisitor);
         }
     }
 }
diff --git a/src/proguard/classfile/ClassFile.java b/src/proguard/classfile/Clazz.java
similarity index 58%
rename from src/proguard/classfile/ClassFile.java
rename to src/proguard/classfile/Clazz.java
index bddab5a..9919f46 100644
--- a/src/proguard/classfile/ClassFile.java
+++ b/src/proguard/classfile/Clazz.java
@@ -1,8 +1,7 @@
-/* $Id: ClassFile.java,v 1.27.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -21,17 +20,17 @@
  */
 package proguard.classfile;
 
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
 
 
 /**
- * This interface provides access to the data in a Java class file (*.class).
+ * This interface provides access to the representation of a Java class.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
-public interface ClassFile extends VisitorAccepter
+public interface Clazz extends VisitorAccepter
 {
     /**
      * Returns the access flags of this class.
@@ -62,61 +61,66 @@ public interface ClassFile extends VisitorAccepter
     public String getInterfaceName(int index);
 
     /**
-     * Returns the tag value of the CpEntry at the specified index.
+     * Returns the tag value of the Constant at the specified index.
+     */
+    public int getTag(int constantIndex);
+
+    /**
+     * Returns the String value of the Utf8Constant at the specified index.
      */
-    public int getCpTag(int cpIndex);
+    public String getString(int constantIndex);
 
     /**
-     * Returns the String value of the Utf8CpEntry at the specified index.
+     * Returns the String value of the StringConstant at the specified index.
      */
-    public String getCpString(int cpIndex);
+    public String getStringString(int constantIndex);
 
     /**
-     * Returns the class name of ClassCpEntry at the specified index.
+     * Returns the class name of ClassConstant at the specified index.
      */
-    public String getCpClassNameString(int cpIndex);
+    public String getClassName(int constantIndex);
 
     /**
-     * Returns the name of the NameAndTypeCpEntry at the specified index.
+     * Returns the name of the NameAndTypeConstant at the specified index.
      */
-    public String getCpNameString(int cpIndex);
+    public String getName(int constantIndex);
 
     /**
-     * Returns the type of the NameAndTypeCpEntry at the specified index.
+     * Returns the type of the NameAndTypeConstant at the specified index.
      */
-    public String getCpTypeString(int cpIndex);
+    public String getType(int constantIndex);
 
 
-    // Methods pertaining to related class files.
+    // Methods pertaining to related classes.
 
     /**
-     * Notifies this ClassFile that it is being subclassed by another class.
+     * Notifies this Clazz that it is being subclassed by another class.
      */
-    public void addSubClass(ClassFile classFile);
+    public void addSubClass(Clazz clazz);
 
     /**
      * Returns the super class of this class.
      */
-    public ClassFile getSuperClass();
+    public Clazz getSuperClass();
 
     /**
      * Returns the interface at the given index.
      */
-    public ClassFile getInterface(int index);
+    public Clazz getInterface(int index);
 
     /**
      * Returns whether this class extends the given class.
      * A class is always considered to extend itself.
      * Interfaces are considered to only extend the root Object class.
      */
-    public boolean extends_(ClassFile classFile);
+    public boolean extends_(Clazz clazz);
 
     /**
      * Returns whether this class implements the given class.
      * A class is always considered to implement itself.
      * Interfaces are considered to implement all their superinterfaces.
      */
-    public boolean implements_(ClassFile classFile);
+    public boolean extendsOrImplements(Clazz clazz);
 
 
     // Methods for getting specific class members.
@@ -124,90 +128,90 @@ public interface ClassFile extends VisitorAccepter
     /**
      * Returns the field with the given name and descriptor.
      */
-    FieldInfo findField(String name, String descriptor);
+    Field findField(String name, String descriptor);
 
     /**
      * Returns the method with the given name and descriptor.
      */
-    MethodInfo findMethod(String name, String descriptor);
+    Method findMethod(String name, String descriptor);
 
 
     // Methods for accepting various types of visitors.
 
     /**
-     * Accepts the given class file visitor.
+     * Accepts the given class visitor.
      */
-    public void accept(ClassFileVisitor classFileVisitor);
+    public void accept(ClassVisitor classVisitor);
 
     /**
-     * Accepts the given class file visitor in the class hierarchy.
+     * Accepts the given class visitor in the class hierarchy.
      * @param visitThisClass   specifies whether to visit this class.
      * @param visitSuperClass  specifies whether to visit the super classes.
      * @param visitInterfaces  specifies whether to visit the interfaces.
      * @param visitSubclasses  specifies whether to visit the subclasses.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> that will
+     * @param classVisitor     the <code>ClassVisitor</code> that will
      *                         visit the class hierarchy.
      */
-    public void hierarchyAccept(boolean          visitThisClass,
-                                boolean          visitSuperClass,
-                                boolean          visitInterfaces,
-                                boolean          visitSubclasses,
-                                ClassFileVisitor classFileVisitor);
+    public void hierarchyAccept(boolean      visitThisClass,
+                                boolean      visitSuperClass,
+                                boolean      visitInterfaces,
+                                boolean      visitSubclasses,
+                                ClassVisitor classVisitor);
 
     /**
      * Lets the given constant pool entry visitor visit all constant pool entries
      * of this class.
      */
-    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor);
+    public void constantPoolEntriesAccept(ConstantVisitor constantVisitor);
 
     /**
      * Lets the given constant pool entry visitor visit the constant pool entry
      * at the specified index.
      */
-    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor);
+    public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor);
 
     /**
      * Lets the given member info visitor visit all fields of this class.
      */
-    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor);
+    public void fieldsAccept(MemberVisitor memberVisitor);
 
     /**
      * Lets the given member info visitor visit the specified field.
      */
-    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor);
+    public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor);
 
     /**
      * Lets the given member info visitor visit all methods of this class.
      */
-    public void methodsAccept(MemberInfoVisitor memberInfoVisitor);
+    public void methodsAccept(MemberVisitor memberVisitor);
 
     /**
      * Lets the given member info visitor visit the specified method.
      */
-    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor);
+    public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor);
 
     /**
      * Returns whether the given method may possibly have implementing or
      * overriding methods down the class hierarchy. This can only be true
      * if the class is not final, and the method is not private, static, or
      * final, or a constructor.
-     * @param methodInfo the method that may have implementations.
+     * @param method the method that may have implementations.
      * @return whether it may have implementations.
      */
-    public boolean mayHaveImplementations(MethodInfo methodInfo);
+    public boolean mayHaveImplementations(Method method);
 
     /**
      * Lets the given member info visitor visit all concrete implementations of
      * the specified method in the class hierarchy.
-     * @param methodInfo        the method that may have concrete implementations.
+     * @param method            the method that may have concrete implementations.
      * @param visitThisMethod   specifies whether to visit the method in
      *                          this class.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> that will
+     * @param memberVisitor     the <code>MemberVisitor</code> that will
      *                          visit the method hierarchy.
      */
-    public void methodImplementationsAccept(MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor);
+    public void methodImplementationsAccept(Method        method,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor);
 
     /**
      * Lets the given member info visitor visit all concrete implementations of
@@ -216,13 +220,13 @@ public interface ClassFile extends VisitorAccepter
      * @param type              the method descriptor.
      * @param visitThisMethod   specifies whether to visit the method in
      *                          this class.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> that will
+     * @param memberVisitor     the <code>MemberVisitor</code> that will
      *                          visit the method hierarchy.
      */
-    public void methodImplementationsAccept(String            name,
-                                            String            type,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor);
+    public void methodImplementationsAccept(String        name,
+                                            String        type,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor);
 
     /**
      * Lets the given member info visitor visit all concrete implementations of
@@ -238,22 +242,22 @@ public interface ClassFile extends VisitorAccepter
      * @param visitOverridingMethods specifies whether to visit the method in
      *                               the subclasses.
      * @param visitSpecialMethods    specifies whether to visit special methods.
-     * @param memberInfoVisitor      the <code>MemberInfoVisitor</code> that
+     * @param memberVisitor          the <code>MemberVisitor</code> that
      *                               will visit the method hierarchy.
      */
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSpecialMethods,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            MemberInfoVisitor memberInfoVisitor);
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSpecialMethods,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            MemberVisitor memberVisitor);
     /**
      * Lets the given member info visitor visit all concrete implementations of
      * the specified method in the class hierarchy.
      * @param name                   the method name.
      * @param descriptor             the method descriptor.
-     * @param methodInfo             the method itself, if present.
+     * @param method                 the method itself, if present.
      * @param visitThisMethod        specifies whether to visit the method in
      *                               this class.
      * @param visitSpecialMethods    specifies whether to visit the method in
@@ -263,20 +267,20 @@ public interface ClassFile extends VisitorAccepter
      * @param visitOverridingMethods specifies whether to visit the method in
      *                               the subclasses.
      * @param visitSpecialMethods    specifies whether to visit special methods.
-     * @param memberInfoVisitor      the <code>MemberInfoVisitor</code> that
+     * @param memberVisitor          the <code>MemberVisitor</code> that
      *                               will visit the method hierarchy.
      */
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            boolean           visitSpecialMethods,
-                                            MemberInfoVisitor memberInfoVisitor);
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            Method        method,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            boolean       visitSpecialMethods,
+                                            MemberVisitor memberVisitor);
 
     /**
      * Lets the given attribute info visitor visit all attributes of this class.
      */
-    public void attributesAccept(AttrInfoVisitor attrInfoVisitor);
+    public void attributesAccept(AttributeVisitor attributeVisitor);
 }
diff --git a/src/proguard/classfile/CpInfo.java b/src/proguard/classfile/CpInfo.java
deleted file mode 100644
index 6c7bc5c..0000000
--- a/src/proguard/classfile/CpInfo.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/* $Id: CpInfo.java,v 1.23.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of an entry in the ConstantPool. Specific types of entry
- * have their representations sub-classed from this.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public abstract class CpInfo implements VisitorAccepter
-{
-    // Shared copies of constant pool objects, to avoid creating a lot of objects.
-    private static final IntegerCpInfo            integerCpInfo            = new IntegerCpInfo();
-    private static final FloatCpInfo              floatCpInfo              = new FloatCpInfo();
-    private static final LongCpInfo               longCpInfo               = new LongCpInfo();
-    private static final DoubleCpInfo             doubleCpInfo             = new DoubleCpInfo();
-    private static final StringCpInfo             stringCpInfo             = new StringCpInfo();
-    private static final FieldrefCpInfo           fieldrefCpInfo           = new FieldrefCpInfo();
-    private static final MethodrefCpInfo          methodrefCpInfo          = new MethodrefCpInfo();
-    private static final InterfaceMethodrefCpInfo interfaceMethodrefCpInfo = new InterfaceMethodrefCpInfo();
-    private static final NameAndTypeCpInfo        nameAndTypeCpInfo        = new NameAndTypeCpInfo();
-
-
-    //public int  u1tag;
-    //public byte info[];
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    /**
-     * Creates a new CpInfo from the data passed.
-     *
-     * @throws IOException if the class file is corrupt or incomplete.
-     */
-    public static CpInfo create(DataInput din) throws IOException
-    {
-        // Instantiate based on tag byte
-        CpInfo cpInfo = null;
-        int u1tag = din.readUnsignedByte();
-        switch (u1tag)
-        {
-        case ClassConstants.CONSTANT_Utf8:               cpInfo = new Utf8CpInfo();              break;
-        case ClassConstants.CONSTANT_Integer:            cpInfo = new IntegerCpInfo();           break;
-        case ClassConstants.CONSTANT_Float:              cpInfo = new FloatCpInfo();             break;
-        case ClassConstants.CONSTANT_Long:               cpInfo = new LongCpInfo();              break;
-        case ClassConstants.CONSTANT_Double:             cpInfo = new DoubleCpInfo();            break;
-        case ClassConstants.CONSTANT_Class:              cpInfo = new ClassCpInfo();             break;
-        case ClassConstants.CONSTANT_String:             cpInfo = new StringCpInfo();            break;
-        case ClassConstants.CONSTANT_Fieldref:           cpInfo = new FieldrefCpInfo();          break;
-        case ClassConstants.CONSTANT_Methodref:          cpInfo = new MethodrefCpInfo();         break;
-        case ClassConstants.CONSTANT_InterfaceMethodref: cpInfo = new InterfaceMethodrefCpInfo();break;
-        case ClassConstants.CONSTANT_NameAndType:        cpInfo = new NameAndTypeCpInfo();       break;
-        default: throw new IOException("Unknown constant type ["+u1tag+"] in constant pool");
-        }
-        cpInfo.readInfo(din);
-        return cpInfo;
-    }
-
-
-    /**
-     * Creates a new CpInfo from the data passed, for UTF-8 and Class constant
-     * pool entries, or returns a shared object, for all other entries.
-     *
-     * @throws IOException if the class file is corrupt or incomplete.
-     */
-    public static CpInfo createOrShare(DataInput din) throws IOException
-    {
-        // Instantiate based on tag byte
-        CpInfo cpInfo = null;
-        int u1tag = din.readUnsignedByte();
-        switch (u1tag)
-        {
-        case ClassConstants.CONSTANT_Utf8:               cpInfo = new Utf8CpInfo();         break;
-        case ClassConstants.CONSTANT_Integer:            cpInfo = integerCpInfo;            break;
-        case ClassConstants.CONSTANT_Float:              cpInfo = floatCpInfo;              break;
-        case ClassConstants.CONSTANT_Long:               cpInfo = longCpInfo;               break;
-        case ClassConstants.CONSTANT_Double:             cpInfo = doubleCpInfo;             break;
-        case ClassConstants.CONSTANT_Class:              cpInfo = new ClassCpInfo();        break;
-        case ClassConstants.CONSTANT_String:             cpInfo = stringCpInfo;             break;
-        case ClassConstants.CONSTANT_Fieldref:           cpInfo = fieldrefCpInfo;           break;
-        case ClassConstants.CONSTANT_Methodref:          cpInfo = methodrefCpInfo;          break;
-        case ClassConstants.CONSTANT_InterfaceMethodref: cpInfo = interfaceMethodrefCpInfo; break;
-        case ClassConstants.CONSTANT_NameAndType:        cpInfo = nameAndTypeCpInfo;        break;
-        default: throw new IOException("Unknown constant type ["+u1tag+"] in constant pool");
-        }
-        cpInfo.readInfo(din);
-        return cpInfo;
-    }
-
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public void write(DataOutput dout) throws IOException
-    {
-        dout.writeByte(getTag());
-        writeInfo(dout);
-    }
-
-
-    // Abstract methods to be implemented by extensions.
-
-    /**
-     * Returns the class pool info tag that specifies the entry type.
-     */
-    public abstract int getTag();
-
-
-    /**
-     * Reads the 'info' data following the u1tag byte.
-     */
-    protected abstract void readInfo(DataInput din) throws IOException;
-
-
-    /**
-     * Writes the 'info' data following the u1tag byte.
-     */
-    protected abstract void writeInfo(DataOutput dout) throws IOException;
-
-
-    /**
-     * Accepts the given visitor.
-     */
-    public abstract void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor);
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/DoubleCpInfo.java b/src/proguard/classfile/DoubleCpInfo.java
deleted file mode 100644
index 631643c..0000000
--- a/src/proguard/classfile/DoubleCpInfo.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* $Id: DoubleCpInfo.java,v 1.18.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'double' entry in the ConstantPool (takes up two indices).
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class DoubleCpInfo extends CpInfo
-{
-    public int u4highBytes;
-    public int u4lowBytes;
-
-
-    /**
-     * Creates a new DoubleCpInfo with the given double value.
-     */
-    public DoubleCpInfo(double value)
-    {
-        setValue(value);
-    }
-
-
-    protected DoubleCpInfo()
-    {
-    }
-
-
-    /**
-     * Returns the double value of this DoubleCpInfo.
-     */
-    public double getValue()
-    {
-        return Double.longBitsToDouble((long)u4highBytes << 32 | (u4lowBytes & 0xffffffffL));
-    }
-
-
-    /**
-     * Sets the double value of this DoubleCpInfo.
-     */
-    public void setValue(double value)
-    {
-        long longValue = Double.doubleToLongBits(value);
-        u4highBytes = (int)(longValue >> 32);
-        u4lowBytes  = (int) longValue;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Double;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u4highBytes = din.readInt();
-        u4lowBytes = din.readInt();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeInt(u4highBytes);
-        dout.writeInt(u4lowBytes);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitDoubleCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/Field.java
similarity index 74%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/Field.java
index d9a4a34..d516c10 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/Field.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -24,10 +23,10 @@ package proguard.classfile;
 
 
 /**
- * Representation of a field from a class file.
+ * Representation of a field from a class.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public interface Field extends Member
 {
 }
diff --git a/src/proguard/classfile/FieldrefCpInfo.java b/src/proguard/classfile/FieldrefCpInfo.java
deleted file mode 100644
index ddfe54f..0000000
--- a/src/proguard/classfile/FieldrefCpInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id: FieldrefCpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-/**
- * Representation of a 'field reference' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class FieldrefCpInfo extends RefCpInfo
-{
-    protected FieldrefCpInfo()
-    {
-    }
-
-
-    /**
-     * Creates a new FieldrefCpInfo with the given name and type indices.
-     * @param u2classIndex         the index of the class in the constant pool.
-     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
-     * @param referencedClassFile  the referenced class file.
-     * @param referencedMemberInfo the referenced member info.
-     */
-    public FieldrefCpInfo(int        u2classIndex,
-                          int        u2nameAndTypeIndex,
-                          ClassFile  referencedClassFile,
-                          MemberInfo referencedMemberInfo)
-    {
-        this.u2classIndex         = u2classIndex;
-        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
-        this.referencedClassFile  = referencedClassFile;
-        this.referencedMemberInfo = referencedMemberInfo;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Fieldref;
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitFieldrefCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/FloatCpInfo.java b/src/proguard/classfile/FloatCpInfo.java
deleted file mode 100644
index acdaf60..0000000
--- a/src/proguard/classfile/FloatCpInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/* $Id: FloatCpInfo.java,v 1.17.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'float' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class FloatCpInfo extends CpInfo
-{
-    public int u4bytes;
-
-
-    /**
-     * Creates a new FloatCpInfo with the given float value.
-     */
-    public FloatCpInfo(float value)
-    {
-        setValue(value);
-    }
-
-
-    protected FloatCpInfo()
-    {
-    }
-
-
-    /**
-     * Returns the float value of this FloatCpInfo.
-     */
-    public float getValue()
-    {
-        return Float.intBitsToFloat(u4bytes);
-    }
-
-
-    /**
-     * Sets the float value of this FloatCpInfo.
-     */
-    public void setValue(float value)
-    {
-        u4bytes = Float.floatToIntBits(value);
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Float;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u4bytes = din.readInt();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeInt(u4bytes);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitFloatCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/IntegerCpInfo.java b/src/proguard/classfile/IntegerCpInfo.java
deleted file mode 100644
index b789a8a..0000000
--- a/src/proguard/classfile/IntegerCpInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/* $Id: IntegerCpInfo.java,v 1.17.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'integer' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class IntegerCpInfo extends CpInfo
-{
-    public int u4bytes;
-
-
-    /**
-     * Creates a new IntegerCpInfo with the given integer value.
-     */
-    public IntegerCpInfo(int value)
-    {
-        setValue(value);
-    }
-
-
-    protected IntegerCpInfo()
-    {
-    }
-
-
-    /**
-     * Returns the integer value of this IntegerCpInfo.
-     */
-    public int getValue()
-    {
-        return u4bytes;
-    }
-
-
-    /**
-     * Sets the integer value of this IntegerCpInfo.
-     */
-    public void setValue(int value)
-    {
-        u4bytes = value;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Integer;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u4bytes = din.readInt();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeInt(u4bytes);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitIntegerCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/InterfaceMethodrefCpInfo.java b/src/proguard/classfile/InterfaceMethodrefCpInfo.java
deleted file mode 100644
index 267437f..0000000
--- a/src/proguard/classfile/InterfaceMethodrefCpInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id: InterfaceMethodrefCpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-/**
- * Representation of a 'interface method reference' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class InterfaceMethodrefCpInfo extends RefCpInfo
-{
-    protected InterfaceMethodrefCpInfo()
-    {
-    }
-
-
-    /**
-     * Creates a new InterfaceMethodrefCpInfo with the given name and type indices.
-     * @param u2classIndex         the index of the class in the constant pool.
-     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
-     * @param referencedClassFile  the referenced class file.
-     * @param referencedMemberInfo the referenced member info.
-     */
-    public InterfaceMethodrefCpInfo(int        u2classIndex,
-                                    int        u2nameAndTypeIndex,
-                                    ClassFile  referencedClassFile,
-                                    MemberInfo referencedMemberInfo)
-    {
-        this.u2classIndex         = u2classIndex;
-        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
-        this.referencedClassFile  = referencedClassFile;
-        this.referencedMemberInfo = referencedMemberInfo;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_InterfaceMethodref;
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitInterfaceMethodrefCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/LibraryClass.java b/src/proguard/classfile/LibraryClass.java
new file mode 100644
index 0000000..fef6938
--- /dev/null
+++ b/src/proguard/classfile/LibraryClass.java
@@ -0,0 +1,553 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This Clazz is a compact representation of the essential data in a Java class.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryClass implements Clazz
+{
+    public int             u2accessFlags;
+    public String          thisClassName;
+    public String          superClassName;
+    public String[]        interfaceNames;
+    public LibraryField[]  fields;
+    public LibraryMethod[] methods;
+
+    /**
+     * An extra field pointing to the superclass of this class.
+     * This field is filled out by the {@link ClassSuperHierarchyInitializer}.
+     */
+    public Clazz   superClass;
+
+    /**
+     * An extra field pointing to the interfaces of this class.
+     * This field is filled out by the {@link ClassSuperHierarchyInitializer}.
+     */
+    public Clazz[] interfaceClasses;
+
+    /**
+     * An extra field pointing to the subclasses of this class.
+     * This field is filled out by the {@link ClassSubHierarchyInitializer}.
+     */
+    public Clazz[] subClasses;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates an empty LibraryClass.
+     */
+    public LibraryClass() {}
+
+
+    /**
+     * Returns whether this library class is visible to the outside world.
+     */
+    boolean isVisible()
+    {
+        return (u2accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0;
+    }
+
+
+    // Implementations for Clazz.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName()
+    {
+        return thisClassName;
+    }
+
+    public String getSuperName()
+    {
+        // This may be java/lang/Object, in which case there is no super.
+        return superClassName;
+    }
+
+    public int getInterfaceCount()
+    {
+        return interfaceClasses.length;
+    }
+
+    public String getInterfaceName(int index)
+    {
+        return interfaceNames[index];
+    }
+
+    public int getTag(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+    public String getString(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+    public String getStringString(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+    public String getClassName(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+    public String getName(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+    public String getType(int constantIndex)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store constant pool");
+    }
+
+
+    public void addSubClass(Clazz clazz)
+    {
+        if (subClasses == null)
+        {
+            subClasses = new Clazz[1];
+        }
+        else
+        {
+            // Copy the old elements into new larger array.
+            Clazz[] temp     = new Clazz[subClasses.length+1];
+            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
+            subClasses = temp;
+        }
+
+        subClasses[subClasses.length-1] = clazz;
+    }
+
+
+    public Clazz getSuperClass()
+    {
+        return superClass;
+    }
+
+
+    public Clazz getInterface(int index)
+    {
+        return interfaceClasses[index];
+    }
+
+
+    public boolean extends_(Clazz clazz)
+    {
+        if (this.equals(clazz))
+        {
+            return true;
+        }
+
+        return superClass != null &&
+               superClass.extends_(clazz);
+    }
+
+
+    public boolean extendsOrImplements(Clazz clazz)
+    {
+        if (this.equals(clazz))
+        {
+            return true;
+        }
+
+        if (superClass != null &&
+            superClass.extendsOrImplements(clazz))
+        {
+            return true;
+        }
+
+        if (interfaceClasses != null)
+        {
+            for (int index = 0; index < interfaceClasses.length; index++)
+            {
+                Clazz interfaceClass = interfaceClasses[index];
+                if (interfaceClass != null &&
+                    interfaceClass.extendsOrImplements(clazz))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    public Field findField(String name, String descriptor)
+    {
+        for (int index = 0; index < fields.length; index++)
+        {
+            Field field = fields[index];
+            if (field != null &&
+                (name       == null || field.getName(this).equals(name)) &&
+                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
+            {
+                return field;
+            }
+        }
+
+        return null;
+    }
+
+
+    public Method findMethod(String name, String descriptor)
+    {
+        for (int index = 0; index < methods.length; index++)
+        {
+            Method method = methods[index];
+            if (method != null &&
+                (name       == null || method.getName(this).equals(name)) &&
+                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
+            {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+
+    public void accept(ClassVisitor classVisitor)
+    {
+        classVisitor.visitLibraryClass(this);
+    }
+
+
+    public void hierarchyAccept(boolean      visitThisClass,
+                                boolean      visitSuperClass,
+                                boolean      visitInterfaces,
+                                boolean      visitSubclasses,
+                                ClassVisitor classVisitor)
+    {
+        // First visit the current classfile.
+        if (visitThisClass)
+        {
+            accept(classVisitor);
+        }
+
+        // Then visit its superclass, recursively.
+        if (visitSuperClass)
+        {
+            if (superClass != null)
+            {
+                superClass.hierarchyAccept(true,
+                                           true,
+                                           visitInterfaces,
+                                           false,
+                                           classVisitor);
+            }
+        }
+
+        // Then visit its interfaces, recursively.
+        if (visitInterfaces)
+        {
+            if (interfaceClasses != null)
+            {
+                for (int index = 0; index < interfaceClasses.length; index++)
+                {
+                    Clazz interfaceClass = interfaceClasses[index];
+                    if (interfaceClass != null)
+                    {
+                        interfaceClass.hierarchyAccept(true,
+                                                       true,
+                                                       true,
+                                                       false,
+                                                       classVisitor);
+                    }
+                }
+            }
+        }
+
+        // Then visit its subclasses, recursively.
+        if (visitSubclasses)
+        {
+            if (subClasses != null)
+            {
+                for (int index = 0; index < subClasses.length; index++)
+                {
+                    subClasses[index].hierarchyAccept(true,
+                                                      false,
+                                                      false,
+                                                      true,
+                                                      classVisitor);
+                }
+            }
+        }
+    }
+
+
+    public void constantPoolEntriesAccept(ConstantVisitor constantVisitor)
+    {
+        // This class doesn't keep references to its constant pool entries.
+    }
+
+
+    public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor)
+    {
+        // This class doesn't keep references to its constant pool entries.
+    }
+
+
+    public void fieldsAccept(MemberVisitor memberVisitor)
+    {
+        for (int index = 0; index < fields.length; index++)
+        {
+            Field field = fields[index];
+            if (field != null)
+            {
+                field.accept(this, memberVisitor);
+            }
+        }
+    }
+
+
+    public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor)
+    {
+        Field field = findField(name, descriptor);
+        if (field != null)
+        {
+            field.accept(this, memberVisitor);
+        }
+    }
+
+
+    public void methodsAccept(MemberVisitor memberVisitor)
+    {
+        for (int index = 0; index < methods.length; index++)
+        {
+            Method method = methods[index];
+            if (method != null)
+            {
+                method.accept(this, memberVisitor);
+            }
+        }
+    }
+
+
+    public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor)
+    {
+        Method method = findMethod(name, descriptor);
+        if (method != null)
+        {
+            method.accept(this, memberVisitor);
+        }
+    }
+
+
+    public boolean mayHaveImplementations(Method method)
+    {
+        return
+           (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 &&
+           (method == null ||
+            ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
+                                         ClassConstants.INTERNAL_ACC_STATIC  |
+                                         ClassConstants.INTERNAL_ACC_FINAL)) == 0 &&
+                                                                                  !method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)));
+    }
+
+
+    private boolean isSpecial(Method method)
+    {
+        return
+            (method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
+                                        ClassConstants.INTERNAL_ACC_STATIC)) != 0 ||
+                                                                                  method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+    }
+
+
+    public void methodImplementationsAccept(Method        method,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(method.getName(this),
+                                    method.getDescriptor(this),
+                                    method,
+                                    visitThisMethod,
+                                    true,
+                                    true,
+                                    true,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(name,
+                                    descriptor,
+                                    visitThisMethod,
+                                    true,
+                                    true,
+                                    true,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSpecialMethods,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(name,
+                                    descriptor,
+                                    findMethod(name, descriptor),
+                                    visitThisMethod,
+                                    visitSpecialMethods,
+                                    visitSuperMethods,
+                                    visitOverridingMethods,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            Method        method,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSpecialMethods,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            MemberVisitor memberVisitor)
+    {
+        // Do we have the method in this class?
+        if (method != null)
+        {
+            // Is it a special method?
+            if (isSpecial(method))
+            {
+                // Visit the special method in this class, if allowed.
+                if (visitSpecialMethods)
+                {
+                    method.accept(this, memberVisitor);
+
+                    // The method can't have any other implementations.
+                    return;
+                }
+            }
+            else
+            {
+                // Visit the method in this class, if allowed.
+                if (visitThisMethod)
+                {
+                    method.accept(this, memberVisitor);
+                }
+
+                // We don't have to look in subclasses if there can't be
+                // any overriding implementations.
+                if (!mayHaveImplementations(method))
+                {
+                    visitOverridingMethods = false;
+                }
+
+                // We don't have to look in superclasses if we have a concrete
+                // implementation here.
+                if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
+                {
+                    visitSuperMethods = false;
+                }
+            }
+        }
+
+        // Then visit the method in its subclasses, recursively.
+        if (visitOverridingMethods)
+        {
+            // Go looking for implementations in all of the subclasses.
+            if (subClasses != null)
+            {
+                for (int index = 0; index < subClasses.length; index++)
+                {
+                    Clazz subClass = subClasses[index];
+                    subClass.methodImplementationsAccept(name,
+                                                         descriptor,
+                                                         true,
+                                                         false,
+                                                         visitSuperMethods,
+                                                         true,
+                                                         memberVisitor);
+                }
+            }
+
+            // We don't have to look in superclasses right away if we dont't
+            // have a concrete class here.
+            if ((u2accessFlags & (ClassConstants.INTERNAL_ACC_INTERFACE |
+                                  ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
+            {
+                visitSuperMethods = false;
+            }
+        }
+
+        // Then visit the method in its superclass, recursively.
+        if (visitSuperMethods)
+        {
+            if (superClass != null)
+            {
+                superClass.methodImplementationsAccept(name,
+                                                       descriptor,
+                                                       true,
+                                                       false,
+                                                       true,
+                                                       false,
+                                                       memberVisitor);
+            }
+        }
+    }
+
+
+    public void attributesAccept(AttributeVisitor attributeVisitor)
+    {
+        throw new UnsupportedOperationException("Library class ["+thisClassName+"] doesn't store attributes");
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/LibraryClassFile.java b/src/proguard/classfile/LibraryClassFile.java
deleted file mode 100644
index 1762ce1..0000000
--- a/src/proguard/classfile/LibraryClassFile.java
+++ /dev/null
@@ -1,743 +0,0 @@
-/* $Id: LibraryClassFile.java,v 1.40.2.3 2007/01/25 21:01:01 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * This is a compact representation of the essential data in a Java class file.
- * A LibraryClassFile instance representing a *.class file can be generated
- * using the static create(DataInput) method, but not persisted back.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LibraryClassFile implements ClassFile
-{
-    // Some objects and arrays that can be reused.
-    private static LibraryClassFile    reusableLibraryClassFile;
-    private static CpInfo[]            reusableConstantPool;
-    private static LibraryFieldInfo[]  reusableFields;
-    private static LibraryMethodInfo[] reusableMethods;
-
-
-    public int                 u2accessFlags;
-    public String              thisClassName;
-    public String              superClassName;
-    public String[]            interfaceNames;
-    public LibraryFieldInfo[]  fields;
-    public LibraryMethodInfo[] methods;
-
-    /**
-     * An extra field pointing to the superclass of this class.
-     * This field is filled out by the <code>{@link ClassFileHierarchyInitializer}</code>.
-     */
-    public ClassFile   superClass          = null;
-
-    /**
-     * An extra field pointing to the interfaces of this class.
-     * This field is filled out by the <code>{@link ClassFileHierarchyInitializer}</code>.
-     */
-    public ClassFile[] interfaceClasses    = null;
-
-    /**
-     * An extra field pointing to the subclasses of this class.
-     * This field is filled out by the <code>{@link ClassFileHierarchyInitializer}</code>.
-     */
-    public ClassFile[] subClasses          = null;
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    /**
-     * Creates a new LibraryClassFile from the class file format data in the DataInput
-     * stream. If specified, this method may return <code>null</code> if the
-     * class file is not visible.
-     *
-     * @throws IOException if the class file is corrupt or incomplete
-     */
-    public static LibraryClassFile create(DataInput din,
-                                          boolean skipNonPublicClasses,
-                                          boolean skipNonPublicClassMembers)
-    throws IOException
-    {
-        // See if we have to create a new library class file object.
-        if (reusableLibraryClassFile == null)
-        {
-            reusableLibraryClassFile = new LibraryClassFile();
-        }
-
-        // We'll start using the reusable object.
-        LibraryClassFile libraryClassFile = reusableLibraryClassFile;
-
-        libraryClassFile.read(din, skipNonPublicClasses, skipNonPublicClassMembers);
-
-        // Did we actually read a useful library class file?
-        if (libraryClassFile.thisClassName != null)
-        {
-            // We can't reuse this library class file object next time.
-            reusableLibraryClassFile = null;
-        }
-        else
-        {
-            // We don't have a useful library class file to return.
-            libraryClassFile = null;
-        }
-
-        return libraryClassFile;
-    }
-
-
-    /**
-     * Creates an empty LibraryClassFile.
-     */
-    private LibraryClassFile() {}
-
-
-    /**
-     * Imports the class data into this LibraryClassFile.
-     */
-    private void read(DataInput din,
-                      boolean skipNonPublicClasses,
-                      boolean skipNonPublicClassmembers) throws IOException
-    {
-        // Read and check the class file magic number.
-        int u4magic = din.readInt();
-        ClassUtil.checkMagicNumber(u4magic);
-
-        // Read and check the class file version numbers.
-        int u2minorVersion = din.readUnsignedShort();
-        int u2majorVersion = din.readUnsignedShort();
-        ClassUtil.checkVersionNumbers(u2majorVersion, u2minorVersion);
-
-        // Read the constant pool.
-        int u2constantPoolCount = din.readUnsignedShort();
-
-        // Make sure there's sufficient space in the reused constant pool array.
-        if (reusableConstantPool == null ||
-            reusableConstantPool.length < u2constantPoolCount)
-        {
-            reusableConstantPool = new CpInfo[u2constantPoolCount];
-        }
-
-        // Fill the constant pool. The zero entry is not used, nor are the
-        // entries following a Long or Double.
-        for (int i = 1; i < u2constantPoolCount; i++)
-        {
-            reusableConstantPool[i] = CpInfo.createOrShare(din);
-            int tag = reusableConstantPool[i].getTag();
-            if (tag == ClassConstants.CONSTANT_Long ||
-                tag == ClassConstants.CONSTANT_Double)
-            {
-                i++;
-            }
-        }
-
-        u2accessFlags = din.readUnsignedShort();
-
-        // We may stop parsing this library class file if it's not public anyway.
-        // E.g. only about 60% of all rt.jar classes need to be parsed.
-        if (skipNonPublicClasses && !isVisible())
-        {
-            return;
-        }
-
-        // Read the class and super class indices.
-        int u2thisClass  = din.readUnsignedShort();
-        int u2superClass = din.readUnsignedShort();
-
-        // Store their actual names.
-        thisClassName  = toName(reusableConstantPool, u2thisClass);
-        superClassName = (u2superClass == 0) ? null :
-                         toName(reusableConstantPool, u2superClass);
-
-        // Read the interface indices.
-        int u2interfacesCount = din.readUnsignedShort();
-
-        // Store their actual names.
-        interfaceNames = new String[u2interfacesCount];
-        for (int i = 0; i < u2interfacesCount; i++)
-        {
-            int u2interface = din.readUnsignedShort();
-            interfaceNames[i] = toName(reusableConstantPool, u2interface);
-        }
-
-        // Read the fields.
-        int u2fieldsCount = din.readUnsignedShort();
-
-        // Make sure there's sufficient space in the reused fields array.
-        if (reusableFields == null ||
-            reusableFields.length < u2fieldsCount)
-        {
-            reusableFields = new LibraryFieldInfo[u2fieldsCount];
-        }
-
-        int visibleFieldsCount = 0;
-        for (int i = 0; i < u2fieldsCount; i++)
-        {
-            LibraryFieldInfo field = LibraryFieldInfo.create(din, reusableConstantPool);
-
-            // Only store fields that are visible.
-            if (field.isVisible() ||
-                (!skipNonPublicClassmembers &&
-                 (field.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) == 0))
-            {
-                reusableFields[visibleFieldsCount++] = field;
-            }
-        }
-
-        // Copy the visible fields into a fields array of the right size.
-        fields = new LibraryFieldInfo[visibleFieldsCount];
-        System.arraycopy(reusableFields, 0, fields, 0, visibleFieldsCount);
-
-
-        // Read the methods.
-        int u2methodsCount = din.readUnsignedShort();
-
-        // Make sure there's sufficient space in the reused methods array.
-        if (reusableMethods == null ||
-            reusableMethods.length < u2methodsCount)
-        {
-            reusableMethods = new LibraryMethodInfo[u2methodsCount];
-        }
-
-        int visibleMethodsCount = 0;
-        for (int i = 0; i < u2methodsCount; i++)
-        {
-            LibraryMethodInfo method = LibraryMethodInfo.create(din, reusableConstantPool);
-
-            // Only store methods that are visible.
-            if (method.isVisible() ||
-                (!skipNonPublicClasses &&
-                 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) == 0))
-            {
-                reusableMethods[visibleMethodsCount++] = method;
-            }
-        }
-
-        // Copy the visible methods into a methods array of the right size.
-        methods = new LibraryMethodInfo[visibleMethodsCount];
-        System.arraycopy(reusableMethods, 0, methods, 0, visibleMethodsCount);
-
-
-        // Skip the attributes.
-        int u2attributesCount = din.readUnsignedShort();
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            LibraryAttrInfo.skip(din);
-        }
-    }
-
-
-    /**
-     * Returns whether this library class file is visible to the outside world.
-     */
-    boolean isVisible()
-    {
-        return (u2accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0;
-    }
-
-
-    /**
-     * Returns the class name of the ClassCpInfo at the specified index in the
-     * given constant pool.
-     */
-    private String toName(CpInfo[] constantPool, int cpIndex)
-    {
-        ClassCpInfo classEntry = (ClassCpInfo)constantPool[cpIndex];
-        Utf8CpInfo  nameEntry  = (Utf8CpInfo)constantPool[classEntry.getNameIndex()];
-
-        return nameEntry.getString();
-    }
-
-
-    // Implementations for ClassFile.
-
-    public int getAccessFlags()
-    {
-        return u2accessFlags;
-    }
-
-    public String getName()
-    {
-        return thisClassName;
-    }
-
-    public String getSuperName()
-    {
-        // This may be java/lang/Object, in which case there is no super.
-        return superClassName;
-    }
-
-    public int getInterfaceCount()
-    {
-        return interfaceClasses.length;
-    }
-
-    public String getInterfaceName(int index)
-    {
-        return interfaceNames[index];
-    }
-
-    public int getCpTag(int cpIndex)
-    {
-        return -1;
-    }
-
-    public String getCpString(int cpIndex)
-    {
-        return null;
-    }
-
-    public String getCpClassNameString(int cpIndex)
-    {
-        return null;
-    }
-
-    public String getCpNameString(int cpIndex)
-    {
-        return null;
-    }
-
-    public String getCpTypeString(int cpIndex)
-    {
-        return null;
-    }
-
-
-    public void addSubClass(ClassFile classFile)
-    {
-        if (subClasses == null)
-        {
-            subClasses = new ClassFile[1];
-        }
-        else
-        {
-            // Copy the old elements into new larger array.
-            ClassFile[] temp = new ClassFile[subClasses.length+1];
-            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
-            subClasses = temp;
-        }
-
-        subClasses[subClasses.length-1] = classFile;
-    }
-
-
-    public ClassFile getSuperClass()
-    {
-        return superClass;
-    }
-
-
-    public ClassFile getInterface(int index)
-    {
-        return interfaceClasses[index];
-    }
-
-
-    public boolean extends_(ClassFile classFile)
-    {
-        if (this.equals(classFile))
-        {
-            return true;
-        }
-
-        ClassFile superClass = getSuperClass();
-        return superClass != null &&
-               superClass.extends_(classFile);
-    }
-
-
-    public boolean implements_(ClassFile classFile)
-    {
-        if (this.equals(classFile))
-        {
-            return true;
-        }
-
-        if (interfaceClasses != null)
-        {
-            for (int i = 0; i < interfaceClasses.length; i++)
-            {
-                ClassFile interfaceClass = interfaceClasses[i];
-                if (interfaceClass != null &&
-                    interfaceClass.implements_(classFile))
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    public FieldInfo findField(String name, String descriptor)
-    {
-        for (int i = 0; i < fields.length; i++)
-        {
-            FieldInfo field = fields[i];
-            if (field != null &&
-                (name       == null || field.getName(this).equals(name)) &&
-                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
-            {
-                return field;
-            }
-        }
-
-        return null;
-    }
-
-
-    public MethodInfo findMethod(String name, String descriptor)
-    {
-        for (int i = 0; i < methods.length; i++)
-        {
-            MethodInfo method = methods[i];
-            if (method != null &&
-                (name       == null || method.getName(this).equals(name)) &&
-                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
-            {
-                return method;
-            }
-        }
-
-        return null;
-    }
-
-
-    public void accept(ClassFileVisitor classFileVisitor)
-    {
-        classFileVisitor.visitLibraryClassFile(this);
-    }
-
-
-    public void hierarchyAccept(boolean          visitThisClass,
-                                boolean          visitSuperClass,
-                                boolean          visitInterfaces,
-                                boolean          visitSubclasses,
-                                ClassFileVisitor classFileVisitor)
-    {
-        // First visit the current classfile.
-        if (visitThisClass)
-        {
-            accept(classFileVisitor);
-        }
-
-        // Then visit its superclass, recursively.
-        if (visitSuperClass)
-        {
-            if (superClass != null)
-            {
-                superClass.hierarchyAccept(true,
-                                           true,
-                                           visitInterfaces,
-                                           false,
-                                           classFileVisitor);
-            }
-        }
-
-        // Then visit its interfaces, recursively.
-        if (visitInterfaces)
-        {
-            if (interfaceClasses != null)
-            {
-                for (int i = 0; i < interfaceClasses.length; i++)
-                {
-                    ClassFile interfaceClass = interfaceClasses[i];
-                    if (interfaceClass != null)
-                    {
-                        interfaceClass.hierarchyAccept(true,
-                                                       true,
-                                                       true,
-                                                       false,
-                                                       classFileVisitor);
-                    }
-                }
-            }
-        }
-
-        // Then visit its subclasses, recursively.
-        if (visitSubclasses)
-        {
-            if (subClasses != null)
-            {
-                for (int i = 0; i < subClasses.length; i++)
-                {
-                    ClassFile subClass = subClasses[i];
-                    subClass.hierarchyAccept(true,
-                                             false,
-                                             false,
-                                             true,
-                                             classFileVisitor);
-                }
-            }
-        }
-    }
-
-
-    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor)
-    {
-        // This class doesn't keep references to its constant pool entries.
-    }
-
-
-    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor)
-    {
-        // This class doesn't keep references to its constant pool entries.
-    }
-
-
-    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor)
-    {
-        for (int i = 0; i < fields.length; i++)
-        {
-            if (fields[i] != null)
-            {
-                fields[i].accept(this, memberInfoVisitor);
-            }
-        }
-    }
-
-
-    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
-    {
-        FieldInfo field = findField(name, descriptor);
-        if (field != null)
-        {
-            field.accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public void methodsAccept(MemberInfoVisitor memberInfoVisitor)
-    {
-        for (int i = 0; i < methods.length; i++)
-        {
-            if (methods[i] != null)
-            {
-                methods[i].accept(this, memberInfoVisitor);
-            }
-        }
-    }
-
-
-    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
-    {
-        MethodInfo method = findMethod(name, descriptor);
-        if (method != null)
-        {
-            method.accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public boolean mayHaveImplementations(MethodInfo methodInfo)
-    {
-        return
-           (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 &&
-           (methodInfo == null ||
-            ((methodInfo.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
-                                             ClassConstants.INTERNAL_ACC_STATIC  |
-                                             ClassConstants.INTERNAL_ACC_FINAL)) == 0 &&
-             !methodInfo.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)));
-    }
-
-
-    private boolean isSpecial(MethodInfo methodInfo)
-    {
-        return
-            (methodInfo.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
-                                            ClassConstants.INTERNAL_ACC_STATIC)) != 0 ||
-            methodInfo.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
-    }
-
-
-    public void methodImplementationsAccept(MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(methodInfo.getName(this),
-                                    methodInfo.getDescriptor(this),
-                                    methodInfo,
-                                    visitThisMethod,
-                                    true,
-                                    true,
-                                    true,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(name,
-                                    descriptor,
-                                    visitThisMethod,
-                                    true,
-                                    true,
-                                    true,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSpecialMethods,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(name,
-                                    descriptor,
-                                    findMethod(name, descriptor),
-                                    visitThisMethod,
-                                    visitSpecialMethods,
-                                    visitSuperMethods,
-                                    visitOverridingMethods,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSpecialMethods,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        // Do we have the method in this class?
-        if (methodInfo != null)
-        {
-            // Is it a special method?
-            if (isSpecial(methodInfo))
-            {
-                // Visit the special method in this class, if allowed.
-                if (visitSpecialMethods)
-                {
-                    methodInfo.accept(this, memberInfoVisitor);
-
-                    // The method can't have any other implementations.
-                    return;
-                }
-            }
-            else
-            {
-                // Visit the method in this class, if allowed.
-                if (visitThisMethod)
-                {
-                    methodInfo.accept(this, memberInfoVisitor);
-                }
-
-                // We don't have to look in subclasses if there can't be
-                // any overriding implementations.
-                if (!mayHaveImplementations(methodInfo))
-                {
-                    visitOverridingMethods = false;
-                }
-
-                // We don't have to look in superclasses if we have a concrete
-                // implementation here.
-                if ((methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
-                {
-                    visitSuperMethods = false;
-                }
-            }
-        }
-
-        // Then visit the method in its subclasses, recursively.
-        if (visitOverridingMethods)
-        {
-            // Go looking for implementations in all of the subclasses.
-            if (subClasses != null)
-            {
-                for (int i = 0; i < subClasses.length; i++)
-                {
-                    ClassFile subClass = subClasses[i];
-                    subClass.methodImplementationsAccept(name,
-                                                         descriptor,
-                                                         true,
-                                                         false,
-                                                         visitSuperMethods,
-                                                         true,
-                                                         memberInfoVisitor);
-                }
-            }
-
-            // We don't have to look in superclasses right away if we dont't
-            // have a concrete class here.
-            if ((u2accessFlags & (ClassConstants.INTERNAL_ACC_INTERFACE |
-                                  ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
-            {
-                visitSuperMethods = false;
-            }
-        }
-
-        // Then visit the method in its superclass, recursively.
-        if (visitSuperMethods)
-        {
-            ClassFile superClass = getSuperClass();
-            if (superClass != null)
-            {
-                superClass.methodImplementationsAccept(name,
-                                                       descriptor,
-                                                       true,
-                                                       false,
-                                                       true,
-                                                       false,
-                                                       memberInfoVisitor);
-            }
-        }
-    }
-
-
-    public void attributesAccept(AttrInfoVisitor attrInfoVisitor)
-    {
-        // This class doesn't keep references to its attributes.
-    }
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/LibraryField.java b/src/proguard/classfile/LibraryField.java
new file mode 100644
index 0000000..265b10e
--- /dev/null
+++ b/src/proguard/classfile/LibraryField.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a field from a class-file.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryField extends LibraryMember implements Field
+{
+    /**
+     * An extra field pointing to the Clazz object referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public Clazz referencedClass;
+
+
+    /**
+     * Creates an uninitialized LibraryField.
+     */
+    public LibraryField()
+    {
+    }
+
+
+    // Implementations for LibraryMember.
+
+    public void accept(LibraryClass libraryClass, MemberVisitor memberVisitor)
+    {
+        memberVisitor.visitLibraryField(libraryClass, this);
+    }
+
+
+    // Implementations for Member.
+
+    public void referencedClassesAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClass != null)
+        {
+            referencedClass.accept(classVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/LibraryFieldInfo.java b/src/proguard/classfile/LibraryFieldInfo.java
deleted file mode 100644
index 8d18b64..0000000
--- a/src/proguard/classfile/LibraryFieldInfo.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $Id: LibraryFieldInfo.java,v 1.16.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a field from a class-file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LibraryFieldInfo extends LibraryMemberInfo implements FieldInfo
-{
-    /**
-     * An extra field pointing to the ClassFile object referenced in the
-     * descriptor string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to primitive types are ignored.
-     */
-    public ClassFile referencedClassFile;
-
-
-    /**
-     * Creates a new LibraryFieldInfo from the file format data in the DataInput stream.
-     *
-     * @throws IOException if class file is corrupt or incomplete
-     */
-    public static LibraryFieldInfo create(DataInput din, CpInfo[] constantPool) throws IOException
-    {
-        LibraryFieldInfo fi = new LibraryFieldInfo();
-        fi.read(din, constantPool);
-        return fi;
-    }
-
-
-    protected LibraryFieldInfo()
-    {
-    }
-
-
-    // Implementations for LibraryMemberInfo.
-
-    public void accept(LibraryClassFile libraryClassFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, this);
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/LibraryMember.java b/src/proguard/classfile/LibraryMember.java
new file mode 100644
index 0000000..92caf19
--- /dev/null
+++ b/src/proguard/classfile/LibraryMember.java
@@ -0,0 +1,90 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * Representation of a field or method from a library class.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class LibraryMember implements Member
+{
+    private static final int ACC_VISIBLE = ClassConstants.INTERNAL_ACC_PUBLIC |
+                                           ClassConstants.INTERNAL_ACC_PROTECTED;
+
+
+    public int    u2accessFlags;
+    public String name;
+    public String descriptor;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    protected LibraryMember() {}
+
+
+    /**
+     * Accepts the given member info visitor.
+     */
+    public abstract void accept(LibraryClass  libraryClass,
+                                MemberVisitor memberVisitor);
+
+
+    // Implementations for Member.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName(Clazz clazz)
+    {
+        return name;
+    }
+
+    public String getDescriptor(Clazz clazz)
+    {
+        return descriptor;
+    }
+
+    public void accept(Clazz clazz, MemberVisitor memberVisitor)
+    {
+        accept((LibraryClass)clazz, memberVisitor);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/LibraryMemberInfo.java b/src/proguard/classfile/LibraryMemberInfo.java
deleted file mode 100644
index 2b45b99..0000000
--- a/src/proguard/classfile/LibraryMemberInfo.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/* $Id: LibraryMemberInfo.java,v 1.22.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of a field or method from a library class file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-abstract public class LibraryMemberInfo implements MemberInfo
-{
-    private static final int ACC_VISIBLE = ClassConstants.INTERNAL_ACC_PUBLIC |
-                                           ClassConstants.INTERNAL_ACC_PROTECTED;
-
-
-    public int    u2accessFlags;
-    public String name;
-    public String descriptor;
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    protected LibraryMemberInfo() {}
-
-
-    /**
-     * Accepts the given member info visitor.
-     */
-    public abstract void accept(LibraryClassFile  libraryClassFile,
-                                MemberInfoVisitor memberInfoVisitor);
-
-
-    /**
-     * Imports the field or method data to internal representation.
-     */
-    protected void read(DataInput din, CpInfo[] constantPool) throws IOException
-    {
-        // Read the access flags.
-        u2accessFlags = din.readUnsignedShort();
-
-        // Read the name and descriptor indices.
-        int u2nameIndex       = din.readUnsignedShort();
-        int u2descriptorIndex = din.readUnsignedShort();
-
-        // Store the actual name and descriptor.
-        name       = ((Utf8CpInfo)constantPool[u2nameIndex]).getString();
-        descriptor = ((Utf8CpInfo)constantPool[u2descriptorIndex]).getString();
-
-        // Skip the attributes.
-        int u2attributesCount = din.readUnsignedShort();
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            LibraryAttrInfo.skip(din);
-        }
-    }
-
-
-    /**
-     * Returns whether this library member is visible to the outside world.
-     */
-    boolean isVisible()
-    {
-        return (u2accessFlags & ACC_VISIBLE) != 0;
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public int getAccessFlags()
-    {
-        return u2accessFlags;
-    }
-
-    public String getName(ClassFile classFile)
-    {
-        return name;
-    }
-
-    public String getDescriptor(ClassFile classFile)
-    {
-        return descriptor;
-    }
-
-    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        accept((LibraryClassFile)classFile, memberInfoVisitor);
-    }
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/LibraryMethod.java b/src/proguard/classfile/LibraryMethod.java
new file mode 100644
index 0000000..1f294f4
--- /dev/null
+++ b/src/proguard/classfile/LibraryMethod.java
@@ -0,0 +1,72 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a method from a class-file.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryMethod extends LibraryMember implements Method
+{
+    /**
+     * An extra field pointing to the Clazz objects referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public Clazz[] referencedClasses;
+
+
+    /**
+     * Creates an uninitialized LibraryMethod.
+     */
+    public LibraryMethod()
+    {
+    }
+
+
+    // Implementations for LibraryMember.
+
+    public void accept(LibraryClass libraryClass, MemberVisitor memberVisitor)
+    {
+        memberVisitor.visitLibraryMethod(libraryClass, this);
+    }
+
+
+    // Implementations for Member.
+
+    public void referencedClassesAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClasses != null)
+        {
+            for (int index = 0; index < referencedClasses.length; index++)
+            {
+                if (referencedClasses[index] != null)
+                {
+                    referencedClasses[index].accept(classVisitor);
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/LibraryMethodInfo.java b/src/proguard/classfile/LibraryMethodInfo.java
deleted file mode 100644
index a2d6aa3..0000000
--- a/src/proguard/classfile/LibraryMethodInfo.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/* $Id: LibraryMethodInfo.java,v 1.16.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a method from a class-file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LibraryMethodInfo extends LibraryMemberInfo implements MethodInfo
-{
-    /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * descriptor string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to primitive types are ignored.
-     */
-    public ClassFile[] referencedClassFiles;
-
-
-    /**
-     * Creates a new LibraryMethodInfo from the file format data in the DataInput stream.
-     *
-     * @throws IOException if class file is corrupt or incomplete
-     */
-    public static LibraryMethodInfo create(DataInput din, CpInfo[] constantPool) throws IOException
-    {
-        LibraryMethodInfo mi = new LibraryMethodInfo();
-        mi.read(din, constantPool);
-        return mi;
-    }
-
-
-    protected LibraryMethodInfo()
-    {
-    }
-
-
-    // Implementations for LibraryMemberInfo.
-
-    public void accept(LibraryClassFile libraryClassFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, this);
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFiles != null)
-        {
-            for (int i = 0; i < referencedClassFiles.length; i++)
-            {
-                if (referencedClassFiles[i] != null)
-                {
-                    referencedClassFiles[i].accept(classFileVisitor);
-                }
-            }
-        }
-    }
-}
diff --git a/src/proguard/classfile/LongCpInfo.java b/src/proguard/classfile/LongCpInfo.java
deleted file mode 100644
index a3b51f2..0000000
--- a/src/proguard/classfile/LongCpInfo.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $Id: LongCpInfo.java,v 1.17.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'long' entry in the ConstantPool (takes up two indices).
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LongCpInfo extends CpInfo
-{
-    public int u4highBytes;
-    public int u4lowBytes;
-
-
-    /**
-     * Creates a new LongCpInfo with the given long value.
-     */
-    public LongCpInfo(long value)
-    {
-        setValue(value);
-    }
-
-
-    protected LongCpInfo()
-    {
-    }
-
-
-    /**
-     * Returns the long value of this LongCpInfo.
-     */
-    public long getValue()
-    {
-        return (long)u4highBytes << 32 | (u4lowBytes & 0xffffffffL);
-    }
-
-
-    /**
-     * Sets the long value of this LongCpInfo.
-     */
-    public void setValue(long value)
-    {
-        u4highBytes = (int)(value >> 32);
-        u4lowBytes  = (int) value;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Long;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u4highBytes = din.readInt();
-        u4lowBytes = din.readInt();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeInt(u4highBytes);
-        dout.writeInt(u4lowBytes);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitLongCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/MemberInfo.java b/src/proguard/classfile/Member.java
similarity index 61%
rename from src/proguard/classfile/MemberInfo.java
rename to src/proguard/classfile/Member.java
index de66edc..c6cd3bc 100644
--- a/src/proguard/classfile/MemberInfo.java
+++ b/src/proguard/classfile/Member.java
@@ -1,8 +1,7 @@
-/* $Id: MemberInfo.java,v 1.20.2.3 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -24,12 +23,11 @@ package proguard.classfile;
 import proguard.classfile.visitor.*;
 
 /**
- * Representation of a field or method from a program class file.
+ * Representation of a field or method from a program class.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
-public interface MemberInfo extends VisitorAccepter
+public interface Member extends VisitorAccepter
 {
     /**
      * Returns access flags.
@@ -39,21 +37,21 @@ public interface MemberInfo extends VisitorAccepter
     /**
      * Returns method/field string name.
      */
-    public String getName(ClassFile classFile);
+    public String getName(Clazz clazz);
 
     /**
      * Returns descriptor string.
      */
-    public String getDescriptor(ClassFile classFile);
+    public String getDescriptor(Clazz clazz);
 
     /**
-     * Accepts the given class file visitor.
+     * Accepts the given class visitor.
      */
-    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor);
+    public void accept(Clazz clazz, MemberVisitor memberVisitor);
 
     /**
-     * Lets the ClassFile objects referenced in the descriptor string
+     * Lets the Clazz objects referenced in the descriptor string
      * accept the given visitor.
      */
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor);
+    public void referencedClassesAccept(ClassVisitor classVisitor);
 }
diff --git a/src/proguard/classfile/MethodInfo.java b/src/proguard/classfile/Method.java
similarity index 74%
rename from src/proguard/classfile/MethodInfo.java
rename to src/proguard/classfile/Method.java
index 855759f..4adf1fa 100644
--- a/src/proguard/classfile/MethodInfo.java
+++ b/src/proguard/classfile/Method.java
@@ -1,8 +1,7 @@
-/* $Id: MethodInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -24,10 +23,10 @@ package proguard.classfile;
 
 
 /**
- * Representation of a method from a class file.
+ * Representation of a method from a class.
  *
  * @author Eric Lafortune
  */
-public interface MethodInfo extends MemberInfo
+public interface Method extends Member
 {
 }
diff --git a/src/proguard/classfile/MethodrefCpInfo.java b/src/proguard/classfile/MethodrefCpInfo.java
deleted file mode 100644
index 08bc1f5..0000000
--- a/src/proguard/classfile/MethodrefCpInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id: MethodrefCpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-/**
- * Representation of a 'method reference' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class MethodrefCpInfo extends RefCpInfo
-{
-    protected MethodrefCpInfo()
-    {
-    }
-
-
-    /**
-     * Creates a new MethodrefCpInfo with the given name and type indices.
-     * @param u2classIndex         the index of the class in the constant pool.
-     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
-     * @param referencedClassFile  the referenced class file.
-     * @param referencedMemberInfo the referenced member info.
-     */
-    public MethodrefCpInfo(int        u2classIndex,
-                           int        u2nameAndTypeIndex,
-                           ClassFile  referencedClassFile,
-                           MemberInfo referencedMemberInfo)
-    {
-        this.u2classIndex         = u2classIndex;
-        this.u2nameAndTypeIndex   = u2nameAndTypeIndex;
-        this.referencedClassFile  = referencedClassFile;
-        this.referencedMemberInfo = referencedMemberInfo;
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_Methodref;
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitMethodrefCpInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/ProgramClass.java b/src/proguard/classfile/ProgramClass.java
new file mode 100644
index 0000000..27e678a
--- /dev/null
+++ b/src/proguard/classfile/ProgramClass.java
@@ -0,0 +1,587 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.ClassSubHierarchyInitializer;
+import proguard.classfile.visitor.*;
+
+/**
+ * This Clazz is a complete representation of the data in a Java class.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramClass implements Clazz
+{
+    public int             u4magic;
+    public int             u4version;
+    public int             u2constantPoolCount;
+    public Constant[]      constantPool;
+    public int             u2accessFlags;
+    public int             u2thisClass;
+    public int             u2superClass;
+    public int             u2interfacesCount;
+    public int[]           u2interfaces;
+    public int             u2fieldsCount;
+    public ProgramField[]  fields;
+    public int             u2methodsCount;
+    public ProgramMethod[] methods;
+    public int             u2attributesCount;
+    public Attribute[]     attributes;
+
+    /**
+     * An extra field pointing to the subclasses of this class.
+     * This field is filled out by the {@link ClassSubHierarchyInitializer}.
+     */
+    public Clazz[] subClasses;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Creates an uninitialized ProgramClass.
+     */
+    public ProgramClass() {}
+
+
+    /**
+     * Returns the Constant at the given index in the constant pool.
+     */
+    public Constant getConstant(int constantIndex)
+    {
+        return constantPool[constantIndex];
+    }
+
+
+    // Implementations for Clazz.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName()
+    {
+        return getClassName(u2thisClass);
+    }
+
+    public String getSuperName()
+    {
+        return u2superClass == 0 ? null : getClassName(u2superClass);
+    }
+
+    public int getInterfaceCount()
+    {
+        return u2interfacesCount;
+    }
+
+    public String getInterfaceName(int index)
+    {
+        return getClassName(u2interfaces[index]);
+    }
+
+    public int getTag(int constantIndex)
+    {
+        return constantPool[constantIndex].getTag();
+    }
+
+    public String getString(int constantIndex)
+    {
+        try
+        {
+            return ((Utf8Constant)constantPool[constantIndex]).getString();
+        }
+        catch (ClassCastException ex)
+        {
+            new ClassPrinter().visitProgramClass(this);
+            throw new ClassCastException("Expected Utf8Constant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]");
+        }
+    }
+
+    public String getStringString(int constantIndex)
+    {
+        try
+        {
+            return ((StringConstant)constantPool[constantIndex]).getString(this);
+        }
+        catch (ClassCastException ex)
+        {
+            throw new ClassCastException("Expected StringConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]");
+        }
+    }
+
+    public String getClassName(int constantIndex)
+    {
+        try
+        {
+            return ((ClassConstant)constantPool[constantIndex]).getName(this);
+        }
+        catch (ClassCastException ex)
+        {
+            throw new ClassCastException("Expected ClassConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]");
+        }
+    }
+
+    public String getName(int constantIndex)
+    {
+        try
+        {
+            return ((NameAndTypeConstant)constantPool[constantIndex]).getName(this);
+        }
+        catch (ClassCastException ex)
+        {
+            throw new ClassCastException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]");
+        }
+    }
+
+    public String getType(int constantIndex)
+    {
+        try
+        {
+            return ((NameAndTypeConstant)constantPool[constantIndex]).getType(this);
+        }
+        catch (ClassCastException ex)
+        {
+            throw new ClassCastException("Expected NameAndTypeConstant at index ["+constantIndex+"] in class ["+getName()+"], found ["+ex.getMessage()+"]");
+        }
+    }
+
+
+    public void addSubClass(Clazz clazz)
+    {
+        if (subClasses == null)
+        {
+            subClasses = new Clazz[1];
+        }
+        else
+        {
+            // Copy the old elements into new larger array.
+            Clazz[] temp = new Clazz[subClasses.length+1];
+            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
+            subClasses = temp;
+        }
+
+        subClasses[subClasses.length-1] = clazz;
+    }
+
+
+    public Clazz getSuperClass()
+    {
+        return u2superClass != 0 ?
+            ((ClassConstant)constantPool[u2superClass]).referencedClass :
+            null;
+    }
+
+
+    public Clazz getInterface(int index)
+    {
+        return ((ClassConstant)constantPool[u2interfaces[index]]).referencedClass;
+    }
+
+
+    public boolean extends_(Clazz clazz)
+    {
+        if (this.equals(clazz))
+        {
+            return true;
+        }
+
+        Clazz superClass = getSuperClass();
+        return superClass != null &&
+               superClass.extends_(clazz);
+    }
+
+
+    public boolean extendsOrImplements(Clazz clazz)
+    {
+        if (this.equals(clazz))
+        {
+            return true;
+        }
+
+        Clazz superClass = getSuperClass();
+        if (superClass != null &&
+            superClass.extendsOrImplements(clazz))
+        {
+            return true;
+        }
+
+        for (int index = 0; index < u2interfacesCount; index++)
+        {
+            Clazz interfaceClass = getInterface(index);
+            if (interfaceClass != null &&
+                interfaceClass.extendsOrImplements(clazz))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    public Field findField(String name, String descriptor)
+    {
+        for (int index = 0; index < u2fieldsCount; index++)
+        {
+            Field field = fields[index];
+            if ((name       == null || field.getName(this).equals(name)) &&
+                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
+            {
+                return field;
+            }
+        }
+
+        return null;
+    }
+
+
+    public Method findMethod(String name, String descriptor)
+    {
+        for (int index = 0; index < u2methodsCount; index++)
+        {
+            Method method = methods[index];
+            if ((name       == null || method.getName(this).equals(name)) &&
+                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
+            {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+
+    public void accept(ClassVisitor classVisitor)
+    {
+        classVisitor.visitProgramClass(this);
+    }
+
+
+    public void hierarchyAccept(boolean      visitThisClass,
+                                boolean      visitSuperClass,
+                                boolean      visitInterfaces,
+                                boolean      visitSubclasses,
+                                ClassVisitor classVisitor)
+    {
+        // First visit the current classfile.
+        if (visitThisClass)
+        {
+            accept(classVisitor);
+        }
+
+        // Then visit its superclass, recursively.
+        if (visitSuperClass)
+        {
+            Clazz superClass = getSuperClass();
+            if (superClass != null)
+            {
+                superClass.hierarchyAccept(true,
+                                           true,
+                                           visitInterfaces,
+                                           false,
+                                           classVisitor);
+            }
+        }
+
+        // Then visit its interfaces, recursively.
+        if (visitInterfaces)
+        {
+            for (int index = 0; index < u2interfacesCount; index++)
+            {
+                Clazz interfaceClass = getInterface(index);
+                if (interfaceClass != null)
+                {
+                    interfaceClass.hierarchyAccept(true,
+                                                   true,
+                                                   true,
+                                                   false,
+                                                   classVisitor);
+                }
+            }
+        }
+
+        // Then visit its subclasses, recursively.
+        if (visitSubclasses)
+        {
+            if (subClasses != null)
+            {
+                for (int index = 0; index < subClasses.length; index++)
+                {
+                    Clazz subClass = subClasses[index];
+                    subClass.hierarchyAccept(true,
+                                             false,
+                                             false,
+                                             true,
+                                             classVisitor);
+                }
+            }
+        }
+    }
+
+
+    public void constantPoolEntriesAccept(ConstantVisitor constantVisitor)
+    {
+        for (int index = 1; index < u2constantPoolCount; index++)
+        {
+            if (constantPool[index] != null)
+            {
+                constantPool[index].accept(this, constantVisitor);
+            }
+        }
+    }
+
+
+    public void constantPoolEntryAccept(int index, ConstantVisitor constantVisitor)
+    {
+        constantPool[index].accept(this, constantVisitor);
+    }
+
+
+    public void fieldsAccept(MemberVisitor memberVisitor)
+    {
+        for (int index = 0; index < u2fieldsCount; index++)
+        {
+            fields[index].accept(this, memberVisitor);
+        }
+    }
+
+
+    public void fieldAccept(String name, String descriptor, MemberVisitor memberVisitor)
+    {
+        Field field = findField(name, descriptor);
+        if (field != null)
+        {
+            field.accept(this, memberVisitor);
+        }
+    }
+
+
+    public void methodsAccept(MemberVisitor memberVisitor)
+    {
+        for (int index = 0; index < u2methodsCount; index++)
+        {
+            methods[index].accept(this, memberVisitor);
+        }
+    }
+
+
+    public void methodAccept(String name, String descriptor, MemberVisitor memberVisitor)
+    {
+        Method method = findMethod(name, descriptor);
+        if (method != null)
+        {
+            method.accept(this, memberVisitor);
+        }
+    }
+
+
+    public boolean mayHaveImplementations(Method method)
+    {
+        return
+            (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 &&
+            (method == null ||
+             ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
+                                          ClassConstants.INTERNAL_ACC_STATIC  |
+                                          ClassConstants.INTERNAL_ACC_FINAL)) == 0 &&
+              !method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)));
+    }
+
+
+    private boolean isSpecial(Method method)
+    {
+        return
+            (method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
+                                        ClassConstants.INTERNAL_ACC_STATIC)) != 0 ||
+            method.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+    }
+
+
+    public void methodImplementationsAccept(Method        method,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(method.getName(this),
+                                    method.getDescriptor(this),
+                                    method,
+                                    visitThisMethod,
+                                    true,
+                                    true,
+                                    true,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            boolean       visitThisMethod,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(name,
+                                    descriptor,
+                                    visitThisMethod,
+                                    true,
+                                    true,
+                                    true,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSpecialMethods,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            MemberVisitor memberVisitor)
+    {
+        methodImplementationsAccept(name,
+                                    descriptor,
+                                    findMethod(name, descriptor),
+                                    visitThisMethod,
+                                    visitSpecialMethods,
+                                    visitSuperMethods,
+                                    visitOverridingMethods,
+                                    memberVisitor);
+    }
+
+
+    public void methodImplementationsAccept(String        name,
+                                            String        descriptor,
+                                            Method        method,
+                                            boolean       visitThisMethod,
+                                            boolean       visitSpecialMethods,
+                                            boolean       visitSuperMethods,
+                                            boolean       visitOverridingMethods,
+                                            MemberVisitor memberVisitor)
+    {
+        // Do we have the method in this class?
+        if (method != null)
+        {
+            // Is it a special method?
+            if (isSpecial(method))
+            {
+                // Visit the special method in this class, if allowed.
+                if (visitSpecialMethods)
+                {
+                    method.accept(this, memberVisitor);
+
+                    // The method can't have any other implementations.
+                    return;
+                }
+            }
+            else
+            {
+                // Visit the method in this class, if allowed.
+                if (visitThisMethod)
+                {
+                    method.accept(this, memberVisitor);
+                }
+
+                // We don't have to look in subclasses if there can't be
+                // any overriding implementations.
+                if (!mayHaveImplementations(method))
+                {
+                    visitOverridingMethods = false;
+                }
+
+                // We don't have to look in superclasses if we have a concrete
+                // implementation here.
+                if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
+                {
+                    visitSuperMethods = false;
+                }
+            }
+        }
+
+        // Then visit the method in its subclasses, recursively.
+        if (visitOverridingMethods)
+        {
+            // Go looking for implementations in all of the subclasses.
+            if (subClasses != null)
+            {
+                for (int index = 0; index < subClasses.length; index++)
+                {
+                    Clazz subClass = subClasses[index];
+                    subClass.methodImplementationsAccept(name,
+                                                         descriptor,
+                                                         true,
+                                                         false,
+                                                         visitSuperMethods,
+                                                         true,
+                                                         memberVisitor);
+                }
+            }
+
+            // We don't have to look in superclasses right away if we dont't
+            // have a concrete class here.
+            if ((u2accessFlags & (ClassConstants.INTERNAL_ACC_INTERFACE |
+                                  ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
+            {
+                visitSuperMethods = false;
+            }
+        }
+
+        // Then visit the method in its superclass, recursively.
+        if (visitSuperMethods)
+        {
+            Clazz superClass = getSuperClass();
+            if (superClass != null)
+            {
+                superClass.methodImplementationsAccept(name,
+                                                       descriptor,
+                                                       true,
+                                                       false,
+                                                       true,
+                                                       false,
+                                                       memberVisitor);
+            }
+        }
+    }
+
+
+    public void attributesAccept(AttributeVisitor attributeVisitor)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            attributes[index].accept(this, attributeVisitor);
+        }
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/ProgramClassFile.java b/src/proguard/classfile/ProgramClassFile.java
deleted file mode 100644
index d56b732..0000000
--- a/src/proguard/classfile/ProgramClassFile.java
+++ /dev/null
@@ -1,706 +0,0 @@
-/* $Id: ProgramClassFile.java,v 1.37.2.3 2007/01/25 21:01:01 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * This is a complete representation of the data in a Java class file.
- * A ClassFile instance can be generated using the static create(DataInput)
- * method, manipulated using various operators, and persisted back using the
- * write(DataOutput) method.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ProgramClassFile implements ClassFile
-{
-    public int                 u4magic;
-    public int                 u2minorVersion;
-    public int                 u2majorVersion;
-    public int                 u2constantPoolCount;
-    public CpInfo[]            constantPool;
-    public int                 u2accessFlags;
-    public int                 u2thisClass;
-    public int                 u2superClass;
-    public int                 u2interfacesCount;
-    public int[]               u2interfaces;
-    public int                 u2fieldsCount;
-    public ProgramFieldInfo[]  fields;
-    public int                 u2methodsCount;
-    public ProgramMethodInfo[] methods;
-    public int                 u2attributesCount;
-    public AttrInfo[]          attributes;
-
-    /**
-     * An extra field pointing to the subclasses of this class.
-     * This field is filled out by the <code>{@link ClassFileHierarchyInitializer}</code>.
-     */
-    public ClassFile[] subClasses = null;
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    /**
-     * Creates a new ClassFile from the class file format data in the DataInput
-     * stream.
-     *
-     * @throws IOException if class file is corrupt or incomplete
-     */
-    public static ClassFile create(DataInput din) throws IOException
-    {
-        ProgramClassFile cf = new ProgramClassFile();
-        cf.read(din);
-        return cf;
-    }
-
-
-    /**
-     * Creates an empty ProgramClassFile.
-     */
-    private ProgramClassFile() {}
-
-
-    /**
-     * Imports the class data into this ProgramClassFile.
-     */
-    private void read(DataInput din) throws IOException
-    {
-        // Read and check the class file magic number.
-        u4magic = din.readInt();
-        ClassUtil.checkMagicNumber(u4magic);
-
-        // Read and check the class file version numbers.
-        u2minorVersion = din.readUnsignedShort();
-        u2majorVersion = din.readUnsignedShort();
-        ClassUtil.checkVersionNumbers(u2majorVersion, u2minorVersion);
-
-        // Read the constant pool.
-        u2constantPoolCount = din.readUnsignedShort();
-        constantPool        = new CpInfo[u2constantPoolCount];
-
-        // Fill the constant pool. The zero entry is not used, nor are the
-        // entries following a Long or Double.
-        for (int i = 1; i < u2constantPoolCount; i++)
-        {
-            constantPool[i] = CpInfo.create(din);
-
-            int tag = constantPool[i].getTag();
-            if (tag == ClassConstants.CONSTANT_Long ||
-                tag == ClassConstants.CONSTANT_Double)
-            {
-                i++;
-            }
-        }
-
-        // Read the access flags, class name, and super class name.
-        u2accessFlags = din.readUnsignedShort();
-        u2thisClass   = din.readUnsignedShort();
-        u2superClass  = din.readUnsignedShort();
-
-        // Read the interfaces.
-        u2interfacesCount = din.readUnsignedShort();
-        if (u2interfacesCount > 0)
-        {
-            u2interfaces = new int[u2interfacesCount];
-            for (int i = 0; i < u2interfacesCount; i++)
-            {
-                u2interfaces[i] = din.readUnsignedShort();
-            }
-        }
-
-        // Read the fields.
-        u2fieldsCount = din.readUnsignedShort();
-        if (u2fieldsCount > 0)
-        {
-            fields = new ProgramFieldInfo[u2fieldsCount];
-            for (int i = 0; i < u2fieldsCount; i++)
-            {
-                fields[i] = ProgramFieldInfo.create(din, this);
-            }
-        }
-
-        // Read the methods.
-        u2methodsCount = din.readUnsignedShort();
-        if (u2methodsCount > 0)
-        {
-            methods = new ProgramMethodInfo[u2methodsCount];
-            for (int i = 0; i < u2methodsCount; i++)
-            {
-                methods[i] = ProgramMethodInfo.create(din, this);
-            }
-        }
-
-        // Read the attributes.
-        u2attributesCount = din.readUnsignedShort();
-        if (u2attributesCount > 0)
-        {
-            attributes = new AttrInfo[u2attributesCount];
-            for (int i = 0; i < u2attributesCount; i++)
-            {
-                attributes[i] = AttrInfo.create(din, this);
-            }
-        }
-    }
-
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public void write(DataOutput dout) throws IOException
-    {
-        dout.writeInt(u4magic);
-        dout.writeShort(u2minorVersion);
-        dout.writeShort(u2majorVersion);
-        dout.writeShort(u2constantPoolCount);
-        for (int i = 1; i < u2constantPoolCount; i++)
-        {
-            CpInfo cpInfo = constantPool[i];
-            if (cpInfo != null)
-            {
-                cpInfo.write(dout);
-            }
-        }
-        dout.writeShort(u2accessFlags);
-        dout.writeShort(u2thisClass);
-        dout.writeShort(u2superClass);
-        dout.writeShort(u2interfacesCount);
-        for (int i = 0; i < u2interfacesCount; i++)
-        {
-            dout.writeShort(u2interfaces[i]);
-        }
-        dout.writeShort(u2fieldsCount);
-        for (int i = 0; i < u2fieldsCount; i++)
-        {
-            fields[i].write(dout);
-        }
-        dout.writeShort(u2methodsCount);
-        for (int i = 0; i < u2methodsCount; i++)
-        {
-            methods[i].write(dout);
-        }
-        dout.writeShort(u2attributesCount);
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].write(dout);
-        }
-    }
-
-
-    /**
-     * Returns the CpInfo at the given index in the constant pool.
-     */
-    public CpInfo getCpEntry(int cpIndex)
-    {
-        return constantPool[cpIndex];
-    }
-
-
-    /**
-     * Returns the attribute specified by the given name.
-     */
-    AttrInfo getAttribute(String name)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            AttrInfo attribute = attributes[i];
-            if (attribute.getAttributeName(this).equals(name))
-            {
-                return attribute;
-            }
-        }
-
-        return null;
-    }
-
-
-    // Implementations for ClassFile.
-
-    public int getAccessFlags()
-    {
-        return u2accessFlags;
-    }
-
-    public String getName()
-    {
-        return getCpClassNameString(u2thisClass);
-    }
-
-    public String getSuperName()
-    {
-        return u2superClass == 0 ? null : getCpClassNameString(u2superClass);
-    }
-
-    public int getInterfaceCount()
-    {
-        return u2interfacesCount;
-    }
-
-    public String getInterfaceName(int index)
-    {
-        return getCpClassNameString(u2interfaces[index]);
-    }
-
-    public int getCpTag(int cpIndex)
-    {
-        return constantPool[cpIndex].getTag();
-    }
-
-    public String getCpString(int cpIndex)
-    {
-        return ((Utf8CpInfo)constantPool[cpIndex]).getString();
-    }
-
-    public String getCpClassNameString(int cpIndex)
-    {
-        ClassCpInfo classEntry = (ClassCpInfo)constantPool[cpIndex];
-        Utf8CpInfo  nameEntry  = (Utf8CpInfo)constantPool[classEntry.getNameIndex()];
-
-        return nameEntry.getString();
-    }
-
-    public String getCpNameString(int cpIndex)
-    {
-        return ((NameAndTypeCpInfo)constantPool[cpIndex]).getName(this);
-    }
-
-    public String getCpTypeString(int cpIndex)
-    {
-        return ((NameAndTypeCpInfo)constantPool[cpIndex]).getType(this);
-    }
-
-
-    public void addSubClass(ClassFile classFile)
-    {
-        if (subClasses == null)
-        {
-            subClasses = new ClassFile[1];
-        }
-        else
-        {
-            // Copy the old elements into new larger array.
-            ClassFile[] temp = new ClassFile[subClasses.length+1];
-            System.arraycopy(subClasses, 0, temp, 0, subClasses.length);
-            subClasses = temp;
-        }
-
-        subClasses[subClasses.length-1] = classFile;
-    }
-
-
-    public ClassFile getSuperClass()
-    {
-        return u2superClass != 0 ?
-            ((ClassCpInfo)constantPool[u2superClass]).referencedClassFile :
-            null;
-    }
-
-
-    public ClassFile getInterface(int index)
-    {
-        return ((ClassCpInfo)constantPool[u2interfaces[index]]).referencedClassFile;
-    }
-
-
-    public boolean extends_(ClassFile classFile)
-    {
-        if (this.equals(classFile))
-        {
-            return true;
-        }
-
-        ClassFile superClass = getSuperClass();
-        return superClass != null &&
-               superClass.extends_(classFile);
-    }
-
-
-    public boolean implements_(ClassFile classFile)
-    {
-        if (this.equals(classFile))
-        {
-            return true;
-        }
-
-        for (int i = 0; i < u2interfacesCount; i++)
-        {
-            ClassFile interfaceClass = getInterface(i);
-            if (interfaceClass != null &&
-                interfaceClass.implements_(classFile))
-            {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    public FieldInfo findField(String name, String descriptor)
-    {
-        for (int i = 0; i < u2fieldsCount; i++)
-        {
-            FieldInfo field = fields[i];
-            if ((name       == null || field.getName(this).equals(name)) &&
-                (descriptor == null || field.getDescriptor(this).equals(descriptor)))
-            {
-                return field;
-            }
-        }
-
-        return null;
-    }
-
-
-    public MethodInfo findMethod(String name, String descriptor)
-    {
-        for (int i = 0; i < u2methodsCount; i++)
-        {
-            MethodInfo method = methods[i];
-            if ((name       == null || method.getName(this).equals(name)) &&
-                (descriptor == null || method.getDescriptor(this).equals(descriptor)))
-            {
-                return method;
-            }
-        }
-
-        return null;
-    }
-
-
-    public void accept(ClassFileVisitor classFileVisitor)
-    {
-        classFileVisitor.visitProgramClassFile(this);
-    }
-
-
-    public void hierarchyAccept(boolean          visitThisClass,
-                                boolean          visitSuperClass,
-                                boolean          visitInterfaces,
-                                boolean          visitSubclasses,
-                                ClassFileVisitor classFileVisitor)
-    {
-        // First visit the current classfile.
-        if (visitThisClass)
-        {
-            accept(classFileVisitor);
-        }
-
-        // Then visit its superclass, recursively.
-        if (visitSuperClass)
-        {
-            ClassFile superClass = getSuperClass();
-            if (superClass != null)
-            {
-                superClass.hierarchyAccept(true,
-                                           true,
-                                           visitInterfaces,
-                                           false,
-                                           classFileVisitor);
-            }
-        }
-
-        // Then visit its interfaces, recursively.
-        if (visitInterfaces)
-        {
-            for (int i = 0; i < u2interfacesCount; i++)
-            {
-                ClassFile interfaceClass = getInterface(i);
-                if (interfaceClass != null)
-                {
-                    interfaceClass.hierarchyAccept(true,
-                                                   true,
-                                                   true,
-                                                   false,
-                                                   classFileVisitor);
-                }
-            }
-        }
-
-        // Then visit its subclasses, recursively.
-        if (visitSubclasses)
-        {
-            if (subClasses != null)
-            {
-                for (int i = 0; i < subClasses.length; i++)
-                {
-                    ClassFile subClass = subClasses[i];
-                    subClass.hierarchyAccept(true,
-                                             false,
-                                             false,
-                                             true,
-                                             classFileVisitor);
-                }
-            }
-        }
-    }
-
-
-    public void constantPoolEntriesAccept(CpInfoVisitor cpInfoVisitor)
-    {
-        for (int i = 1; i < u2constantPoolCount; i++)
-        {
-            if (constantPool[i] != null)
-            {
-                constantPool[i].accept(this, cpInfoVisitor);
-            }
-        }
-    }
-
-
-    public void constantPoolEntryAccept(int index, CpInfoVisitor cpInfoVisitor)
-    {
-        constantPool[index].accept(this, cpInfoVisitor);
-    }
-
-
-    public void fieldsAccept(MemberInfoVisitor memberInfoVisitor)
-    {
-        for (int i = 0; i < u2fieldsCount; i++)
-        {
-            fields[i].accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public void fieldAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
-    {
-        FieldInfo field = findField(name, descriptor);
-        if (field != null)
-        {
-            field.accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public void methodsAccept(MemberInfoVisitor memberInfoVisitor)
-    {
-        for (int i = 0; i < u2methodsCount; i++)
-        {
-            methods[i].accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public void methodAccept(String name, String descriptor, MemberInfoVisitor memberInfoVisitor)
-    {
-        MethodInfo method = findMethod(name, descriptor);
-        if (method != null)
-        {
-            method.accept(this, memberInfoVisitor);
-        }
-    }
-
-
-    public boolean mayHaveImplementations(MethodInfo methodInfo)
-    {
-        return
-           (u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 &&
-           (methodInfo == null ||
-            ((methodInfo.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
-                                             ClassConstants.INTERNAL_ACC_STATIC  |
-                                             ClassConstants.INTERNAL_ACC_FINAL)) == 0 &&
-             !methodInfo.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)));
-    }
-
-
-    private boolean isSpecial(MethodInfo methodInfo)
-    {
-        return
-            (methodInfo.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE |
-                                            ClassConstants.INTERNAL_ACC_STATIC)) != 0 ||
-            methodInfo.getName(this).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
-    }
-
-
-    public void methodImplementationsAccept(MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(methodInfo.getName(this),
-                                    methodInfo.getDescriptor(this),
-                                    methodInfo,
-                                    visitThisMethod,
-                                    true,
-                                    true,
-                                    true,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            boolean           visitThisMethod,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(name,
-                                    descriptor,
-                                    visitThisMethod,
-                                    true,
-                                    true,
-                                    true,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSpecialMethods,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        methodImplementationsAccept(name,
-                                    descriptor,
-                                    findMethod(name, descriptor),
-                                    visitThisMethod,
-                                    visitSpecialMethods,
-                                    visitSuperMethods,
-                                    visitOverridingMethods,
-                                    memberInfoVisitor);
-    }
-
-
-    public void methodImplementationsAccept(String            name,
-                                            String            descriptor,
-                                            MethodInfo        methodInfo,
-                                            boolean           visitThisMethod,
-                                            boolean           visitSpecialMethods,
-                                            boolean           visitSuperMethods,
-                                            boolean           visitOverridingMethods,
-                                            MemberInfoVisitor memberInfoVisitor)
-    {
-        // Do we have the method in this class?
-        if (methodInfo != null)
-        {
-            // Is it a special method?
-            if (isSpecial(methodInfo))
-            {
-                // Visit the special method in this class, if allowed.
-                if (visitSpecialMethods)
-                {
-                    methodInfo.accept(this, memberInfoVisitor);
-
-                    // The method can't have any other implementations.
-                    return;
-                }
-            }
-            else
-            {
-                // Visit the method in this class, if allowed.
-                if (visitThisMethod)
-                {
-                    methodInfo.accept(this, memberInfoVisitor);
-                }
-
-                // We don't have to look in subclasses if there can't be
-                // any overriding implementations.
-                if (!mayHaveImplementations(methodInfo))
-                {
-                    visitOverridingMethods = false;
-                }
-
-                // We don't have to look in superclasses if we have a concrete
-                // implementation here.
-                if ((methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
-                {
-                    visitSuperMethods = false;
-                }
-            }
-        }
-
-        // Then visit the method in its subclasses, recursively.
-        if (visitOverridingMethods)
-        {
-            // Go looking for implementations in all of the subclasses.
-            if (subClasses != null)
-            {
-                for (int i = 0; i < subClasses.length; i++)
-                {
-                    ClassFile subClass = subClasses[i];
-                    subClass.methodImplementationsAccept(name,
-                                                         descriptor,
-                                                         true,
-                                                         false,
-                                                         visitSuperMethods,
-                                                         true,
-                                                         memberInfoVisitor);
-                }
-            }
-
-            // We don't have to look in superclasses right away if we dont't
-            // have a concrete class here.
-            if ((u2accessFlags & (ClassConstants.INTERNAL_ACC_INTERFACE |
-                                  ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
-            {
-                visitSuperMethods = false;
-            }
-        }
-
-        // Then visit the method in its superclass, recursively.
-        if (visitSuperMethods)
-        {
-            ClassFile superClass = getSuperClass();
-            if (superClass != null)
-            {
-                superClass.methodImplementationsAccept(name,
-                                                       descriptor,
-                                                       true,
-                                                       false,
-                                                       true,
-                                                       false,
-                                                       memberInfoVisitor);
-            }
-        }
-    }
-
-
-    public void attributesAccept(AttrInfoVisitor attrInfoVisitor)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].accept(this, attrInfoVisitor);
-        }
-    }
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/ProgramField.java b/src/proguard/classfile/ProgramField.java
new file mode 100644
index 0000000..58720b1
--- /dev/null
+++ b/src/proguard/classfile/ProgramField.java
@@ -0,0 +1,76 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a field from a program class.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramField extends ProgramMember implements Field
+{
+    /**
+     * An extra field pointing to the Clazz object referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public Clazz referencedClass;
+
+
+    /**
+     * Creates an uninitialized ProgramField.
+     */
+    public ProgramField()
+    {
+    }
+
+
+    // Implementations for ProgramMember.
+
+    public void accept(ProgramClass programClass, MemberVisitor memberVisitor)
+    {
+        memberVisitor.visitProgramField(programClass, this);
+    }
+
+
+    public void attributesAccept(ProgramClass programClass, AttributeVisitor attributeVisitor)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            attributes[index].accept(programClass, this, attributeVisitor);
+        }
+    }
+
+
+    // Implementations for Member.
+
+    public void referencedClassesAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClass != null)
+        {
+            referencedClass.accept(classVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/ProgramFieldInfo.java b/src/proguard/classfile/ProgramFieldInfo.java
deleted file mode 100644
index 8280c8a..0000000
--- a/src/proguard/classfile/ProgramFieldInfo.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/* $Id: ProgramFieldInfo.java,v 1.18.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of a field from a program class file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ProgramFieldInfo extends ProgramMemberInfo implements FieldInfo
-{
-    /**
-     * An extra field pointing to the ClassFile object referenced in the
-     * descriptor string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to primitive types are ignored.
-     */
-    public ClassFile referencedClassFile;
-
-
-    /**
-     * Creates a new ProgramFieldInfo from the file format data in the DataInput stream.
-     *
-     * @throws IOException if class file is corrupt or incomplete
-     */
-    public static ProgramFieldInfo create(DataInput din, ClassFile cf) throws IOException
-    {
-        ProgramFieldInfo fi = new ProgramFieldInfo();
-        fi.read(din, cf);
-        return fi;
-    }
-
-
-    protected ProgramFieldInfo()
-    {
-    }
-
-
-    // Implementations for ProgramMemberInfo.
-
-    public void accept(ProgramClassFile programClassFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        memberInfoVisitor.visitProgramFieldInfo(programClassFile, this);
-    }
-
-
-    public void attributesAccept(ProgramClassFile programClassFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].accept(programClassFile, this, attrInfoVisitor);
-        }
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/ProgramMember.java b/src/proguard/classfile/ProgramMember.java
new file mode 100644
index 0000000..6fe5fb3
--- /dev/null
+++ b/src/proguard/classfile/ProgramMember.java
@@ -0,0 +1,146 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * Representation of a field or method from a program class.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class ProgramMember implements Member
+{
+    public int         u2accessFlags;
+    public int         u2nameIndex;
+    public int         u2descriptorIndex;
+    public int         u2attributesCount;
+    public Attribute[] attributes;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    protected ProgramMember() {}
+
+
+    /**
+     * Returns the line number range of the given class member as "m:n",
+     * if it can find it, or <code>null</code> otherwise.
+     */
+    public String getLineNumberRange(Clazz clazz)
+    {
+        CodeAttribute codeAttribute =
+            (CodeAttribute)getAttribute(clazz, ClassConstants.ATTR_Code);
+        if (codeAttribute  == null)
+        {
+            return null;
+        }
+
+        LineNumberTableAttribute lineNumberTableAttribute =
+            (LineNumberTableAttribute)codeAttribute.getAttribute(clazz,
+                                                                 ClassConstants.ATTR_LineNumberTable);
+        if (lineNumberTableAttribute  == null)
+        {
+            return null;
+        }
+
+        return "" +
+               lineNumberTableAttribute.getLineNumber(0) +
+               ":" +
+               lineNumberTableAttribute.getLineNumber(Integer.MAX_VALUE);
+    }
+
+
+    /**
+     * Returns the (first) attribute with the given name.
+     */
+    private Attribute getAttribute(Clazz clazz, String name)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            Attribute attribute = attributes[index];
+            if (attribute.getAttributeName(clazz).equals(name))
+            {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Accepts the given member info visitor.
+     */
+    public abstract void accept(ProgramClass  programClass,
+                                MemberVisitor memberVisitor);
+
+
+
+    /**
+     * Lets the given attribute info visitor visit all the attributes of
+     * this member info.
+     */
+    public abstract void attributesAccept(ProgramClass     programClass,
+                                          AttributeVisitor attributeVisitor);
+
+
+    // Implementations for Member.
+
+    public int getAccessFlags()
+    {
+        return u2accessFlags;
+    }
+
+    public String getName(Clazz clazz)
+    {
+        return clazz.getString(u2nameIndex);
+    }
+
+    public String getDescriptor(Clazz clazz)
+    {
+        return clazz.getString(u2descriptorIndex);
+    }
+
+    public void accept(Clazz clazz, MemberVisitor memberVisitor)
+    {
+        accept((ProgramClass)clazz, memberVisitor);
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/ProgramMemberInfo.java b/src/proguard/classfile/ProgramMemberInfo.java
deleted file mode 100644
index c61829a..0000000
--- a/src/proguard/classfile/ProgramMemberInfo.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/* $Id: ProgramMemberInfo.java,v 1.29.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of a field or method from a program class file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-abstract public class ProgramMemberInfo implements MemberInfo
-{
-    public int        u2accessFlags;
-    public int        u2nameIndex;
-    public int        u2descriptorIndex;
-    public int        u2attributesCount;
-    public AttrInfo[] attributes;
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    protected ProgramMemberInfo() {}
-
-
-    /**
-     * Returns the line number range of the given class member as "m:n",
-     * if it can find it, or <code>null</code> otherwise.
-     */
-    public String getLineNumberRange(ClassFile classFile)
-    {
-        CodeAttrInfo codeAttribute =
-            (CodeAttrInfo)getAttribute(classFile, ClassConstants.ATTR_Code);
-        if (codeAttribute == null)
-        {
-            return null;
-        }
-
-        LineNumberTableAttrInfo lineNumberTableAttribute =
-            (LineNumberTableAttrInfo)codeAttribute.getAttribute(classFile,
-                                                                ClassConstants.ATTR_LineNumberTable);
-        if (lineNumberTableAttribute == null)
-        {
-            return null;
-        }
-
-        return "" +
-               lineNumberTableAttribute.getLineNumber(0) +
-               ":" +
-               lineNumberTableAttribute.getLineNumber(Integer.MAX_VALUE);
-    }
-
-
-    /**
-     * Returns the (first) attribute with the given name.
-     */
-    private AttrInfo getAttribute(ClassFile classFile, String name)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            AttrInfo attribute = attributes[i];
-            if (attribute.getAttributeName(classFile).equals(name))
-            {
-                return attribute;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Accepts the given member info visitor.
-     */
-    public abstract void accept(ProgramClassFile  programClassFile,
-                                MemberInfoVisitor memberInfoVisitor);
-
-
-
-    /**
-     * Lets the given attribute info visitor visit all the attributes of
-     * this member info.
-     */
-    public abstract void attributesAccept(ProgramClassFile programClassFile,
-                                          AttrInfoVisitor  attrInfoVisitor);
-
-
-    /**
-     * Imports the field or method data to internal representation.
-     */
-    protected void read(DataInput din, ClassFile cf) throws IOException
-    {
-        u2accessFlags = din.readUnsignedShort();
-        u2nameIndex = din.readUnsignedShort();
-        u2descriptorIndex = din.readUnsignedShort();
-        u2attributesCount = din.readUnsignedShort();
-        if (u2attributesCount > 0)
-        {
-            attributes = new AttrInfo[u2attributesCount];
-            for (int i = 0; i < u2attributesCount; i++)
-            {
-                attributes[i] = AttrInfo.create(din, cf);
-            }
-        }
-    }
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public void write(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2accessFlags);
-        dout.writeShort(u2nameIndex);
-        dout.writeShort(u2descriptorIndex);
-        dout.writeShort(u2attributesCount);
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].write(dout);
-        }
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public int getAccessFlags()
-    {
-        return u2accessFlags;
-    }
-
-    public String getName(ClassFile classFile)
-    {
-        return classFile.getCpString(u2nameIndex);
-    }
-
-    public String getDescriptor(ClassFile classFile)
-    {
-        return classFile.getCpString(u2descriptorIndex);
-    }
-
-    public void accept(ClassFile classFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        accept((ProgramClassFile)classFile, memberInfoVisitor);
-    }
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/ProgramMethod.java b/src/proguard/classfile/ProgramMethod.java
new file mode 100644
index 0000000..89a09f7
--- /dev/null
+++ b/src/proguard/classfile/ProgramMethod.java
@@ -0,0 +1,82 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile;
+
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * Representation of a method from a program class.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramMethod extends ProgramMember implements Method
+{
+    /**
+     * An extra field pointing to the Clazz objects referenced in the
+     * descriptor string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public Clazz[] referencedClasses;
+
+
+    /**
+     * Creates an uninitialized ProgramMethod.
+     */
+    public ProgramMethod()
+    {
+    }
+
+
+    // Implementations for ProgramMember.
+
+    public void accept(ProgramClass programClass, MemberVisitor memberVisitor)
+    {
+        memberVisitor.visitProgramMethod(programClass, this);
+    }
+
+
+    public void attributesAccept(ProgramClass programClass, AttributeVisitor attributeVisitor)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            attributes[index].accept(programClass, this, attributeVisitor);
+        }
+    }
+
+
+    // Implementations for Member.
+
+    public void referencedClassesAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClasses != null)
+        {
+            for (int index = 0; index < referencedClasses.length; index++)
+            {
+                if (referencedClasses[index] != null)
+                {
+                    referencedClasses[index].accept(classVisitor);
+                }
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/ProgramMethodInfo.java b/src/proguard/classfile/ProgramMethodInfo.java
deleted file mode 100644
index 0f8749e..0000000
--- a/src/proguard/classfile/ProgramMethodInfo.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $Id: ProgramMethodInfo.java,v 1.19.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of a method from a program class file.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ProgramMethodInfo extends ProgramMemberInfo implements MethodInfo
-{
-    /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * descriptor string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to primitive types are ignored.
-     */
-    public ClassFile[] referencedClassFiles;
-
-
-    /**
-     * Creates a new ProgramMethodInfo from the file format data in the DataInput stream.
-     *
-     * @throws IOException if class file is corrupt or incomplete
-     */
-    public static ProgramMethodInfo create(DataInput din, ClassFile cf) throws IOException
-    {
-        ProgramMethodInfo mi = new ProgramMethodInfo();
-        mi.read(din, cf);
-        return mi;
-    }
-
-
-    public ProgramMethodInfo()
-    {
-    }
-
-
-    // Implementations for ProgramMemberInfo.
-
-    public void accept(ProgramClassFile programClassFile, MemberInfoVisitor memberInfoVisitor)
-    {
-        memberInfoVisitor.visitProgramMethodInfo(programClassFile, this);
-    }
-
-
-    public void attributesAccept(ProgramClassFile programClassFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].accept(programClassFile, this, attrInfoVisitor);
-        }
-    }
-
-
-    // Implementations for MemberInfo.
-
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFiles != null)
-        {
-            for (int i = 0; i < referencedClassFiles.length; i++)
-            {
-                if (referencedClassFiles[i] != null)
-                {
-                    referencedClassFiles[i].accept(classFileVisitor);
-                }
-            }
-        }
-    }
-}
diff --git a/src/proguard/classfile/StringCpInfo.java b/src/proguard/classfile/StringCpInfo.java
deleted file mode 100644
index 37cc92c..0000000
--- a/src/proguard/classfile/StringCpInfo.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/* $Id: StringCpInfo.java,v 1.18.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of a 'string' entry in the ConstantPool.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class StringCpInfo extends CpInfo
-{
-    public int u2stringIndex;
-
-    /**
-     * An extra field pointing to the referenced ClassFile object, if this
-     * string is being used in Class.forName() or .class constructs.
-     * This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to library class files are not filled out.
-     */
-    public ClassFile referencedClassFile;
-
-
-    protected StringCpInfo()
-    {
-    }
-
-
-    /**
-     * Creates a new StringCpInfo with the given string index.
-     * @param u2nameIndex         the index of the string in the constant pool.
-     * @param referencedClassFile the referenced class file, if any.
-     */
-    public StringCpInfo(int       u2stringIndex,
-                        ClassFile referencedClassFile)
-    {
-        this.u2stringIndex       = u2stringIndex;
-        this.referencedClassFile = referencedClassFile;
-    }
-
-
-    /**
-     * Returns the string value.
-     */
-    public String getString(ClassFile classFile)
-    {
-        return classFile.getCpString(u2stringIndex);
-    }
-
-
-    // Implementations for CpInfo.
-
-    public int getTag()
-    {
-        return ClassConstants.CONSTANT_String;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2stringIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2stringIndex);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
-    {
-        cpInfoVisitor.visitStringCpInfo(classFile, this);
-    }
-
-
-    /**
-     * Lets the referenced class file accept the given visitor.
-     */
-    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/VisitorAccepter.java b/src/proguard/classfile/VisitorAccepter.java
index a54e7e8..a3fa6a2 100644
--- a/src/proguard/classfile/VisitorAccepter.java
+++ b/src/proguard/classfile/VisitorAccepter.java
@@ -1,8 +1,7 @@
-/* $Id: VisitorAccepter.java,v 1.12.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
diff --git a/src/proguard/classfile/attribute/AllAttrInfoVisitor.java b/src/proguard/classfile/attribute/AllAttrInfoVisitor.java
deleted file mode 100644
index 6ace2ab..0000000
--- a/src/proguard/classfile/attribute/AllAttrInfoVisitor.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/* $Id: AllAttrInfoVisitor.java,v 1.2 2005/05/22 00:29:17 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.attribute;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This ClassFileVisitor and MemberInfoVisitor lets a given AttrInfoVisitor
- * visit all AttrInfo objects of the program classes and program class members
- * it visits.
- *
- * @author Eric Lafortune
- */
-public class AllAttrInfoVisitor
-implements   ClassFileVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor
-{
-    private AttrInfoVisitor attrInfoVisitor;
-
-
-    public AllAttrInfoVisitor(AttrInfoVisitor attrInfoVisitor)
-    {
-        this.attrInfoVisitor = attrInfoVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Visit the attributes of all fields and methods.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        // Visit the attributes.
-        programClassFile.attributesAccept(attrInfoVisitor);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Library class files don't have attributes.
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        programFieldInfo.attributesAccept(programClassFile, attrInfoVisitor);
-
-        // Visit the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        programMethodInfo.attributesAccept(programClassFile, attrInfoVisitor);
-
-        // Visit the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Library class file fields don't have attributes.
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Library class file methods don't have attributes.
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Visit the attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/AttrInfo.java b/src/proguard/classfile/attribute/AttrInfo.java
deleted file mode 100644
index c1bc7eb..0000000
--- a/src/proguard/classfile/attribute/AttrInfo.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/* $Id: AttrInfo.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.annotation.*;
-
-import java.io.*;
-
-/**
- * Representation of an attribute. Specific attributes have their representations
- * sub-classed from this.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public abstract class AttrInfo implements VisitorAccepter
-{
-    protected static final int CONSTANT_FIELD_SIZE = 6;
-
-
-    public int u2attrNameIndex;
-    //public int  u4attrLength;
-    //public byte info[];
-
-    /**
-     * An extra field in which visitors can store information.
-     */
-    public Object visitorInfo;
-
-
-    /**
-     * Creates a new AttrInfo from the data passed.
-     *
-     * @throws java.io.IOException if class file is corrupt or incomplete
-     */
-    public static AttrInfo create(DataInput din, ClassFile classFile) throws IOException
-    {
-        // Instantiate based on attribute name
-        int u2attrNameIndex = din.readUnsignedShort();
-        int u4attrLength    = din.readInt();
-        String attrName = classFile.getCpString(u2attrNameIndex);
-        AttrInfo attrInfo =
-            attrName.equals(ClassConstants.ATTR_InnerClasses)                         ? (AttrInfo)new InnerClassesAttrInfo():
-            attrName.equals(ClassConstants.ATTR_EnclosingMethod)                      ? (AttrInfo)new EnclosingMethodAttrInfo():
-            attrName.equals(ClassConstants.ATTR_ConstantValue)                        ? (AttrInfo)new ConstantValueAttrInfo():
-            attrName.equals(ClassConstants.ATTR_Exceptions)                           ? (AttrInfo)new ExceptionsAttrInfo():
-            attrName.equals(ClassConstants.ATTR_Code)                                 ? (AttrInfo)new CodeAttrInfo():
-            attrName.equals(ClassConstants.ATTR_LineNumberTable)                      ? (AttrInfo)new LineNumberTableAttrInfo():
-            attrName.equals(ClassConstants.ATTR_LocalVariableTable)                   ? (AttrInfo)new LocalVariableTableAttrInfo():
-            attrName.equals(ClassConstants.ATTR_LocalVariableTypeTable)               ? (AttrInfo)new LocalVariableTypeTableAttrInfo():
-            attrName.equals(ClassConstants.ATTR_SourceFile)                           ? (AttrInfo)new SourceFileAttrInfo():
-            attrName.equals(ClassConstants.ATTR_SourceDir)                            ? (AttrInfo)new SourceDirAttrInfo():
-            attrName.equals(ClassConstants.ATTR_Deprecated)                           ? (AttrInfo)new DeprecatedAttrInfo():
-            attrName.equals(ClassConstants.ATTR_Synthetic)                            ? (AttrInfo)new SyntheticAttrInfo():
-            attrName.equals(ClassConstants.ATTR_Signature)                            ? (AttrInfo)new SignatureAttrInfo():
-            attrName.equals(ClassConstants.ATTR_RuntimeVisibleAnnotations)            ? (AttrInfo)new RuntimeVisibleAnnotationsAttrInfo():
-            attrName.equals(ClassConstants.ATTR_RuntimeInvisibleAnnotations)          ? (AttrInfo)new RuntimeInvisibleAnnotationsAttrInfo():
-            attrName.equals(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations)   ? (AttrInfo)new RuntimeVisibleParameterAnnotationsAttrInfo():
-            attrName.equals(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations) ? (AttrInfo)new RuntimeInvisibleParameterAnnotationsAttrInfo():
-            attrName.equals(ClassConstants.ATTR_AnnotationDefault)                    ? (AttrInfo)new AnnotationDefaultAttrInfo():
-                                                                                        (AttrInfo)new UnknownAttrInfo(u4attrLength);
-        attrInfo.u2attrNameIndex = u2attrNameIndex;
-        attrInfo.readInfo(din, classFile);
-
-        return attrInfo;
-    }
-
-
-    protected AttrInfo()
-    {
-    }
-
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public final void write(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2attrNameIndex);
-        dout.writeInt(getLength());
-        writeInfo(dout);
-    }
-
-
-    /**
-     * Returns the String name of the attribute; override this in subclasses.
-     */
-    public String getAttributeName(ClassFile classFile)
-    {
-        return classFile.getCpString(u2attrNameIndex);
-    }
-
-
-    // Abstract methods to be implemented by extensions.
-
-    /**
-     * Returns the length of the attribute, expressed in bytes.
-     */
-    protected abstract int getLength();
-
-
-    /**
-     * Reads the data following the header.
-     */
-    protected abstract void readInfo(DataInput din, ClassFile classFile) throws IOException;
-
-
-    /**
-     * Exports data following the header to a DataOutput stream.
-     */
-    protected abstract void writeInfo(DataOutput dout) throws IOException;
-
-
-    /**
-     * Accepts the given visitor.
-     * This default implementation does nothing, which is useful for attributes
-     * that require a context.
-     */
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        // Do nothing.
-    }
-
-    /**
-     * Accepts the given visitor in the context of a field.
-     * This default implementation ignores the context.
-     */
-    public void accept(ClassFile classFile, FieldInfo fieldInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        accept(classFile, attrInfoVisitor);
-    }
-
-    /**
-     * Accepts the given visitor in the context of a method.
-     * This default implementation ignores the context.
-     */
-    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        accept(classFile, attrInfoVisitor);
-    }
-
-    /**
-     * Accepts the given visitor in the context of a method's code.
-     * This default implementation ignores the code.
-     */
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        accept(classFile, methodInfo, attrInfoVisitor);
-    }
-
-
-    // Implementations for VisitorAccepter.
-
-    public Object getVisitorInfo()
-    {
-        return visitorInfo;
-    }
-
-    public void setVisitorInfo(Object visitorInfo)
-    {
-        this.visitorInfo = visitorInfo;
-    }
-}
diff --git a/src/proguard/classfile/attribute/AttrInfoVisitor.java b/src/proguard/classfile/attribute/AttrInfoVisitor.java
deleted file mode 100644
index 4f641ef..0000000
--- a/src/proguard/classfile/attribute/AttrInfoVisitor.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/* $Id: AttrInfoVisitor.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.annotation.*;
-
-/**
- * This interface specifies the methods for a visitor of <code>AttrInfo</code>
- * objects.
- *
- * @author Eric Lafortune
- */
-public interface AttrInfoVisitor
-{
-    public void visitUnknownAttrInfo(               ClassFile classFile, UnknownAttrInfo         unknownAttrInfo);
-    public void visitInnerClassesAttrInfo(          ClassFile classFile, InnerClassesAttrInfo    innerClassesAttrInfo);
-    public void visitEnclosingMethodAttrInfo(       ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo);
-
-    public void visitConstantValueAttrInfo(         ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo);
-
-    public void visitExceptionsAttrInfo(            ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo);
-    public void visitCodeAttrInfo(                  ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo       codeAttrInfo);
-
-    public void visitLineNumberTableAttrInfo(       ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo        lineNumberTableAttrInfo);
-    public void visitLocalVariableTableAttrInfo(    ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo     localVariableTableAttrInfo);
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo);
-
-    public void visitSourceFileAttrInfo(            ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo);
-    public void visitSourceDirAttrInfo(             ClassFile classFile, SourceDirAttrInfo  sourceDirAttrInfo);
-    public void visitDeprecatedAttrInfo(            ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo);
-    public void visitSyntheticAttrInfo(             ClassFile classFile, SyntheticAttrInfo  syntheticAttrInfo);
-    public void visitSignatureAttrInfo(             ClassFile classFile, SignatureAttrInfo  syntheticAttrInfo);
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(           ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo            runtimeVisibleAnnotationsAttrInfo);
-    public void visitRuntimeInvisibleAnnotationAttrInfo(         ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo          runtimeInvisibleAnnotationsAttrInfo);
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(  ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo   runtimeVisibleParameterAnnotationsAttrInfo);
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo);
-    public void visitAnnotationDefaultAttrInfo(                  ClassFile classFile, AnnotationDefaultAttrInfo                    annotationDefaultAttrInfo);
-}
diff --git a/src/proguard/classfile/attribute/Attribute.java b/src/proguard/classfile/attribute/Attribute.java
new file mode 100644
index 0000000..6f326f9
--- /dev/null
+++ b/src/proguard/classfile/attribute/Attribute.java
@@ -0,0 +1,125 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This abstract class represents an attribute that is attached to a class,
+ * a class member, or a code attribute. Specific types of attributes are
+ * subclassed from it.
+ *
+ * @author Eric Lafortune
+ * @noinspection AbstractClassWithoutAbstractMethods
+ */
+public abstract class Attribute implements VisitorAccepter
+{
+    public int u2attributeNameIndex;
+    //public int  u4attributeLength;
+    //public byte info[];
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Returns the String name of the attribute.
+     */
+    public String getAttributeName(Clazz clazz)
+    {
+        return clazz.getString(u2attributeNameIndex);
+    }
+
+
+    // Methods to be implemented by extensions, if applicable.
+
+    /**
+     * Accepts the given visitor.
+     */
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+    /**
+     * Accepts the given visitor in the context of the given field.
+     */
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        // Delegate the default invocation if the field is null anyway.
+        if (field == null)
+        {
+            accept(clazz, attributeVisitor);
+        }
+        else
+        {
+            throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+        }
+    }
+
+    /**
+     * Accepts the given visitor in the context of the given method.
+     */
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        // Delegate the default invocation if the method is null anyway.
+        if (method == null)
+        {
+            accept(clazz, (Field)null, attributeVisitor);
+        }
+        else
+        {
+            throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+        }
+    }
+
+    /**
+     * Accepts the given visitor in the context of the given code attribute.
+     */
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        // Delegate the default invocation if the code attribute is null anyway.
+        if (codeAttribute == null)
+        {
+            accept(clazz, method, attributeVisitor);
+        }
+        else
+        {
+            throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+        }
+    }
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/attribute/CodeAttrInfo.java b/src/proguard/classfile/attribute/CodeAttrInfo.java
deleted file mode 100644
index 04d4f54..0000000
--- a/src/proguard/classfile/attribute/CodeAttrInfo.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/* $Id: CodeAttrInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-
-import proguard.classfile.*;
-import proguard.classfile.instruction.*;
-
-import java.io.*;
-
-/**
- * Representation of a code attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class CodeAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 12;
-
-
-    public int             u2maxStack;
-    public int             u2maxLocals;
-    public int             u4codeLength;
-    public byte[]          code;
-    public int             u2exceptionTableLength;
-    public ExceptionInfo[] exceptionTable;
-    public int             u2attributesCount;
-    public AttrInfo[]      attributes;
-
-
-    protected CodeAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the (first) attribute with the given name.
-     */
-    public AttrInfo getAttribute(ClassFile classFile, String name)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            AttrInfo attribute = attributes[i];
-            if (attribute.getAttributeName(classFile).equals(name))
-            {
-                return attribute;
-            }
-        }
-
-        return null;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        int length = CONSTANT_FIELD_SIZE + u4codeLength +
-                        u2exceptionTableLength * ExceptionInfo.CONSTANT_FIELD_SIZE;
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            length += AttrInfo.CONSTANT_FIELD_SIZE + attributes[i].getLength();
-        }
-
-        return length;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2maxStack = din.readUnsignedShort();
-        u2maxLocals = din.readUnsignedShort();
-        u4codeLength = din.readInt();
-        code = new byte[u4codeLength];
-        din.readFully(code);
-        u2exceptionTableLength = din.readUnsignedShort();
-        exceptionTable = new ExceptionInfo[u2exceptionTableLength];
-        for (int i = 0; i < u2exceptionTableLength; i++)
-        {
-            exceptionTable[i] = ExceptionInfo.create(din);
-        }
-        u2attributesCount = din.readUnsignedShort();
-        attributes = new AttrInfo[u2attributesCount];
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i] = AttrInfo.create(din, classFile);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2maxStack);
-        dout.writeShort(u2maxLocals);
-        dout.writeInt(u4codeLength);
-        dout.write(code, 0, u4codeLength);
-        dout.writeShort(u2exceptionTableLength);
-        for (int i = 0; i < u2exceptionTableLength; i++)
-        {
-            exceptionTable[i].write(dout);
-        }
-        dout.writeShort(u2attributesCount);
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].write(dout);
-        }
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitCodeAttrInfo(classFile, null, this);
-    }
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitCodeAttrInfo(classFile, methodInfo, this);
-    }
-
-
-    /**
-     * Applies the given instruction visitor to all instructions.
-     */
-    public void instructionsAccept(ClassFile classFile, MethodInfo methodInfo, InstructionVisitor instructionVisitor)
-    {
-        int offset = 0;
-
-        do
-        {
-            // Note that the instruction is only volatile.
-            Instruction instruction = InstructionFactory.create(code, offset);
-            int length = instruction.length(offset);
-            instruction.accept(classFile, methodInfo, this, offset, instructionVisitor);
-            offset += length;
-        }
-        while (offset < u4codeLength);
-    }
-
-    /**
-     * Applies the given instruction visitor to the given instruction.
-     */
-    public void instructionAccept(ClassFile classFile, MethodInfo methodInfo, InstructionVisitor instructionVisitor, int offset)
-    {
-        Instruction instruction = InstructionFactory.create(code, offset);
-        instruction.accept(classFile, methodInfo, this, offset, instructionVisitor);
-    }
-
-    /**
-     * Applies the given exception visitor to all exceptions.
-     */
-    public void exceptionsAccept(ClassFile classFile, MethodInfo methodInfo, ExceptionInfoVisitor exceptionInfoVisitor)
-    {
-        for (int i = 0; i < u2exceptionTableLength; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of ExceptionInfo.
-            exceptionInfoVisitor.visitExceptionInfo(classFile, methodInfo, this, exceptionTable[i]);
-        }
-    }
-
-    /**
-     * Applies the given attribute visitor to all attributes.
-     */
-    public void attributesAccept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        for (int i = 0; i < u2attributesCount; i++)
-        {
-            attributes[i].accept(classFile, methodInfo, this, attrInfoVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/CodeAttribute.java b/src/proguard/classfile/attribute/CodeAttribute.java
new file mode 100644
index 0000000..2efbaf3
--- /dev/null
+++ b/src/proguard/classfile/attribute/CodeAttribute.java
@@ -0,0 +1,176 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+
+/**
+ * This Attribute represents a code attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeAttribute extends Attribute
+{
+    public int             u2maxStack;
+    public int             u2maxLocals;
+    public int             u4codeLength;
+    public byte[]          code;
+    public int             u2exceptionTableLength;
+    public ExceptionInfo[] exceptionTable;
+    public int             u2attributesCount;
+    public Attribute[]     attributes;
+
+
+    /**
+     * Creates an uninitialized CodeAttribute.
+     */
+    public CodeAttribute()
+    {
+    }
+
+
+    /**
+     * Returns the (first) attribute with the given name.
+     */
+    public Attribute getAttribute(Clazz clazz, String name)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            Attribute attribute = attributes[index];
+            if (attribute.getAttributeName(clazz).equals(name))
+            {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitCodeAttribute(clazz, method, this);
+    }
+
+
+    /**
+     * Applies the given instruction visitor to all instructions.
+     */
+    public void instructionsAccept(Clazz clazz, Method method, InstructionVisitor instructionVisitor)
+    {
+        instructionsAccept(clazz, method, 0, u4codeLength, instructionVisitor);
+    }
+
+
+    /**
+     * Applies the given instruction visitor to the instruction at the specified
+     * offset.
+     */
+    public void instructionAccept(Clazz clazz, Method method, int offset, InstructionVisitor instructionVisitor)
+    {
+        Instruction instruction = InstructionFactory.create(code, offset);
+        instruction.accept(clazz, method, this, offset, instructionVisitor);
+    }
+
+
+    /**
+     * Applies the given instruction visitor to all instructions in the
+     * specified range of offsets.
+     */
+    public void instructionsAccept(Clazz clazz, Method method, int startOffset, int endOffset, InstructionVisitor instructionVisitor)
+    {
+        int offset = startOffset;
+
+        while (offset < endOffset)
+        {
+            // Note that the instruction is only volatile.
+            Instruction instruction = InstructionFactory.create(code, offset);
+            int instructionLength = instruction.length(offset);
+            instruction.accept(clazz, method, this, offset, instructionVisitor);
+            offset += instructionLength;
+        }
+    }
+
+
+    /**
+     * Applies the given exception visitor to all exceptions.
+     */
+    public void exceptionsAccept(Clazz clazz, Method method, ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        for (int index = 0; index < u2exceptionTableLength; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of ExceptionInfo.
+            exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionTable[index]);
+        }
+    }
+
+
+    /**
+     * Applies the given exception visitor to all exceptions that are applicable
+     * to the instruction at the specified offset.
+     */
+    public void exceptionsAccept(Clazz clazz, Method method, int offset, ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        for (int index = 0; index < u2exceptionTableLength; index++)
+        {
+            ExceptionInfo exceptionInfo = exceptionTable[index];
+            if (exceptionInfo.isApplicable(offset))
+            {
+                exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionInfo);
+            }
+        }
+    }
+
+
+    /**
+     * Applies the given exception visitor to all exceptions that are applicable
+     * to any of the instructions in the specified range of offsets.
+     */
+    public void exceptionsAccept(Clazz clazz, Method method, int startOffset, int endOffset, ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        for (int index = 0; index < u2exceptionTableLength; index++)
+        {
+            ExceptionInfo exceptionInfo = exceptionTable[index];
+            if (exceptionInfo.isApplicable(startOffset, endOffset))
+            {
+                exceptionInfoVisitor.visitExceptionInfo(clazz, method, this, exceptionInfo);
+            }
+        }
+    }
+
+
+    /**
+     * Applies the given attribute visitor to all attributes.
+     */
+    public void attributesAccept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            attributes[index].accept(clazz, method, this, attributeVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/ConstantValueAttrInfo.java b/src/proguard/classfile/attribute/ConstantValueAttrInfo.java
deleted file mode 100644
index 86e937c..0000000
--- a/src/proguard/classfile/attribute/ConstantValueAttrInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/* $Id: ConstantValueAttrInfo.java,v 1.1 2004/10/10 21:10:04 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a constant value attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ConstantValueAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int u2constantValueIndex;
-
-
-    protected ConstantValueAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2constantValueIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2constantValueIndex);
-    }
-
-    public void accept(ClassFile classFile, FieldInfo fieldInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitConstantValueAttrInfo(classFile, fieldInfo, this);
-    }
-}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/attribute/ConstantValueAttribute.java
similarity index 54%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/attribute/ConstantValueAttribute.java
index d9a4a34..cbd9fa5 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/attribute/ConstantValueAttribute.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,33 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
+package proguard.classfile.attribute;
 
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 
 /**
- * Representation of a field from a class file.
+ * This Attribute represents a constant value attribute.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class ConstantValueAttribute extends Attribute
 {
+    public int u2constantValueIndex;
+
+
+    /**
+     * Creates an uninitialized ConstantValueAttribute.
+     */
+    public ConstantValueAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitConstantValueAttribute(clazz, field, this);
+    }
 }
diff --git a/src/proguard/classfile/attribute/DeprecatedAttrInfo.java b/src/proguard/classfile/attribute/DeprecatedAttrInfo.java
deleted file mode 100644
index 28b1e6e..0000000
--- a/src/proguard/classfile/attribute/DeprecatedAttrInfo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/* $Id: DeprecatedAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a deprecated attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class DeprecatedAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 0;
-
-
-    protected DeprecatedAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitDeprecatedAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/DeprecatedAttribute.java b/src/proguard/classfile/attribute/DeprecatedAttribute.java
new file mode 100644
index 0000000..c77850e
--- /dev/null
+++ b/src/proguard/classfile/attribute/DeprecatedAttribute.java
@@ -0,0 +1,57 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents a deprecated attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class DeprecatedAttribute extends Attribute
+{
+    /**
+     * Creates an uninitialized DeprecatedAttribute.
+     */
+    public DeprecatedAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitDeprecatedAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitDeprecatedAttribute(clazz, field, this);
+    }
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitDeprecatedAttribute(clazz, method, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java b/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java
deleted file mode 100644
index c796511..0000000
--- a/src/proguard/classfile/attribute/EnclosingMethodAttrInfo.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/* $Id: EnclosingMethodAttrInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * Representation of an enclosing method attribute.
- *
- * @author Eric Lafortune
- */
-public class EnclosingMethodAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 4;
-
-
-    public int u2classIndex;
-    public int u2nameAndTypeIndex;
-
-    /**
-     * An extra field pointing to the referenced ClassFile object.
-     * This field is typically filled out by the <code>{@link
-     * ClassFileReferenceInitializer}</code>.
-     */
-    public ClassFile referencedClassFile;
-
-    /**
-     * An extra field optionally pointing to the referenced MethodInfo object.
-     * This field is typically filled out by the <code>{@link
-     * ClassFileReferenceInitializer}</code>.
-     */
-    public MethodInfo referencedMethodInfo;
-
-
-    protected EnclosingMethodAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the class name.
-     */
-    public String getClassName(ClassFile classFile)
-    {
-        return classFile.getCpClassNameString(u2classIndex);
-    }
-
-    /**
-     * Returns the method/field name.
-     */
-    public String getName(ClassFile classFile)
-    {
-        return classFile.getCpNameString(u2nameAndTypeIndex);
-    }
-
-    /**
-     * Returns the type.
-     */
-    public String getType(ClassFile classFile)
-    {
-        return classFile.getCpTypeString(u2nameAndTypeIndex);
-    }
-
-
-    /**
-     * Lets the referenced class file accept the given visitor.
-     */
-    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-
-
-    /**
-     * Lets the referenced class member accept the given visitor.
-     */
-    public void referencedMethodInfoAccept(MemberInfoVisitor memberInfoVisitor)
-    {
-        if (referencedMethodInfo != null)
-        {
-            referencedMethodInfo.accept(referencedClassFile,
-                                        memberInfoVisitor);
-        }
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2classIndex       = din.readUnsignedShort();
-        u2nameAndTypeIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2classIndex);
-        dout.writeShort(u2nameAndTypeIndex);
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitEnclosingMethodAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/EnclosingMethodAttribute.java b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java
new file mode 100644
index 0000000..f7e7c22
--- /dev/null
+++ b/src/proguard/classfile/attribute/EnclosingMethodAttribute.java
@@ -0,0 +1,118 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This Attribute represents an enclosing method attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class EnclosingMethodAttribute extends Attribute
+{
+    public int u2classIndex;
+    public int u2nameAndTypeIndex;
+
+    /**
+     * An extra field pointing to the referenced Clazz object.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
+     */
+    public Clazz referencedClass;
+
+    /**
+     * An extra field optionally pointing to the referenced Method object.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
+     */
+    public Method referencedMethod;
+
+
+    /**
+     * Creates an uninitialized EnclosingMethodAttribute.
+     */
+    public EnclosingMethodAttribute()
+    {
+    }
+
+
+    /**
+     * Returns the class name.
+     */
+    public String getClassName(Clazz clazz)
+    {
+        return clazz.getClassName(u2classIndex);
+    }
+
+    /**
+     * Returns the method/field name.
+     */
+    public String getName(Clazz clazz)
+    {
+        return clazz.getName(u2nameAndTypeIndex);
+    }
+
+    /**
+     * Returns the type.
+     */
+    public String getType(Clazz clazz)
+    {
+        return clazz.getType(u2nameAndTypeIndex);
+    }
+
+
+    /**
+     * Lets the referenced class accept the given visitor.
+     */
+    public void referencedClassAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClass != null)
+        {
+            referencedClass.accept(classVisitor);
+        }
+    }
+
+
+    /**
+     * Lets the referenced class member accept the given visitor.
+     */
+    public void referencedMethodAccept(MemberVisitor memberVisitor)
+    {
+        if (referencedMethod != null)
+        {
+            referencedMethod.accept(referencedClass,
+                                    memberVisitor);
+        }
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitEnclosingMethodAttribute(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/ExceptionInfo.java b/src/proguard/classfile/attribute/ExceptionInfo.java
index 74ecc53..e8a3d3a 100644
--- a/src/proguard/classfile/attribute/ExceptionInfo.java
+++ b/src/proguard/classfile/attribute/ExceptionInfo.java
@@ -1,14 +1,13 @@
-/* $Id: ExceptionInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,24 +20,18 @@
  */
 package proguard.classfile.attribute;
 
-import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.VisitorAccepter;
 
 /**
  * Representation of an Exception table entry.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public class ExceptionInfo implements VisitorAccepter
 {
-    public static final int CONSTANT_FIELD_SIZE = 8;
-
-
-    public int u2startpc;
-    public int u2endpc;
-    public int u2handlerpc;
+    public int u2startPC;
+    public int u2endPC;
+    public int u2handlerPC;
     public int u2catchType;
 
     /**
@@ -47,33 +40,49 @@ public class ExceptionInfo implements VisitorAccepter
     public Object visitorInfo;
 
 
-    public static ExceptionInfo create(DataInput din) throws IOException
+    /**
+     * Creates an uninitialized ExceptionInfo.
+     */
+    public ExceptionInfo()
     {
-        ExceptionInfo ei = new ExceptionInfo();
-        ei.read(din);
-        return ei;
+        this(0, 0, 0, 0);
     }
 
 
-    private ExceptionInfo() {}
+    /**
+     * Creates an ExceptionInfo with the given properties.
+     */
+    public ExceptionInfo(int u2startPC,
+                         int u2endPC,
+                         int u2handlerPC,
+                         int u2catchType)
+    {
+        this.u2startPC   = u2startPC;
+        this.u2endPC     = u2endPC;
+        this.u2handlerPC = u2handlerPC;
+        this.u2catchType = u2catchType;
+    }
+
 
-    private void read(DataInput din) throws IOException
+    /**
+     * Returns whether the exception's try block contains the instruction at the
+     * given offset.
+     */
+    public boolean isApplicable(int instructionOffset)
     {
-        u2startpc   = din.readUnsignedShort();
-        u2endpc     = din.readUnsignedShort();
-        u2handlerpc = din.readUnsignedShort();
-        u2catchType = din.readUnsignedShort();
+        return instructionOffset >= u2startPC &&
+               instructionOffset <  u2endPC;
     }
 
+
     /**
-     * Exports the representation to a DataOutput stream.
+     * Returns whether the exception's try block overlaps with the specified
+     * block of instructions.
      */
-    public void write(DataOutput dout) throws IOException
+    public boolean isApplicable(int startOffset, int endOffset)
     {
-        dout.writeShort(u2startpc);
-        dout.writeShort(u2endpc);
-        dout.writeShort(u2handlerpc);
-        dout.writeShort(u2catchType);
+        return u2startPC < endOffset &&
+               u2endPC   > startOffset;
     }
 
 
diff --git a/src/proguard/classfile/attribute/ExceptionsAttrInfo.java b/src/proguard/classfile/attribute/ExceptionsAttrInfo.java
deleted file mode 100644
index 8eb099c..0000000
--- a/src/proguard/classfile/attribute/ExceptionsAttrInfo.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* $Id: ExceptionsAttrInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of an exceptions attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class ExceptionsAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int   u2numberOfExceptions;
-    public int[] u2exceptionIndexTable;
-
-
-    protected ExceptionsAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE + 2 * u2numberOfExceptions;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2numberOfExceptions = din.readUnsignedShort();
-        u2exceptionIndexTable = new int[u2numberOfExceptions];
-        for (int i = 0; i < u2numberOfExceptions; i++)
-        {
-            u2exceptionIndexTable[i] = din.readUnsignedShort();
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2numberOfExceptions);
-        for (int i = 0; i < u2numberOfExceptions; i++)
-        {
-            dout.writeShort(u2exceptionIndexTable[i]);
-        }
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        // We'll just ignore Exception attributes that do not belong to a method.
-    }
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitExceptionsAttrInfo(classFile, methodInfo, this);
-    }
-
-
-    /**
-     * Applies the given constant pool visitor to all exception class pool info
-     * entries.
-     */
-    public void exceptionEntriesAccept(ProgramClassFile programClassFile, CpInfoVisitor cpInfoVisitor)
-    {
-        for (int i = 0; i < u2numberOfExceptions; i++)
-        {
-            programClassFile.constantPoolEntryAccept(u2exceptionIndexTable[i],
-                                                     cpInfoVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/ExceptionsAttribute.java b/src/proguard/classfile/attribute/ExceptionsAttribute.java
new file mode 100644
index 0000000..25c5758
--- /dev/null
+++ b/src/proguard/classfile/attribute/ExceptionsAttribute.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Attribute represents an exceptions attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionsAttribute extends Attribute
+{
+    public int   u2exceptionIndexTableLength;
+    public int[] u2exceptionIndexTable;
+
+
+    /**
+     * Creates an uninitialized ExceptionsAttribute.
+     */
+    public ExceptionsAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitExceptionsAttribute(clazz, method, this);
+    }
+
+
+    /**
+     * Applies the given constant pool visitor to all exception class pool info
+     * entries.
+     */
+    public void exceptionEntriesAccept(ProgramClass programClass, ConstantVisitor constantVisitor)
+    {
+        for (int index = 0; index < u2exceptionIndexTableLength; index++)
+        {
+            programClass.constantPoolEntryAccept(u2exceptionIndexTable[index],
+                                                 constantVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/InnerClassesAttrInfo.java b/src/proguard/classfile/attribute/InnerClassesAttrInfo.java
deleted file mode 100644
index 69ad945..0000000
--- a/src/proguard/classfile/attribute/InnerClassesAttrInfo.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* $Id: InnerClassesAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of an inner classes attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class InnerClassesAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int                u2numberOfClasses;
-    public InnerClassesInfo[] classes;
-
-
-    protected InnerClassesAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the array of inner classes data.
-     */
-    protected InnerClassesInfo[] getInfo() throws Exception
-    {
-        return classes;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE +
-               u2numberOfClasses * InnerClassesInfo.CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2numberOfClasses = din.readUnsignedShort();
-        classes = new InnerClassesInfo[u2numberOfClasses];
-        for (int i = 0; i < u2numberOfClasses; i++)
-        {
-            classes[i] = InnerClassesInfo.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2numberOfClasses);
-        for (int i = 0; i < u2numberOfClasses; i++)
-        {
-            classes[i].write(dout);
-        }
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitInnerClassesAttrInfo(classFile, this);
-    }
-
-
-    /**
-     * Applies the given visitor to all inner classes.
-     */
-    public void innerClassEntriesAccept(ClassFile classFile, InnerClassesInfoVisitor innerClassesInfoVisitor)
-    {
-        for (int i = 0; i < u2numberOfClasses; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of InnerClassesInfo.
-            innerClassesInfoVisitor.visitInnerClassesInfo(classFile, classes[i]);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/InnerClassesAttribute.java b/src/proguard/classfile/attribute/InnerClassesAttribute.java
new file mode 100644
index 0000000..877cf7d
--- /dev/null
+++ b/src/proguard/classfile/attribute/InnerClassesAttribute.java
@@ -0,0 +1,65 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.visitor.*;
+
+/**
+ * This Attribute represents an inner classes attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class InnerClassesAttribute extends Attribute
+{
+    public int                u2classesCount;
+    public InnerClassesInfo[] classes;
+
+
+    /**
+     * Creates an uninitialized InnerClassesAttribute.
+     */
+    public InnerClassesAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitInnerClassesAttribute(clazz, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all inner classes.
+     */
+    public void innerClassEntriesAccept(Clazz clazz, InnerClassesInfoVisitor innerClassesInfoVisitor)
+    {
+        for (int index = 0; index < u2classesCount; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of InnerClassesInfo.
+            innerClassesInfoVisitor.visitInnerClassesInfo(clazz, classes[index]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/InnerClassesInfo.java b/src/proguard/classfile/attribute/InnerClassesInfo.java
index b523ae1..07db1fd 100644
--- a/src/proguard/classfile/attribute/InnerClassesInfo.java
+++ b/src/proguard/classfile/attribute/InnerClassesInfo.java
@@ -1,14 +1,13 @@
-/* $Id: InnerClassesInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,23 +20,17 @@
  */
 package proguard.classfile.attribute;
 
-import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.VisitorAccepter;
 
 /**
  * Representation of an Inner Classes table entry.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public class InnerClassesInfo implements VisitorAccepter
 {
-    public static final int CONSTANT_FIELD_SIZE = 8;
-
-
-    public int u2innerClassInfoIndex;
-    public int u2outerClassInfoIndex;
+    public int u2innerClassIndex;
+    public int u2outerClassIndex;
     public int u2innerNameIndex;
     public int u2innerClassAccessFlags;
 
@@ -47,19 +40,12 @@ public class InnerClassesInfo implements VisitorAccepter
     public Object visitorInfo;
 
 
-    public static InnerClassesInfo create(DataInput din) throws IOException
-    {
-        InnerClassesInfo ici = new InnerClassesInfo();
-        ici.read(din);
-        return ici;
-    }
-
     /**
      * Returns the inner class index.
      */
     protected int getInnerClassIndex()
     {
-        return u2innerClassInfoIndex;
+        return u2innerClassIndex;
     }
 
     /**
@@ -78,25 +64,6 @@ public class InnerClassesInfo implements VisitorAccepter
         u2innerNameIndex = index;
     }
 
-    private void read(DataInput din) throws IOException
-    {
-        u2innerClassInfoIndex   = din.readUnsignedShort();
-        u2outerClassInfoIndex   = din.readUnsignedShort();
-        u2innerNameIndex        = din.readUnsignedShort();
-        u2innerClassAccessFlags = din.readUnsignedShort();
-    }
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public void write(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2innerClassInfoIndex);
-        dout.writeShort(u2outerClassInfoIndex);
-        dout.writeShort(u2innerNameIndex);
-        dout.writeShort(u2innerClassAccessFlags);
-    }
-
 
     // Implementations for VisitorAccepter.
 
diff --git a/src/proguard/classfile/attribute/LibraryAttrInfo.java b/src/proguard/classfile/attribute/LibraryAttrInfo.java
deleted file mode 100644
index d02d9e2..0000000
--- a/src/proguard/classfile/attribute/LibraryAttrInfo.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/* $Id: LibraryAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import java.io.*;
-
-/**
- * Representation of a light-weight library attribute.
- *
- * @author Eric Lafortune
- */
-public class LibraryAttrInfo
-{
-    /**
-     * Skips LibraryAttrInfo from the data passed.
-     *
-     * @throws java.io.IOException if class file is corrupt or incomplete
-     */
-    public static void skip(DataInput din) throws IOException
-    {
-        // Instantiate based on attribute name
-        int u2attrNameIndex = din.readUnsignedShort();
-        int u4attrLength    = din.readInt();
-        din.skipBytes(u4attrLength);
-    }
-}
diff --git a/src/proguard/classfile/attribute/LineNumberInfo.java b/src/proguard/classfile/attribute/LineNumberInfo.java
index eaae0b2..b2fbdd4 100644
--- a/src/proguard/classfile/attribute/LineNumberInfo.java
+++ b/src/proguard/classfile/attribute/LineNumberInfo.java
@@ -1,14 +1,13 @@
-/* $Id: LineNumberInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,44 +20,21 @@
  */
 package proguard.classfile.attribute;
 
-import java.io.*;
-
 /**
  * Representation of an Line Number table entry.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public class LineNumberInfo
 {
-    public static final int CONSTANT_FIELD_SIZE = 4;
-
-
-    public int u2startpc;
+    public int u2startPC;
     public int u2lineNumber;
 
 
-    public static LineNumberInfo create(DataInput din) throws IOException
-    {
-        LineNumberInfo lni = new LineNumberInfo();
-        lni.read(din);
-        return lni;
-    }
-
-
-    private LineNumberInfo() {}
-    private void read(DataInput din) throws IOException
-    {
-        u2startpc    = din.readUnsignedShort();
-        u2lineNumber = din.readUnsignedShort();
-    }
-
     /**
-     * Exports the representation to a DataOutput stream.
+     * Creates an uninitialized LineNumberInfo.
      */
-    public void write(DataOutput dout) throws IOException
+    public LineNumberInfo()
     {
-        dout.writeShort(u2startpc);
-        dout.writeShort(u2lineNumber);
     }
 }
diff --git a/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java b/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java
deleted file mode 100644
index 194fb2d..0000000
--- a/src/proguard/classfile/attribute/LineNumberTableAttrInfo.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $Id: LineNumberTableAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a line number table attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LineNumberTableAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int              u2lineNumberTableLength;
-    public LineNumberInfo[] lineNumberTable;
-
-
-    protected LineNumberTableAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the line number corresponding to the given byte code program
-     * counter.
-     */
-    public int getLineNumber(int pc)
-    {
-        for (int i = u2lineNumberTableLength-1 ; i >= 0 ; i--)
-        {
-            LineNumberInfo info = lineNumberTable[i];
-            if (pc >= info.u2startpc)
-            {
-                return info.u2lineNumber;
-            }
-        }
-
-        return u2lineNumberTableLength > 0 ?
-            lineNumberTable[0].u2lineNumber :
-            0;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE +
-               u2lineNumberTableLength * LineNumberInfo.CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2lineNumberTableLength = din.readUnsignedShort();
-        lineNumberTable = new LineNumberInfo[u2lineNumberTableLength];
-        for (int i = 0; i < u2lineNumberTableLength; i++)
-        {
-            lineNumberTable[i] = LineNumberInfo.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2lineNumberTableLength);
-        for (int i = 0; i < u2lineNumberTableLength; i++)
-        {
-            lineNumberTable[i].write(dout);
-        }
-    }
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    /**
-     * Applies the given visitor to all line numbers.
-     */
-    public void lineNumbersAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfoVisitor lineNumberInfoVisitor)
-    {
-        for (int i = 0; i < u2lineNumberTableLength; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of LineNumberInfo.
-            lineNumberInfoVisitor.visitLineNumberInfo(classFile, methodInfo, codeAttrInfo, lineNumberTable[i]);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/LineNumberTableAttribute.java b/src/proguard/classfile/attribute/LineNumberTableAttribute.java
new file mode 100644
index 0000000..c3cfa20
--- /dev/null
+++ b/src/proguard/classfile/attribute/LineNumberTableAttribute.java
@@ -0,0 +1,86 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+
+/**
+ * This Attribute represents a line number table attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class LineNumberTableAttribute extends Attribute
+{
+    public int              u2lineNumberTableLength;
+    public LineNumberInfo[] lineNumberTable;
+
+
+    /**
+     * Creates an uninitialized LineNumberTableAttribute.
+     */
+    public LineNumberTableAttribute()
+    {
+    }
+
+
+    /**
+     * Returns the line number corresponding to the given byte code program
+     * counter.
+     */
+    public int getLineNumber(int pc)
+    {
+        for (int index = u2lineNumberTableLength-1 ; index >= 0 ; index--)
+        {
+            LineNumberInfo info = lineNumberTable[index];
+            if (pc >= info.u2startPC)
+            {
+                return info.u2lineNumber;
+            }
+        }
+
+        return u2lineNumberTableLength > 0 ?
+            lineNumberTable[0].u2lineNumber :
+            0;
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitLineNumberTableAttribute(clazz, method, codeAttribute, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all line numbers.
+     */
+    public void lineNumbersAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfoVisitor lineNumberInfoVisitor)
+    {
+        for (int index = 0; index < u2lineNumberTableLength; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LineNumberInfo.
+            lineNumberInfoVisitor.visitLineNumberInfo(clazz, method, codeAttribute, lineNumberTable[index]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableInfo.java b/src/proguard/classfile/attribute/LocalVariableInfo.java
index bbaf6ae..d3f5840 100644
--- a/src/proguard/classfile/attribute/LocalVariableInfo.java
+++ b/src/proguard/classfile/attribute/LocalVariableInfo.java
@@ -1,14 +1,13 @@
-/* $Id: LocalVariableInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,92 +20,39 @@
  */
 package proguard.classfile.attribute;
 
-import proguard.classfile.ClassFile;
-
-import java.io.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
  * Representation of an Local Variable table entry.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public class LocalVariableInfo
 {
-    public static final int CONSTANT_FIELD_SIZE = 10;
-
-
-    public int u2startpc;
+    public int u2startPC;
     public int u2length;
     public int u2nameIndex;
     public int u2descriptorIndex;
     public int u2index;
 
     /**
-     * An extra field pointing to the referenced ClassFile object.
-     * This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     */
-    public ClassFile referencedClassFile;
-
-
-    public static LocalVariableInfo create(DataInput din) throws IOException
-    {
-        LocalVariableInfo lvi = new LocalVariableInfo();
-        lvi.read(din);
-        return lvi;
-    }
-
-    /**
-     * Returns name index into Constant Pool.
-     */
-    protected int getNameIndex()
-    {
-        return u2nameIndex;
-    }
-
-    /**
-     * Sets the name index.
-     */
-    protected void setNameIndex(int index)
-    {
-        u2nameIndex = index;
-    }
-
-    /**
-     * Returns descriptor index into Constant Pool.
+     * An extra field pointing to the referenced Clazz object.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      */
-    protected int getDescriptorIndex()
-    {
-        return u2descriptorIndex;
-    }
+    public Clazz referencedClass;
 
-    /**
-     * Sets the descriptor index.
-     */
-    protected void setDescriptorIndex(int index)
-    {
-        u2descriptorIndex = index;
-    }
-
-    private void read(DataInput din) throws IOException
-    {
-        u2startpc         = din.readUnsignedShort();
-        u2length          = din.readUnsignedShort();
-        u2nameIndex       = din.readUnsignedShort();
-        u2descriptorIndex = din.readUnsignedShort();
-        u2index           = din.readUnsignedShort();
-    }
 
     /**
-     * Exports the representation to a DataOutput stream.
+     * Lets the referenced class accept the given visitor.
      */
-    public void write(DataOutput dout) throws IOException
+    public void referencedClassAccept(ClassVisitor classVisitor)
     {
-        dout.writeShort(u2startpc);
-        dout.writeShort(u2length);
-        dout.writeShort(u2nameIndex);
-        dout.writeShort(u2descriptorIndex);
-        dout.writeShort(u2index);
+        if (referencedClass != null)
+        {
+            referencedClass.accept(classVisitor);
+        }
     }
 }
diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java b/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java
deleted file mode 100644
index 69e2e03..0000000
--- a/src/proguard/classfile/attribute/LocalVariableTableAttrInfo.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/* $Id: LocalVariableTableAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a local variable table attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LocalVariableTableAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int                 u2localVariableTableLength;
-    public LocalVariableInfo[] localVariableTable;
-
-
-    protected LocalVariableTableAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the array of local variable table entries.
-     */
-    protected LocalVariableInfo[] getLocalVariableTable() throws Exception
-    {
-        return localVariableTable;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE +
-               u2localVariableTableLength * LocalVariableInfo.CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2localVariableTableLength = din.readUnsignedShort();
-        localVariableTable = new LocalVariableInfo[u2localVariableTableLength];
-        for (int i = 0; i < u2localVariableTableLength; i++)
-        {
-            localVariableTable[i] = LocalVariableInfo.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2localVariableTableLength);
-        for (int i = 0; i < u2localVariableTableLength; i++)
-        {
-            localVariableTable[i].write(dout);
-        }
-    }
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    /**
-     * Applies the given visitor to all local variables.
-     */
-    public void localVariablesAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfoVisitor localVariableInfoVisitor)
-    {
-        for (int i = 0; i < u2localVariableTableLength; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of LocalVariableInfo.
-            localVariableInfoVisitor.visitLocalVariableInfo(classFile, methodInfo, codeAttrInfo, localVariableTable[i]);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/LocalVariableTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java
new file mode 100644
index 0000000..3aaea6e
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTableAttribute.java
@@ -0,0 +1,65 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+
+/**
+ * This Attribute represents a local variable table attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class LocalVariableTableAttribute extends Attribute
+{
+    public int                 u2localVariableTableLength;
+    public LocalVariableInfo[] localVariableTable;
+
+
+    /**
+     * Creates an uninitialized LocalVariableTableAttribute.
+     */
+    public LocalVariableTableAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitLocalVariableTableAttribute(clazz, method, codeAttribute, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all local variables.
+     */
+    public void localVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfoVisitor localVariableInfoVisitor)
+    {
+        for (int index = 0; index < u2localVariableTableLength; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LocalVariableInfo.
+            localVariableInfoVisitor.visitLocalVariableInfo(clazz, method, codeAttribute, localVariableTable[index]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
index 14f300e..9c3248c 100644
--- a/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
+++ b/src/proguard/classfile/attribute/LocalVariableTypeInfo.java
@@ -1,14 +1,13 @@
-/* $Id: LocalVariableTypeInfo.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,93 +20,47 @@
  */
 package proguard.classfile.attribute;
 
-import proguard.classfile.ClassFile;
-
-import java.io.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
  * Representation of an Local Variable table entry.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
 public class LocalVariableTypeInfo
 {
-    public static final int CONSTANT_FIELD_SIZE = 10;
-
-
-    public int u2startpc;
+    public int u2startPC;
     public int u2length;
     public int u2nameIndex;
     public int u2signatureIndex;
     public int u2index;
 
     /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * type string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * An extra field pointing to the Clazz objects referenced in the
+     * type string. This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      * References to primitive types are ignored.
      */
-    public ClassFile[] referencedClassFiles;
-
-
-    public static LocalVariableTypeInfo create(DataInput din) throws IOException
-    {
-        LocalVariableTypeInfo lvi = new LocalVariableTypeInfo();
-        lvi.read(din);
-        return lvi;
-    }
-
-    /**
-     * Returns name index into Constant Pool.
-     */
-    protected int getNameIndex()
-    {
-        return u2nameIndex;
-    }
-
-    /**
-     * Sets the name index.
-     */
-    protected void setNameIndex(int index)
-    {
-        u2nameIndex = index;
-    }
-
-    /**
-     * Returns descriptor index into Constant Pool.
-     */
-    protected int getDescriptorIndex()
-    {
-        return u2signatureIndex;
-    }
+    public Clazz[] referencedClasses;
 
-    /**
-     * Sets the descriptor index.
-     */
-    protected void setDescriptorIndex(int index)
-    {
-        u2signatureIndex = index;
-    }
-
-    private void read(DataInput din) throws IOException
-    {
-        u2startpc = din.readUnsignedShort();
-        u2length = din.readUnsignedShort();
-        u2nameIndex = din.readUnsignedShort();
-        u2signatureIndex = din.readUnsignedShort();
-        u2index = din.readUnsignedShort();
-    }
 
     /**
-     * Exports the representation to a DataOutput stream.
+     * Applies the given visitor to all referenced classes.
      */
-    public void write(DataOutput dout) throws IOException
+    public void referencedClassesAccept(ClassVisitor classVisitor)
     {
-        dout.writeShort(u2startpc);
-        dout.writeShort(u2length);
-        dout.writeShort(u2nameIndex);
-        dout.writeShort(u2signatureIndex);
-        dout.writeShort(u2index);
+        if (referencedClasses != null)
+        {
+            for (int index = 0; index < referencedClasses.length; index++)
+            {
+                Clazz referencedClass = referencedClasses[index];
+                if (referencedClass != null)
+                {
+                    referencedClass.accept(classVisitor);
+                }
+            }
+        }
     }
 }
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java
deleted file mode 100644
index a8ef40a..0000000
--- a/src/proguard/classfile/attribute/LocalVariableTypeTableAttrInfo.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/* $Id: LocalVariableTypeTableAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a local variable table type attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class LocalVariableTypeTableAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int                     u2localVariableTypeTableLength;
-    public LocalVariableTypeInfo[] localVariableTypeTable;
-
-
-    protected LocalVariableTypeTableAttrInfo()
-    {
-    }
-
-
-    /**
-     * Returns the array of local variable type table entries.
-     */
-    protected LocalVariableTypeInfo[] getLocalVariableTypeTable() throws Exception
-    {
-        return localVariableTypeTable;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE +
-               u2localVariableTypeTableLength * LocalVariableTypeInfo.CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2localVariableTypeTableLength = din.readUnsignedShort();
-        localVariableTypeTable = new LocalVariableTypeInfo[u2localVariableTypeTableLength];
-        for (int i = 0; i < u2localVariableTypeTableLength; i++)
-        {
-            localVariableTypeTable[i] = LocalVariableTypeInfo.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2localVariableTypeTableLength);
-        for (int i = 0; i < u2localVariableTypeTableLength; i++)
-        {
-            localVariableTypeTable[i].write(dout);
-        }
-    }
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    /**
-     * Applies the given visitor to all local variable types.
-     */
-    public void localVariablesAccept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfoVisitor localVariableTypeInfoVisitor)
-    {
-        for (int i = 0; i < u2localVariableTypeTableLength; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of LocalVariableTypeInfo.
-            localVariableTypeInfoVisitor.visitLocalVariableTypeInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTable[i]);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java
new file mode 100644
index 0000000..9e3bdaf
--- /dev/null
+++ b/src/proguard/classfile/attribute/LocalVariableTypeTableAttribute.java
@@ -0,0 +1,65 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+
+/**
+ * This Attribute represents a local variable table type attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class LocalVariableTypeTableAttribute extends Attribute
+{
+    public int                     u2localVariableTypeTableLength;
+    public LocalVariableTypeInfo[] localVariableTypeTable;
+
+
+    /**
+     * Creates an uninitialized LocalVariableTypeTableAttribute.
+     */
+    public LocalVariableTypeTableAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, this);
+    }
+
+
+    /**
+     * Applies the given visitor to all local variable types.
+     */
+    public void localVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfoVisitor localVariableTypeInfoVisitor)
+    {
+        for (int index = 0; index < u2localVariableTypeTableLength; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of LocalVariableTypeInfo.
+            localVariableTypeInfoVisitor.visitLocalVariableTypeInfo(clazz, method, codeAttribute, localVariableTypeTable[index]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java b/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java
deleted file mode 100644
index 4b6113e..0000000
--- a/src/proguard/classfile/attribute/MultiAttrInfoVisitor.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/* $Id: MultiAttrInfoVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-
-/**
- * This AttrInfoVisitor delegates all visits to each AttrInfoVisitor
- * in a given list.
- *
- * @author Eric Lafortune
- */
-public class MultiAttrInfoVisitor implements AttrInfoVisitor
-{
-    private static final int ARRAY_SIZE_INCREMENT = 5;
-
-    private AttrInfoVisitor[] attrInfoVisitors;
-    private int               attrInfoVisitorCount;
-
-
-    public MultiAttrInfoVisitor()
-    {
-    }
-
-
-    public MultiAttrInfoVisitor(AttrInfoVisitor[] attrInfoVisitors)
-    {
-        this.attrInfoVisitors     = attrInfoVisitors;
-        this.attrInfoVisitorCount = attrInfoVisitors.length;
-    }
-
-
-    public void addAttrInfoVisitor(AttrInfoVisitor attrInfoVisitor)
-    {
-        ensureArraySize();
-
-        attrInfoVisitors[attrInfoVisitorCount++] = attrInfoVisitor;
-    }
-
-
-    private void ensureArraySize()
-    {
-        if (attrInfoVisitors == null)
-        {
-            attrInfoVisitors = new AttrInfoVisitor[ARRAY_SIZE_INCREMENT];
-        }
-        else if (attrInfoVisitors.length == attrInfoVisitorCount)
-        {
-            AttrInfoVisitor[] newAttrInfoVisitors =
-                new AttrInfoVisitor[attrInfoVisitorCount +
-                                     ARRAY_SIZE_INCREMENT];
-            System.arraycopy(attrInfoVisitors, 0,
-                             newAttrInfoVisitors, 0,
-                             attrInfoVisitorCount);
-            attrInfoVisitors = newAttrInfoVisitors;
-        }
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitUnknownAttrInfo(classFile, unknownAttrInfo);
-        }
-    }
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitInnerClassesAttrInfo(classFile, innerClassesAttrInfo);
-        }
-    }
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitEnclosingMethodAttrInfo(classFile, enclosingMethodAttrInfo);
-        }
-    }
-
-
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitConstantValueAttrInfo(classFile, fieldInfo, constantValueAttrInfo);
-        }
-    }
-
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitExceptionsAttrInfo(classFile, methodInfo, exceptionsAttrInfo);
-        }
-    }
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
-        }
-    }
-
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, lineNumberTableAttrInfo);
-        }
-    }
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTableAttrInfo);
-        }
-    }
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTableAttrInfo);
-        }
-    }
-
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitSourceFileAttrInfo(classFile, sourceFileAttrInfo);
-        }
-    }
-
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitSourceDirAttrInfo(classFile, sourceDirAttrInfo);
-        }
-    }
-
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitDeprecatedAttrInfo(classFile, deprecatedAttrInfo);
-        }
-    }
-
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitSyntheticAttrInfo(classFile, syntheticAttrInfo);
-        }
-    }
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo syntheticAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitSignatureAttrInfo(classFile, syntheticAttrInfo);
-        }
-    }
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitRuntimeVisibleAnnotationAttrInfo(classFile, runtimeVisibleAnnotationsAttrInfo);
-        }
-    }
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitRuntimeInvisibleAnnotationAttrInfo(classFile, runtimeInvisibleAnnotationsAttrInfo);
-        }
-    }
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, runtimeVisibleParameterAnnotationsAttrInfo);
-        }
-    }
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, runtimeInvisibleParameterAnnotationsAttrInfo);
-        }
-    }
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        for (int index = 0; index < attrInfoVisitorCount; index++)
-        {
-            attrInfoVisitors[index].visitAnnotationDefaultAttrInfo(classFile, annotationDefaultAttrInfo);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/proguard/classfile/attribute/SignatureAttrInfo.java b/src/proguard/classfile/attribute/SignatureAttrInfo.java
deleted file mode 100644
index 64b81d4..0000000
--- a/src/proguard/classfile/attribute/SignatureAttrInfo.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/* $Id: SignatureAttrInfo.java,v 1.3.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.ClassFileVisitor;
-
-import java.io.*;
-
-/**
- * Representation of a signature attribute.
- *
- * @author Eric Lafortune
- */
-public class SignatureAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int u2signatureIndex;
-
-    /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * signature string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
-     * References to primitive types are ignored.
-     */
-    public ClassFile[] referencedClassFiles;
-
-
-    protected SignatureAttrInfo()
-    {
-    }
-
-
-    /**
-     * Lets the ClassFile objects referenced in the signature string
-     * accept the given visitor.
-     */
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
-    {
-        if (referencedClassFiles != null)
-        {
-            for (int i = 0; i < referencedClassFiles.length; i++)
-            {
-                if (referencedClassFiles[i] != null)
-                {
-                    referencedClassFiles[i].accept(classFileVisitor);
-                }
-            }
-        }
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2signatureIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2signatureIndex);
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitSignatureAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/SignatureAttribute.java b/src/proguard/classfile/attribute/SignatureAttribute.java
new file mode 100644
index 0000000..9f7565c
--- /dev/null
+++ b/src/proguard/classfile/attribute/SignatureAttribute.java
@@ -0,0 +1,88 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This Attribute represents a signature attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class SignatureAttribute extends Attribute
+{
+    public int u2signatureIndex;
+
+    /**
+     * An extra field pointing to the Clazz objects referenced in the
+     * signature string. This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     * References to primitive types are ignored.
+     */
+    public Clazz[] referencedClasses;
+
+
+    /**
+     * Creates an uninitialized SignatureAttribute.
+     */
+    public SignatureAttribute()
+    {
+    }
+
+
+    /**
+     * Lets the Clazz objects referenced in the signature string accept the
+     * given visitor.
+     */
+    public void referencedClassesAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClasses != null)
+        {
+            for (int index = 0; index < referencedClasses.length; index++)
+            {
+                if (referencedClasses[index] != null)
+                {
+                    referencedClasses[index].accept(classVisitor);
+                }
+            }
+        }
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSignatureAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSignatureAttribute(clazz, field, this);
+    }
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSignatureAttribute(clazz, method, this);
+    }
+ }
diff --git a/src/proguard/classfile/attribute/SourceDirAttrInfo.java b/src/proguard/classfile/attribute/SourceDirAttrInfo.java
deleted file mode 100644
index 6de52e9..0000000
--- a/src/proguard/classfile/attribute/SourceDirAttrInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id: SourceDirAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a source directory attribute.
- *
- * @author Eric Lafortune
- */
-public class SourceDirAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int u2sourceDirIndex;
-
-
-    protected SourceDirAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2sourceDirIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2sourceDirIndex);
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitSourceDirAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/attribute/SourceDirAttribute.java
similarity index 55%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/attribute/SourceDirAttribute.java
index d9a4a34..80c90bc 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/attribute/SourceDirAttribute.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,33 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
+package proguard.classfile.attribute;
 
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 
 /**
- * Representation of a field from a class file.
+ * This Attribute represents a source directory attribute.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class SourceDirAttribute extends Attribute
 {
+    public int u2sourceDirIndex;
+
+
+    /**
+     * Creates an uninitialized SourceDirAttribute.
+     */
+    public SourceDirAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSourceDirAttribute(clazz, this);
+    }
 }
diff --git a/src/proguard/classfile/attribute/SourceFileAttrInfo.java b/src/proguard/classfile/attribute/SourceFileAttrInfo.java
deleted file mode 100644
index 3d6e53e..0000000
--- a/src/proguard/classfile/attribute/SourceFileAttrInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/* $Id: SourceFileAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a source file attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class SourceFileAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int u2sourceFileIndex;
-
-
-    protected SourceFileAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2sourceFileIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2sourceFileIndex);
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitSourceFileAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/attribute/SourceFileAttribute.java
similarity index 55%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/attribute/SourceFileAttribute.java
index d9a4a34..702a3f9 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/attribute/SourceFileAttribute.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,33 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
+package proguard.classfile.attribute;
 
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 
 /**
- * Representation of a field from a class file.
+ * This Attribute represents a source file attribute.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class SourceFileAttribute extends Attribute
 {
+    public int u2sourceFileIndex;
+
+
+    /**
+     * Creates an uninitialized SourceFileAttribute.
+     */
+    public SourceFileAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSourceFileAttribute(clazz, this);
+    }
 }
diff --git a/src/proguard/classfile/attribute/SyntheticAttrInfo.java b/src/proguard/classfile/attribute/SyntheticAttrInfo.java
deleted file mode 100644
index 2a1afd9..0000000
--- a/src/proguard/classfile/attribute/SyntheticAttrInfo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/* $Id: SyntheticAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of a synthetic attribute.
- *
- * @author Mark Welsh
- * @author Eric Lafortune
- */
-public class SyntheticAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 0;
-
-
-    protected SyntheticAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitSyntheticAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/SyntheticAttribute.java b/src/proguard/classfile/attribute/SyntheticAttribute.java
new file mode 100644
index 0000000..0e7444c
--- /dev/null
+++ b/src/proguard/classfile/attribute/SyntheticAttribute.java
@@ -0,0 +1,57 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents a synthetic attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class SyntheticAttribute extends Attribute
+{
+    /**
+     * Creates an uninitialized SyntheticAttribute.
+     */
+    public SyntheticAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSyntheticAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSyntheticAttribute(clazz, field, this);
+    }
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitSyntheticAttribute(clazz, method, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/UnknownAttrInfo.java b/src/proguard/classfile/attribute/UnknownAttrInfo.java
deleted file mode 100644
index ac900cc..0000000
--- a/src/proguard/classfile/attribute/UnknownAttrInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/* $Id: UnknownAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute;
-
-
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.*;
-
-import java.io.*;
-
-/**
- * Representation of an unknown attribute.
- *
- * @author Eric Lafortune
- */
-public class UnknownAttrInfo extends AttrInfo
-{
-    public int  u4attrLength;
-    public byte info[];
-
-
-    protected UnknownAttrInfo(int attrLength)
-    {
-        u4attrLength = attrLength;
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return u4attrLength;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        info = new byte[u4attrLength];
-        din.readFully(info);
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.write(info);
-    }
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitUnknownAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/UnknownAttribute.java b/src/proguard/classfile/attribute/UnknownAttribute.java
new file mode 100644
index 0000000..b6f2a61
--- /dev/null
+++ b/src/proguard/classfile/attribute/UnknownAttribute.java
@@ -0,0 +1,68 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute;
+
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents an unknown attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class UnknownAttribute extends Attribute
+{
+    public final int    u4attributeLength;
+    public byte[] info;
+
+
+    /**
+     * Creates an uninitialized UnknownAttribute with the given length.
+     */
+    public UnknownAttribute(int attributeLength)
+    {
+        u4attributeLength = attributeLength;
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitUnknownAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitUnknownAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitUnknownAttribute(clazz, this);
+    }
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitUnknownAttribute(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/Annotation.java b/src/proguard/classfile/attribute/annotation/Annotation.java
index 227b955..d168b14 100644
--- a/src/proguard/classfile/attribute/annotation/Annotation.java
+++ b/src/proguard/classfile/attribute/annotation/Annotation.java
@@ -1,13 +1,13 @@
-/* $Id: Annotation.java,v 1.4.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,32 +21,28 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.ClassFileVisitor;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * Representation of a runtime annotation.
+ * Representation of an annotation.
  *
  * @author Eric Lafortune
  */
 public class Annotation implements VisitorAccepter
 {
-    private static final int CONSTANT_FIELD_SIZE  = 4;
-    private static final int CONSTANT_FIELD_SIZE2 = 2;
-
-
     public int            u2typeIndex;
-    public int            u2numberOfElementValuePairs;
+    public int            u2elementValuesCount;
     public ElementValue[] elementValues;
 
     /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * type string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * An extra field pointing to the Clazz objects referenced in the
+     * type string. This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      * References to primitive types are ignored.
      */
-    public ClassFile[] referencedClassFiles;
+    public Clazz[] referencedClasses;
 
     /**
      * An extra field in which visitors can store information.
@@ -54,73 +50,28 @@ public class Annotation implements VisitorAccepter
     public Object visitorInfo;
 
 
-    public static Annotation create(DataInput din) throws IOException
-    {
-        Annotation annotation = new Annotation();
-        annotation.read(din);
-        return annotation;
-    }
-
-    private void read(DataInput din) throws IOException
-    {
-        u2typeIndex                 = din.readUnsignedShort();
-        u2numberOfElementValuePairs = din.readUnsignedShort();
-
-        elementValues = new ElementValue[u2numberOfElementValuePairs];
-
-        for (int i = 0; i < u2numberOfElementValuePairs; i++)
-        {
-            int u2elementName = din.readUnsignedShort();
-
-            elementValues[i] = ElementValue.create(din);
-
-            elementValues[i].u2elementName = u2elementName;
-        }
-    }
-
     /**
-     * Exports the representation to a DataOutput stream.
+     * Returns the type.
      */
-    public void write(DataOutput dout) throws IOException
+    public String getType(Clazz clazz)
     {
-        dout.writeShort(u2typeIndex);
-        dout.writeShort(u2numberOfElementValuePairs);
-        for (int i = 0; i < u2numberOfElementValuePairs; i++)
-        {
-            dout.writeShort(elementValues[i].u2elementName);
-
-            elementValues[i].write(dout);
-        }
+        return clazz.getString(u2typeIndex);
     }
 
 
-    /**
-     * Returns the length of this annotation, expressed in bytes.
-     */
-    protected int getLength()
-    {
-        int length = CONSTANT_FIELD_SIZE;
-        for (int i = 0; i < u2numberOfElementValuePairs; i++)
-        {
-            length += CONSTANT_FIELD_SIZE2 + elementValues[i].getLength();
-        }
-
-        return length;
-    }
-
 
     /**
      * Applies the given visitor to the first referenced class. This is the
      * main annotation class.
      */
-    public void referencedClassFileAccept(ClassFileVisitor classFileVisitor)
+    public void referencedClassAccept(ClassVisitor classVisitor)
     {
-        if (referencedClassFiles != null)
+        if (referencedClasses != null)
         {
-            ClassFile referencedClassFile = referencedClassFiles[0];
-            if (referencedClassFile != null)
+            Clazz referencedClass = referencedClasses[0];
+            if (referencedClass != null)
             {
-                referencedClassFile.accept(classFileVisitor);
+                referencedClass.accept(classVisitor);
             }
         }
     }
@@ -129,16 +80,16 @@ public class Annotation implements VisitorAccepter
     /**
      * Applies the given visitor to all referenced classes.
      */
-    public void referencedClassFilesAccept(ClassFileVisitor classFileVisitor)
+    public void referencedClassesAccept(ClassVisitor classVisitor)
     {
-        if (referencedClassFiles != null)
+        if (referencedClasses != null)
         {
-            for (int index = 0; index < referencedClassFiles.length; index++)
+            for (int index = 0; index < referencedClasses.length; index++)
             {
-                ClassFile referencedClassFile = referencedClassFiles[index];
-                if (referencedClassFile != null)
+                Clazz referencedClass = referencedClasses[index];
+                if (referencedClass != null)
                 {
-                    referencedClassFile.accept(classFileVisitor);
+                    referencedClass.accept(classVisitor);
                 }
             }
         }
@@ -148,11 +99,11 @@ public class Annotation implements VisitorAccepter
     /**
      * Applies the given visitor to all element value pairs.
      */
-    public void elementValuesAccept(ClassFile classFile, ElementValueVisitor elementValueVisitor)
+    public void elementValuesAccept(Clazz clazz, ElementValueVisitor elementValueVisitor)
     {
-        for (int i = 0; i < u2numberOfElementValuePairs; i++)
+        for (int index = 0; index < u2elementValuesCount; index++)
         {
-            elementValues[i].accept(classFile, this, elementValueVisitor);
+            elementValues[index].accept(clazz, this, elementValueVisitor);
         }
     }
 
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java
deleted file mode 100644
index 261ab9c..0000000
--- a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttrInfo.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* $Id: AnnotationDefaultAttrInfo.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.ClassFile;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of an annotation default attribute.
- *
- * @author Eric Lafortune
- */
-public class AnnotationDefaultAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 0;
-
-
-    public ElementValue defaultValue;
-
-
-    public AnnotationDefaultAttrInfo()
-    {
-    }
-
-
-    /**
-     * Applies the given visitor to the default element value.
-     */
-    public void defaultValueAccept(ClassFile classFile, ElementValueVisitor elementValueVisitor)
-    {
-        defaultValue.accept(classFile, null, elementValueVisitor);
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE + defaultValue.getLength();
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        defaultValue = ElementValue.create(din);
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        defaultValue.write(dout);
-    }
-
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitAnnotationDefaultAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java
new file mode 100644
index 0000000..9c8dc69
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/AnnotationDefaultAttribute.java
@@ -0,0 +1,61 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents an annotation default attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationDefaultAttribute extends Attribute
+{
+    public ElementValue defaultValue;
+
+
+    /**
+     * Creates an uninitialized AnnotationDefaultAttribute.
+     */
+    public AnnotationDefaultAttribute()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to the default element value.
+     */
+    public void defaultValueAccept(Clazz clazz, ElementValueVisitor elementValueVisitor)
+    {
+        defaultValue.accept(clazz, null, elementValueVisitor);
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
index 63ded40..2ec0f48 100644
--- a/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/AnnotationElementValue.java
@@ -1,14 +1,13 @@
-/* $Id: AnnotationElementValue.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -22,23 +21,22 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.*;
 
 /**
- * Representation of an annotation element value.
+ * This ElementValue represents an annotation element value.
  *
  * @author Eric Lafortune
  */
 public class AnnotationElementValue extends ElementValue
 {
-    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE;
-
-
     public Annotation annotationValue;
 
 
-    protected AnnotationElementValue()
+    /**
+     * Creates an uninitialized AnnotationElementValue.
+     */
+    public AnnotationElementValue()
     {
     }
 
@@ -46,31 +44,21 @@ public class AnnotationElementValue extends ElementValue
     /**
      * Applies the given visitor to the annotation.
      */
-    public void annotationAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
+    public void annotationAccept(Clazz clazz, AnnotationVisitor annotationVisitor)
     {
-        annotationVisitor.visitAnnotation(classFile, annotationValue);
+        annotationVisitor.visitAnnotation(clazz, annotationValue);
     }
 
 
     // Implementations for ElementValue.
 
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE + annotationValue.getLength();
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        annotationValue = Annotation.create(din);
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
+    public int getTag()
     {
-        annotationValue.write(dout);
+        return ClassConstants.ELEMENT_VALUE_ANNOTATION;
     }
 
-    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        elementValueVisitor.visitAnnotationElementValue(classFile, annotation, this);
+        elementValueVisitor.visitAnnotationElementValue(clazz, annotation, this);
     }
 }
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java
new file mode 100644
index 0000000..68eb42d
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/AnnotationsAttribute.java
@@ -0,0 +1,83 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor;
+
+/**
+ * This Attribute represents an annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class AnnotationsAttribute extends Attribute
+{
+    public int          u2annotationsCount;
+    public Annotation[] annotations;
+
+
+    protected AnnotationsAttribute()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to all class annotations.
+     */
+    public void annotationsAccept(Clazz clazz, AnnotationVisitor annotationVisitor)
+    {
+        for (int index = 0; index < u2annotationsCount; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of Annotation.
+            annotationVisitor.visitAnnotation(clazz, annotations[index]);
+        }
+    }
+
+
+    /**
+     * Applies the given visitor to all field annotations.
+     */
+    public void annotationsAccept(Clazz clazz, Field field, AnnotationVisitor annotationVisitor)
+    {
+        for (int index = 0; index < u2annotationsCount; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of Annotation.
+            annotationVisitor.visitAnnotation(clazz, field, annotations[index]);
+        }
+    }
+
+
+    /**
+     * Applies the given visitor to all method annotations.
+     */
+    public void annotationsAccept(Clazz clazz, Method method, AnnotationVisitor annotationVisitor)
+    {
+        for (int index = 0; index < u2annotationsCount; index++)
+        {
+            // We don't need double dispatching here, since there is only one
+            // type of Annotation.
+            annotationVisitor.visitAnnotation(clazz, method, annotations[index]);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
index 192dc78..d63b4db 100644
--- a/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ArrayElementValue.java
@@ -1,14 +1,13 @@
-/* $Id: ArrayElementValue.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -22,74 +21,48 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
 
 /**
- * Representation of an array element value.
+ * This ElementValue represents an array element value.
  *
  * @author Eric Lafortune
  */
 public class ArrayElementValue extends ElementValue
 {
-    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
-
+    public int            u2elementValuesCount;
+    public ElementValue[] elementValues;
 
-    public int            u2numberOfValues;
-    public ElementValue[] values;
 
-
-    protected ArrayElementValue()
+    /**
+     * Creates an uninitialized ArrayElementValue.
+     */
+    public ArrayElementValue()
     {
     }
 
 
     // Implementations for ElementValue.
 
-    protected int getLength()
+    public int getTag()
     {
-        int length = CONSTANT_FIELD_SIZE;
-        for (int i = 0; i < u2numberOfValues; i++)
-        {
-            length += values[i].getLength();
-        }
-
-        return length;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2numberOfValues = din.readUnsignedShort();
-        values = new ElementValue[u2numberOfValues];
-        for (int i = 0; i < u2numberOfValues; i++)
-        {
-            values[i] = ElementValue.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2numberOfValues);
-        for (int i = 0; i < u2numberOfValues; i++)
-        {
-            values[i].write(dout);
-        }
+        return ClassConstants.ELEMENT_VALUE_ARRAY;
     }
 
-    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        elementValueVisitor.visitArrayElementValue(classFile, annotation, this);
+        elementValueVisitor.visitArrayElementValue(clazz, annotation, this);
     }
 
 
     /**
      * Applies the given visitor to all nested element values.
      */
-    public void elementValuesAccept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void elementValuesAccept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        for (int i = 0; i < u2numberOfValues; i++)
+        for (int index = 0; index < u2elementValuesCount; index++)
         {
-            values[i].accept(classFile, annotation, elementValueVisitor);
+            elementValues[index].accept(clazz, annotation, elementValueVisitor);
         }
     }
 }
diff --git a/src/proguard/classfile/attribute/annotation/ClassElementValue.java b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
index 7e3fe62..95ee572 100644
--- a/src/proguard/classfile/attribute/annotation/ClassElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ClassElementValue.java
@@ -1,14 +1,13 @@
-/* $Id: ClassElementValue.java,v 1.3.2.3 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -22,32 +21,31 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.ClassFileVisitor;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * Representation of a class element value.
+ * This ElementValue represents a class element value.
  *
  * @author Eric Lafortune
  */
 public class ClassElementValue extends ElementValue
 {
-    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
-
-
     public int u2classInfoIndex;
 
     /**
-     * An extra field pointing to the ClassFile objects referenced in the
+     * An extra field pointing to the Clazz objects referenced in the
      * type name string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
      * References to primitive types are ignored.
      */
-    public ClassFile[] referencedClassFiles;
+    public Clazz[] referencedClasses;
 
 
-    protected ClassElementValue()
+    /**
+     * Creates an uninitialized ClassElementValue.
+     */
+    public ClassElementValue()
     {
     }
 
@@ -55,16 +53,16 @@ public class ClassElementValue extends ElementValue
     /**
      * Applies the given visitor to all referenced classes.
      */
-    public void referencedClassesAccept(ClassFileVisitor classFileVisitor)
+    public void referencedClassesAccept(ClassVisitor classVisitor)
     {
-        if (referencedClassFiles != null)
+        if (referencedClasses != null)
         {
-            for (int index = 0; index < referencedClassFiles.length; index++)
+            for (int index = 0; index < referencedClasses.length; index++)
             {
-                ClassFile referencedClassFile = referencedClassFiles[index];
-                if (referencedClassFile != null)
+                Clazz referencedClass = referencedClasses[index];
+                if (referencedClass != null)
                 {
-                    referencedClassFile.accept(classFileVisitor);
+                    referencedClass.accept(classVisitor);
                 }
             }
         }
@@ -73,23 +71,13 @@ public class ClassElementValue extends ElementValue
 
     // Implementations for ElementValue.
 
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2classInfoIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
+    public int getTag()
     {
-        dout.writeShort(u2classInfoIndex);
+        return ClassConstants.ELEMENT_VALUE_CLASS;
     }
 
-    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        elementValueVisitor.visitClassElementValue(classFile, annotation, this);
+        elementValueVisitor.visitClassElementValue(clazz, annotation, this);
     }
 }
diff --git a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
index 3a96fa2..f31c244 100644
--- a/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ConstantElementValue.java
@@ -1,14 +1,13 @@
-/* $Id: ConstantElementValue.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,47 +20,38 @@
  */
 package proguard.classfile.attribute.annotation;
 
-import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
 
 /**
- * Representation of a constant element value.
+ * This ElementValue represents a constant element value.
  *
  * @author Eric Lafortune
  */
 public class ConstantElementValue extends ElementValue
 {
-    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 2;
-
-
+    public final int u1tag;
     public int u2constantValueIndex;
 
 
-    protected ConstantElementValue()
+    /**
+     * Creates an uninitialized ConstantElementValue.
+     */
+    public ConstantElementValue(int u1tag)
     {
+        this.u1tag = u1tag;
     }
 
 
     // Implementations for ElementValue.
 
-    protected int getLength()
-    {
-        return CONSTANT_FIELD_SIZE;
-    }
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2constantValueIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
+    public int getTag()
     {
-        dout.writeShort(u2constantValueIndex);
+        return u1tag;
     }
 
-    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        elementValueVisitor.visitConstantElementValue(classFile, annotation, this);
+        elementValueVisitor.visitConstantElementValue(clazz, annotation, this);
     }
 }
diff --git a/src/proguard/classfile/attribute/annotation/ElementValue.java b/src/proguard/classfile/attribute/annotation/ElementValue.java
index 9ed47c0..938d5a5 100644
--- a/src/proguard/classfile/attribute/annotation/ElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/ElementValue.java
@@ -1,13 +1,13 @@
-/* $Id: ElementValue.java,v 1.6.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -21,41 +21,37 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.visitor.MemberVisitor;
 
 /**
- * Representation of an element value.
+ * This abstract class represents an element value that is attached to an
+ * annotation or an annotation default. Specific types of element values are
+ * subclassed from it.
  *
  * @author Eric Lafortune
  */
 public abstract class ElementValue implements VisitorAccepter
 {
-    protected static final int CONSTANT_FIELD_SIZE = 1;
-
-
-    public int u1tag;
-
     /**
      * An extra field for the optional element name. It is used in element value
      * pairs of annotations. Otherwise, it is 0.
      */
-    public int u2elementName;
+    public int u2elementNameIndex;
 
     /**
-     * An extra field pointing to the referenced ClassFile object.
-     * This field is typically filled out by the <code>{@link
-     * proguard.classfile.util.ClassReferenceInitializer}</code>.
+     * An extra field pointing to the referenced <code>Clazz</code>
+     * object, if applicable. This field is typically filled out by the
+     * <code>{@link proguard.classfile.util.ClassReferenceInitializer}</code>.
      */
-    public ClassFile referencedClassFile;
+    public Clazz referencedClass;
 
     /**
-     * An extra field pointing to the referenced <code>MethodInfo</code>
+     * An extra field pointing to the referenced <code>Method</code>
      * object, if applicable. This field is typically filled out by the
-     * <code>{@link proguard.classfile.util.ClassFileReferenceInitializer}</code>.
+     * <code>{@link proguard.classfile.util.ClassReferenceInitializer}</code>.
      */
-    public MethodInfo referencedMethodInfo;
+    public Method referencedMethod;
 
     /**
      * An extra field in which visitors can store information.
@@ -63,100 +59,38 @@ public abstract class ElementValue implements VisitorAccepter
     public Object visitorInfo;
 
 
-    public static ElementValue create(DataInput din) throws IOException
-    {
-        int u1tag = din.readUnsignedByte();
-        ElementValue elementValue = createElementValue(u1tag);
-
-        elementValue.u1tag = u1tag;
-        elementValue.readInfo(din);
-
-        return elementValue;
-    }
-
-
-    private static ElementValue createElementValue(int u1tag)
-    {
-        switch (u1tag)
-        {
-            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
-            case ClassConstants.INTERNAL_TYPE_BYTE:
-            case ClassConstants.INTERNAL_TYPE_CHAR:
-            case ClassConstants.INTERNAL_TYPE_SHORT:
-            case ClassConstants.INTERNAL_TYPE_INT:
-            case ClassConstants.INTERNAL_TYPE_FLOAT:
-            case ClassConstants.INTERNAL_TYPE_LONG:
-            case ClassConstants.INTERNAL_TYPE_DOUBLE:
-            case ClassConstants.ELEMENT_VALUE_STRING_CONSTANT: return new ConstantElementValue();
-
-            case ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT:   return new EnumConstantElementValue();
-            case ClassConstants.ELEMENT_VALUE_CLASS:           return new ClassElementValue();
-            case ClassConstants.ELEMENT_VALUE_ANNOTATION:      return new AnnotationElementValue();
-            case ClassConstants.ELEMENT_VALUE_ARRAY:           return new ArrayElementValue();
-        }
-
-        throw new IllegalArgumentException("Unknown element value tag ["+u1tag+"]");
-    }
-
-
-    protected ElementValue()
-    {
-    }
-
-
-    /**
-     * Exports the representation to a DataOutput stream.
-     */
-    public final void write(DataOutput dout) throws IOException
-    {
-        dout.writeByte(u1tag);
-        writeInfo(dout);
-    }
-
-
     /**
      * Returns the element name.
      */
-    public String getMethodName(ClassFile classFile)
+    public String getMethodName(Clazz clazz)
     {
-        return classFile.getCpString(u2elementName);
+        return clazz.getString(u2elementNameIndex);
     }
 
 
     // Abstract methods to be implemented by extensions.
 
     /**
-     * Returns the length of this element value, expressed in bytes.
+     * Returns the tag of this element value.
      */
-    protected abstract int getLength();
-
-
-    /**
-     * Reads the data following the header.
-     */
-    protected abstract void readInfo(DataInput din) throws IOException;
-
-
-    /**
-     * Exports data following the header to a DataOutput stream.
-     */
-    protected abstract void writeInfo(DataOutput dout) throws IOException;
+    public abstract int getTag();
 
 
     /**
      * Accepts the given visitor.
      */
-    public abstract void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor);
+    public abstract void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor);
+
 
 
     /**
      * Applies the given visitor to the referenced method.
      */
-    public void referencedMethodInfoAccept(MemberInfoVisitor memberInfoVisitor)
+    public void referencedMethodAccept(MemberVisitor memberVisitor)
     {
-        if (referencedMethodInfo != null)
+        if (referencedMethod != null)
         {
-            referencedMethodInfo.accept(referencedClassFile, memberInfoVisitor);
+            referencedMethod.accept(referencedClass, memberVisitor);
         }
     }
 
diff --git a/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java
deleted file mode 100644
index 1464ef1..0000000
--- a/src/proguard/classfile/attribute/annotation/ElementValueVisitor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/* $Id: ElementValueVisitor.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-
-
-/**
- * This interface specifies the methods for a visitor of <code>ElementValue</code>
- * objects.
- *
- * @author Eric Lafortune
- */
-public interface ElementValueVisitor
-{
-    public void visitConstantElementValue(    ClassFile classFile, Annotation annotation, ConstantElementValue     constantElementValue);
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue);
-    public void visitClassElementValue(       ClassFile classFile, Annotation annotation, ClassElementValue        classElementValue);
-    public void visitAnnotationElementValue(  ClassFile classFile, Annotation annotation, AnnotationElementValue   annotationElementValue);
-    public void visitArrayElementValue(       ClassFile classFile, Annotation annotation, ArrayElementValue        arrayElementValue);
-}
diff --git a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
index 52f5d2c..ae3dc2c 100644
--- a/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
+++ b/src/proguard/classfile/attribute/annotation/EnumConstantElementValue.java
@@ -1,14 +1,13 @@
-/* $Id: EnumConstantElementValue.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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 library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -22,57 +21,65 @@
 package proguard.classfile.attribute.annotation;
 
 import proguard.classfile.*;
-
-import java.io.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * Representation of an enumeration constant element value.
+ * This ElementValue represents an enumeration constant element value.
  *
  * @author Eric Lafortune
  */
 public class EnumConstantElementValue extends ElementValue
 {
-    protected static final int CONSTANT_FIELD_SIZE = ElementValue.CONSTANT_FIELD_SIZE + 4;
-
-
     public int u2typeNameIndex;
     public int u2constantNameIndex;
 
     /**
-     * An extra field pointing to the ClassFile objects referenced in the
-     * type name string. This field is filled out by the <code>{@link
-     * proguard.classfile.util.ClassFileReferenceInitializer ClassFileReferenceInitializer}</code>.
+     * An extra field pointing to the Clazz objects referenced in the
+     * type name string. This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      * References to primitive types are ignored.
      */
-    public ClassFile[] referencedClassFiles;
+    public Clazz[] referencedClasses;
 
 
-    protected EnumConstantElementValue()
+    /**
+     * Creates an uninitialized EnumConstantElementValue.
+     */
+    public EnumConstantElementValue()
     {
     }
 
 
-    // Implementations for ElementValue.
-
-    protected int getLength()
+    /**
+     * Applies the given visitor to all referenced classes.
+     */
+    public void referencedClassesAccept(ClassVisitor classVisitor)
     {
-        return CONSTANT_FIELD_SIZE;
+        if (referencedClasses != null)
+        {
+            for (int index = 0; index < referencedClasses.length; index++)
+            {
+                Clazz referencedClass = referencedClasses[index];
+                if (referencedClass != null)
+                {
+                    referencedClass.accept(classVisitor);
+                }
+            }
+        }
     }
 
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2typeNameIndex     = din.readUnsignedShort();
-        u2constantNameIndex = din.readUnsignedShort();
-    }
 
-    protected void writeInfo(DataOutput dout) throws IOException
+    // Implementations for ElementValue.
+
+    public int getTag()
     {
-        dout.writeShort(u2typeNameIndex);
-        dout.writeShort(u2constantNameIndex);
+        return ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT;
     }
 
-    public void accept(ClassFile classFile, Annotation annotation, ElementValueVisitor elementValueVisitor)
+    public void accept(Clazz clazz, Annotation annotation, ElementValueVisitor elementValueVisitor)
     {
-        elementValueVisitor.visitEnumConstantElementValue(classFile, annotation, this);
+        elementValueVisitor.visitEnumConstantElementValue(clazz, annotation, this);
     }
 }
diff --git a/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java
new file mode 100644
index 0000000..d3a6f14
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/ParameterAnnotationsAttribute.java
@@ -0,0 +1,64 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor;
+
+/**
+ * This Attribute represents a runtime parameter annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class ParameterAnnotationsAttribute extends Attribute
+{
+    public int            u2parametersCount;
+    public int[]          u2parameterAnnotationsCount;
+    public Annotation[][] parameterAnnotations;
+
+
+    protected ParameterAnnotationsAttribute()
+    {
+    }
+
+
+    /**
+     * Applies the given visitor to all annotations.
+     */
+    public void annotationsAccept(Clazz clazz, Method method, AnnotationVisitor annotationVisitor)
+    {
+        // Loop over all parameters.
+        for (int parameterIndex = 0; parameterIndex < u2parametersCount; parameterIndex++)
+        {
+            int          annotationsCount = u2parameterAnnotationsCount[parameterIndex];
+            Annotation[] annotations      = parameterAnnotations[parameterIndex];
+
+            // Loop over all parameter annotations.
+            for (int index = 0; index < annotationsCount; index++)
+            {
+                // We don't need double dispatching here, since there is only one
+                // type of Annotation.
+                annotationVisitor.visitAnnotation(clazz, method, parameterIndex, annotations[index]);
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java
deleted file mode 100644
index 84e56b1..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/* $Id: RuntimeAnnotationsAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-
-import java.io.*;
-
-/**
- * Representation of a runtime annotations attribute.
- *
- * @author Eric Lafortune
- */
-public abstract class RuntimeAnnotationsAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE = 2;
-
-
-    public int          u2numberOfAnnotations;
-    public Annotation[] annotations;
-
-
-    protected RuntimeAnnotationsAttrInfo()
-    {
-    }
-
-
-    /**
-     * Applies the given visitor to all annotations.
-     */
-    public void annotationsAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
-    {
-        for (int i = 0; i < u2numberOfAnnotations; i++)
-        {
-            // We don't need double dispatching here, since there is only one
-            // type of Annotation.
-            annotationVisitor.visitAnnotation(classFile, annotations[i]);
-        }
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        int length = CONSTANT_FIELD_SIZE;
-        for (int i = 0; i < u2numberOfAnnotations; i++)
-        {
-            length += annotations[i].getLength();
-        }
-
-        return length;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2numberOfAnnotations = din.readUnsignedShort();
-        annotations = new Annotation[u2numberOfAnnotations];
-        for (int i = 0; i < u2numberOfAnnotations; i++)
-        {
-            annotations[i] = Annotation.create(din);
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2numberOfAnnotations);
-        for (int i = 0; i < u2numberOfAnnotations; i++)
-        {
-            annotations[i].write(dout);
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java
deleted file mode 100644
index b2c7623..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* $Id: RuntimeInvisibleAnnotationsAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.ClassFile;
-import proguard.classfile.attribute.AttrInfoVisitor;
-
-/**
- * Representation of a runtime invisible annotations attribute.
- *
- * @author Eric Lafortune
- */
-public class RuntimeInvisibleAnnotationsAttrInfo extends RuntimeAnnotationsAttrInfo
-{
-    public RuntimeInvisibleAnnotationsAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitRuntimeInvisibleAnnotationAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java
new file mode 100644
index 0000000..2d5671e
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleAnnotationsAttribute.java
@@ -0,0 +1,59 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents a runtime invisible annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeInvisibleAnnotationsAttribute extends AnnotationsAttribute
+{
+    /**
+     * Creates an uninitialized RuntimeInvisibleAnnotationsAttribute.
+     */
+    public RuntimeInvisibleAnnotationsAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, this);
+    }
+
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, field, this);
+    }
+
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, method, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java
deleted file mode 100644
index 9a2feff..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* $Id: RuntimeInvisibleParameterAnnotationsAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.ClassFile;
-import proguard.classfile.attribute.AttrInfoVisitor;
-
-/**
- * Representation of a runtime invisible parameter annotations attribute.
- *
- * @author Eric Lafortune
- */
-public class RuntimeInvisibleParameterAnnotationsAttrInfo extends RuntimeParameterAnnotationsAttrInfo
-{
-    public RuntimeInvisibleParameterAnnotationsAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/VisitorAccepter.java b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
similarity index 50%
copy from src/proguard/classfile/VisitorAccepter.java
copy to src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
index a54e7e8..b25af56 100644
--- a/src/proguard/classfile/VisitorAccepter.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeInvisibleParameterAnnotationsAttribute.java
@@ -1,8 +1,7 @@
-/* $Id: VisitorAccepter.java,v 1.12.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,30 +18,30 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
-
+package proguard.classfile.attribute.annotation;
 
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 
 /**
- * This interface is a base interface for visitor accepters. It allows
- * visitors to set and get any temporary information they desire on the
- * objects they are visiting. Note that every visitor accepter has only one
- * such property, so visitors will have to take care not to overwrite each
- * other's information, if it is still required.
+ * This Attribute represents a runtime invisible parameter annotations attribute.
  *
  * @author Eric Lafortune
  */
-public interface VisitorAccepter
+public class RuntimeInvisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute
 {
     /**
-     * Gets the visitor information of the visitor accepter.
+     * Creates an uninitialized RuntimeInvisibleParameterAnnotationsAttribute.
      */
-    public Object getVisitorInfo();
+    public RuntimeInvisibleParameterAnnotationsAttribute()
+    {
+    }
 
 
-    /**
-     * Sets the visitor information of the visitor accepter.
-     */
-    public void setVisitorInfo(Object visitorInfo);
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, this);
+    }
 }
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java
deleted file mode 100644
index 006c1df..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeParameterAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/* $Id: RuntimeParameterAnnotationsAttrInfo.java,v 1.6.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.AttrInfo;
-
-import java.io.*;
-
-/**
- * Representation of a runtime parameter annotations attribute.
- *
- * @author Eric Lafortune
- */
-public abstract class RuntimeParameterAnnotationsAttrInfo extends AttrInfo
-{
-    private static final int CONSTANT_FIELD_SIZE1 = 1;
-    private static final int CONSTANT_FIELD_SIZE2 = 2;
-
-
-    public int            u2numberOfParameters;
-    //public int[]          u2numberOfAnnotations;
-    public Annotation[][] parameterAnnotations;
-
-
-    protected RuntimeParameterAnnotationsAttrInfo()
-    {
-    }
-
-
-    /**
-     * Applies the given visitor to all annotations.
-     */
-    public void annotationsAccept(ClassFile classFile, AnnotationVisitor annotationVisitor)
-    {
-        for (int parameterIndex = 0; parameterIndex < u2numberOfParameters; parameterIndex++)
-        {
-            Annotation[] annotations = parameterAnnotations[parameterIndex];
-            int u2numberOfAnnotations = annotations.length;
-
-            for (int i = 0; i < u2numberOfAnnotations; i++)
-            {
-                // We don't need double dispatching here, since there is only one
-                // type of Annotation.
-                //annotationVisitor.visitAnnotation(classFile, methodInfo, i, annotations[j]);
-                annotationVisitor.visitAnnotation(classFile, annotations[i]);
-            }
-        }
-    }
-
-
-    // Implementations for AttrInfo.
-
-    protected int getLength()
-    {
-        int length = CONSTANT_FIELD_SIZE1;
-
-        for (int parameterIndex = 0; parameterIndex < u2numberOfParameters; parameterIndex++)
-        {
-            Annotation[] annotations = parameterAnnotations[parameterIndex];
-            int u2numberOfAnnotations = annotations.length;
-
-            length += CONSTANT_FIELD_SIZE2;
-
-            for (int i = 0; i < u2numberOfAnnotations; i++)
-            {
-                length += annotations[i].getLength();
-            }
-        }
-
-        return length;
-    }
-
-    protected void readInfo(DataInput din, ClassFile classFile) throws IOException
-    {
-        u2numberOfParameters = din.readUnsignedByte();
-        parameterAnnotations = new Annotation[u2numberOfParameters][];
-
-        for (int parameterIndex = 0; parameterIndex < u2numberOfParameters; parameterIndex++)
-        {
-            int u2numberOfAnnotations = din.readUnsignedShort();
-
-            Annotation[] annotations = new Annotation[u2numberOfAnnotations];
-
-            for (int i = 0; i < u2numberOfAnnotations; i++)
-            {
-                annotations[i] = Annotation.create(din);
-            }
-
-            parameterAnnotations[parameterIndex] = annotations;
-        }
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeByte(u2numberOfParameters);
-
-        for (int parameterIndex = 0; parameterIndex < u2numberOfParameters; parameterIndex++)
-        {
-            Annotation[] annotations = parameterAnnotations[parameterIndex];
-            int u2numberOfAnnotations = annotations.length;
-
-            dout.writeShort(u2numberOfAnnotations);
-
-            for (int i = 0; i < u2numberOfAnnotations; i++)
-            {
-                annotations[i].write(dout);
-            }
-        }
-    }
-}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java
deleted file mode 100644
index 57da7d1..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* $Id: RuntimeVisibleAnnotationsAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.ClassFile;
-import proguard.classfile.attribute.AttrInfoVisitor;
-
-/**
- * Representation of a runtime visible annotations attribute.
- *
- * @author Eric Lafortune
- */
-public class RuntimeVisibleAnnotationsAttrInfo extends RuntimeAnnotationsAttrInfo
-{
-    public RuntimeVisibleAnnotationsAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitRuntimeVisibleAnnotationAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java
new file mode 100644
index 0000000..2c63647
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleAnnotationsAttribute.java
@@ -0,0 +1,59 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents a runtime visible annotations attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class RuntimeVisibleAnnotationsAttribute extends AnnotationsAttribute
+{
+    /**
+     * Creates an uninitialized RuntimeVisibleAnnotationsAttribute.
+     */
+    public RuntimeVisibleAnnotationsAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, this);
+    }
+
+
+    public void accept(Clazz clazz, Field field, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, field, this);
+    }
+
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, method, this);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java
deleted file mode 100644
index 7d81a3d..0000000
--- a/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttrInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/* $Id: RuntimeVisibleParameterAnnotationsAttrInfo.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.attribute.annotation;
-
-import proguard.classfile.ClassFile;
-import proguard.classfile.attribute.AttrInfoVisitor;
-
-/**
- * Representation of a runtime visible parameter annotations attribute.
- *
- * @author Eric Lafortune
- */
-public class RuntimeVisibleParameterAnnotationsAttrInfo extends RuntimeParameterAnnotationsAttrInfo
-{
-    public RuntimeVisibleParameterAnnotationsAttrInfo()
-    {
-    }
-
-
-    // Implementations for AttrInfo.
-
-    public void accept(ClassFile classFile, AttrInfoVisitor attrInfoVisitor)
-    {
-        attrInfoVisitor.visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, this);
-    }
-}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
similarity index 50%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
index d9a4a34..5ddcbc7 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/attribute/annotation/RuntimeVisibleParameterAnnotationsAttribute.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,30 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
+package proguard.classfile.attribute.annotation;
 
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 
 /**
- * Representation of a field from a class file.
+ * This Attribute represents a runtime visible parameter annotations attribute.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class RuntimeVisibleParameterAnnotationsAttribute extends ParameterAnnotationsAttribute
 {
+    /**
+     * Creates an uninitialized RuntimeVisibleParameterAnnotationsAttribute.
+     */
+    public RuntimeVisibleParameterAnnotationsAttribute()
+    {
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, this);
+    }
 }
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java
new file mode 100644
index 0000000..f63cae1
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/AllAnnotationVisitor.java
@@ -0,0 +1,100 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is 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.attribute.annotation.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor lets a given AnnotationVisitor visit all Annotation
+ * objects of the attributes it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllAnnotationVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private final AnnotationVisitor annotationVisitor;
+
+
+    public AllAnnotationVisitor(AnnotationVisitor annotationVisitor)
+    {
+        this.annotationVisitor = annotationVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, annotationVisitor);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, field, annotationVisitor);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, annotationVisitor);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, field, annotationVisitor);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Visit the annotations.
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, annotationVisitor);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java
new file mode 100644
index 0000000..626e185
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotatedClassVisitor.java
@@ -0,0 +1,62 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation.visitor;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.annotation.Annotation;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+
+/**
+ * This AnnotationVisitor delegates all visits to a given ClassVisitor.
+ * The latter visits the class of each visited annotation, although
+ * never twice in a row.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotatedClassVisitor
+extends      SimplifiedVisitor
+implements   AnnotationVisitor
+{
+    private final ClassVisitor classVisitor;
+
+    private Clazz lastVisitedClass;
+
+
+    public AnnotatedClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        if (!clazz.equals(lastVisitedClass))
+        {
+            clazz.accept(classVisitor);
+
+            lastVisitedClass = clazz;
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java
new file mode 100644
index 0000000..6bc8f7f
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationToMemberVisitor.java
@@ -0,0 +1,62 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.annotation.Annotation;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+
+/**
+ * This AnnotationVisitor delegates all visits to a given MemberVisitor.
+ * The latter visits the class member of each visited class member annotation
+ * or method parameter annotation, although never twice in a row.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationToMemberVisitor
+extends      SimplifiedVisitor
+implements   AnnotationVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+    private Member lastVisitedMember;
+
+
+    public AnnotationToMemberVisitor(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Member member, Annotation annotation)
+    {
+        if (!member.equals(lastVisitedMember))
+        {
+            member.accept(clazz, memberVisitor);
+
+            lastVisitedMember = member;
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java
new file mode 100644
index 0000000..6f0a9ae
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationTypeFilter.java
@@ -0,0 +1,102 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.annotation.Annotation;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.util.*;
+
+/**
+ * This <code>AnnotationVisitor</code> delegates its visits to another given
+ * <code>AnnotationVisitor</code>, but only when the visited annotation has
+ * a type that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationTypeFilter
+extends      SimplifiedVisitor
+implements   AnnotationVisitor
+{
+    private final StringMatcher     regularExpressionMatcher;
+    private final AnnotationVisitor annotationVisitor;
+
+
+    /**
+     * Creates a new ClassNameFilter.
+     * @param regularExpression the regular expression against which annotation
+     *                          type names will be matched.
+     * @param annotationVisitor the <code>annotationVisitor</code> to which
+     *                          visits will be delegated.
+     */
+    public AnnotationTypeFilter(String            regularExpression,
+                                AnnotationVisitor annotationVisitor)
+    {
+        this.regularExpressionMatcher = new ListParser(new ClassNameParser()).parse(regularExpression);
+        this.annotationVisitor        = annotationVisitor;
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        if (accepted(annotation.getType(clazz)))
+        {
+            annotationVisitor.visitAnnotation(clazz, annotation);
+        }
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Field field, Annotation annotation)
+    {
+        if (accepted(annotation.getType(clazz)))
+        {
+            annotationVisitor.visitAnnotation(clazz, field, annotation);
+        }
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Method method, Annotation annotation)
+    {
+        if (accepted(annotation.getType(clazz)))
+        {
+            annotationVisitor.visitAnnotation(clazz, method, annotation);
+        }
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation)
+    {
+        if (accepted(annotation.getType(clazz)))
+        {
+            annotationVisitor.visitAnnotation(clazz, method, parameterIndex, annotation);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/attribute/annotation/AnnotationVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
similarity index 63%
rename from src/proguard/classfile/attribute/annotation/AnnotationVisitor.java
rename to src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
index 3113937..5634b49 100644
--- a/src/proguard/classfile/attribute/annotation/AnnotationVisitor.java
+++ b/src/proguard/classfile/attribute/annotation/visitor/AnnotationVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: AnnotationVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,11 +18,10 @@
  * 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.attribute.annotation;
+package proguard.classfile.attribute.annotation.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-
+import proguard.classfile.attribute.annotation.Annotation;
 
 /**
  * This interface specifies the methods for a visitor of
@@ -34,7 +33,8 @@ import proguard.classfile.attribute.*;
  */
 public interface AnnotationVisitor
 {
-    public void visitAnnotation(ClassFile classFile, Annotation annotation);
-
-//    public void visitAnnotation(ClassFile classFile, MethodInfo methodInfo, int parameterIndex, Annotation annotation);
+    public void visitAnnotation(Clazz clazz,                                    Annotation annotation);
+    public void visitAnnotation(Clazz clazz, Field  field,                      Annotation annotation);
+    public void visitAnnotation(Clazz clazz, Method method,                     Annotation annotation);
+    public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation);
 }
diff --git a/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java
new file mode 100644
index 0000000..fe31534
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/ElementValueVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.annotation.visitor;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.annotation.*;
+
+/**
+ * This interface specifies the methods for a visitor of <code>ElementValue</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface ElementValueVisitor
+{
+    public void visitConstantElementValue(    Clazz clazz,                Annotation annotation, ConstantElementValue     constantElementValue);
+    public void visitEnumConstantElementValue(Clazz clazz,                Annotation annotation, EnumConstantElementValue enumConstantElementValue);
+    public void visitClassElementValue(       Clazz clazz,                Annotation annotation, ClassElementValue        classElementValue);
+    public void visitAnnotationElementValue(  Clazz clazz,                Annotation annotation, AnnotationElementValue   annotationElementValue);
+    public void visitArrayElementValue(       Clazz clazz,                Annotation annotation, ArrayElementValue        arrayElementValue);
+
+//    public void visitConstantElementValue(    Clazz clazz, Field  field,  Annotation annotation, ConstantElementValue     constantElementValue);
+//    public void visitEnumConstantElementValue(Clazz clazz, Field  field,  Annotation annotation, EnumConstantElementValue enumConstantElementValue);
+//    public void visitClassElementValue(       Clazz clazz, Field  field,  Annotation annotation, ClassElementValue        classElementValue);
+//    public void visitAnnotationElementValue(  Clazz clazz, Field  field,  Annotation annotation, AnnotationElementValue   annotationElementValue);
+//    public void visitArrayElementValue(       Clazz clazz, Field  field,  Annotation annotation, ArrayElementValue        arrayElementValue);
+//
+//    public void visitConstantElementValue(    Clazz clazz, Method method, Annotation annotation, ConstantElementValue     constantElementValue);
+//    public void visitEnumConstantElementValue(Clazz clazz, Method method, Annotation annotation, EnumConstantElementValue enumConstantElementValue);
+//    public void visitClassElementValue(       Clazz clazz, Method method, Annotation annotation, ClassElementValue        classElementValue);
+//    public void visitAnnotationElementValue(  Clazz clazz, Method method, Annotation annotation, AnnotationElementValue   annotationElementValue);
+//    public void visitArrayElementValue(       Clazz clazz, Method method, Annotation annotation, ArrayElementValue        arrayElementValue);
+}
diff --git a/src/proguard/classfile/attribute/annotation/visitor/package.html b/src/proguard/classfile/attribute/annotation/visitor/package.html
new file mode 100644
index 0000000..10d0648
--- /dev/null
+++ b/src/proguard/classfile/attribute/annotation/visitor/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors for annotation attributes and their components.
+</body>
diff --git a/src/proguard/classfile/attribute/preverification/DoubleType.java b/src/proguard/classfile/attribute/preverification/DoubleType.java
new file mode 100644
index 0000000..5df3fec
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/DoubleType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Double type.
+ *
+ * @author Eric Lafortune
+ */
+public class DoubleType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return DOUBLE_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitDoubleType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackDoubleType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesDoubleType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "d";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/FloatType.java b/src/proguard/classfile/attribute/preverification/FloatType.java
new file mode 100644
index 0000000..8d985f9
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/FloatType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Float type.
+ *
+ * @author Eric Lafortune
+ */
+public class FloatType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return FLOAT_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitFloatType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackFloatType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesFloatType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "f";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/FullFrame.java b/src/proguard/classfile/attribute/preverification/FullFrame.java
new file mode 100644
index 0000000..41763b2
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/FullFrame.java
@@ -0,0 +1,202 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.*;
+
+/**
+ * This StackMapFrame represents a "full frame".
+ *
+ * @author Eric Lafortune
+ */
+public class FullFrame extends StackMapFrame
+{
+    public int                variablesCount;
+    public VerificationType[] variables;
+    public int                stackCount;
+    public VerificationType[] stack;
+
+
+    /**
+     * Creates an uninitialized FullFrame.
+     */
+    public FullFrame()
+    {
+    }
+
+
+    /**
+     * Creates a FullFrame with the given variables and stack.
+     */
+    public FullFrame(int                offsetDelta,
+                     VerificationType[] variables,
+                     VerificationType[] stack)
+    {
+        this(offsetDelta,
+             variables.length,
+             variables,
+             stack.length,
+             stack);
+    }
+
+
+    /**
+     * Creates a FullFrame with the given variables and stack.
+     */
+    public FullFrame(int                offsetDelta,
+                     int                variablesCount,
+                     VerificationType[] variables,
+                     int                stackCount,
+                     VerificationType[] stack)
+    {
+        this.u2offsetDelta  = offsetDelta;
+        this.variablesCount = variablesCount;
+        this.variables      = variables;
+        this.stackCount     = stackCount;
+        this.stack          = stack;
+    }
+
+
+    /**
+     * Applies the given verification type visitor to all variables.
+     */
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        for (int index = 0; index < variablesCount; index++)
+        {
+            variables[index].variablesAccept(clazz, method, codeAttribute, offset, index, verificationTypeVisitor);
+        }
+    }
+
+
+    /**
+     * Applies the given verification type visitor to all stack.
+     */
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        for (int index = 0; index < stackCount; index++)
+        {
+            stack[index].stackAccept(clazz, method, codeAttribute, offset, index, verificationTypeVisitor);
+        }
+    }
+
+
+    // Implementations for StackMapFrame.
+
+    public int getTag()
+    {
+        return FULL_FRAME;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        stackMapFrameVisitor.visitFullFrame(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        FullFrame other = (FullFrame)object;
+
+        if (this.u2offsetDelta  != other.u2offsetDelta  ||
+            this.variablesCount != other.variablesCount ||
+            this.stackCount     != other.stackCount)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < variablesCount; index++)
+        {
+            VerificationType thisType  = this.variables[index];
+            VerificationType otherType = other.variables[index];
+
+            if (!thisType.equals(otherType))
+            {
+                return false;
+            }
+        }
+
+        for (int index = 0; index < stackCount; index++)
+        {
+            VerificationType thisType  = this.stack[index];
+            VerificationType otherType = other.stack[index];
+
+            if (!thisType.equals(otherType))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public int hashCode()
+    {
+        int hashCode = super.hashCode();
+
+        for (int index = 0; index < variablesCount; index++)
+        {
+            hashCode ^= variables[index].hashCode();
+        }
+
+        for (int index = 0; index < stackCount; index++)
+        {
+            hashCode ^= stack[index].hashCode();
+        }
+
+        return hashCode;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer(super.toString()).append("Var: ");
+
+        for (int index = 0; index < variablesCount; index++)
+        {
+            buffer = buffer.append('[')
+                           .append(variables[index].toString())
+                           .append(']');
+        }
+
+        buffer.append(", Stack: ");
+        
+        for (int index = 0; index < stackCount; index++)
+        {
+            buffer = buffer.append('[')
+                           .append(stack[index].toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/IntegerType.java b/src/proguard/classfile/attribute/preverification/IntegerType.java
new file mode 100644
index 0000000..0526be4
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/IntegerType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Integer type.
+ *
+ * @author Eric Lafortune
+ */
+public class IntegerType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return INTEGER_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitIntegerType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackIntegerType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesIntegerType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "i";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/LessZeroFrame.java b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java
new file mode 100644
index 0000000..cafd31a
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/LessZeroFrame.java
@@ -0,0 +1,103 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor;
+
+/**
+ * This StackMapFrame represents an "chop frame".
+ *
+ * @author Eric Lafortune
+ */
+public class LessZeroFrame extends StackMapFrame
+{
+    public int choppedVariablesCount;
+
+
+    /**
+     * Creates an uninitialized LessZeroFrame.
+     */
+    public LessZeroFrame()
+    {
+    }
+
+
+    /**
+     * Creates a LessZeroFrame with the given tag.
+     */
+    public LessZeroFrame(int tag)
+    {
+        choppedVariablesCount = LESS_ZERO_FRAME + 3 - tag;
+    }
+
+
+    /**
+     * Creates a LessZeroFrame with the given number of chopped variables.
+     */
+    public LessZeroFrame(byte choppedVariablesCount)
+    {
+        this.choppedVariablesCount = (int)choppedVariablesCount;
+    }
+
+
+    // Implementations for StackMapFrame.
+
+    public int getTag()
+    {
+        return LESS_ZERO_FRAME + 3 - choppedVariablesCount;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        stackMapFrameVisitor.visitLessZeroFrame(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        LessZeroFrame other = (LessZeroFrame)object;
+
+        return this.u2offsetDelta == other.u2offsetDelta &&
+               this.choppedVariablesCount != other.choppedVariablesCount;
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^ choppedVariablesCount;
+    }
+
+
+    public String toString()
+    {
+        return super.toString()+"Var: (chopped "+choppedVariablesCount+"), Stack: (empty)";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/LongType.java b/src/proguard/classfile/attribute/preverification/LongType.java
new file mode 100644
index 0000000..7498dbb
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/LongType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Long type.
+ *
+ * @author Eric Lafortune
+ */
+public class LongType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return LONG_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitLongType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackLongType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesLongType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "l";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java
new file mode 100644
index 0000000..2f365bc
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/MoreZeroFrame.java
@@ -0,0 +1,161 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.*;
+
+/**
+ * This StackMapFrame represents an "append frame".
+ *
+ * @author Eric Lafortune
+ */
+public class MoreZeroFrame extends StackMapFrame
+{
+    public int                additionalVariablesCount;
+    public VerificationType[] additionalVariables;
+
+
+    /**
+     * Creates an uninitialized MoreZeroFrame.
+     */
+    public MoreZeroFrame()
+    {
+    }
+
+
+    /**
+     * Creates a MoreZeroFrame with the given tag.
+     */
+    public MoreZeroFrame(int tag)
+    {
+        additionalVariablesCount = tag + 1 - MORE_ZERO_FRAME;
+    }
+
+
+    /**
+     * Creates a MoreZeroFrame with the given additional variables.
+     */
+    public MoreZeroFrame(VerificationType[] additionalVariables)
+    {
+        this(additionalVariables.length, additionalVariables);
+    }
+
+
+    /**
+     * Creates a MoreZeroFrame with the given additional variables.
+     */
+    public MoreZeroFrame(int                additionalVariablesCount,
+                         VerificationType[] additionalVariables)
+    {
+        this.additionalVariablesCount = additionalVariablesCount;
+        this.additionalVariables      = additionalVariables;
+    }
+
+
+    /**
+     * Applies the given verification type visitor to all variables.
+     */
+    public void additionalVariablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        for (int index = 0; index < additionalVariablesCount; index++)
+        {
+            additionalVariables[index].accept(clazz, method, codeAttribute, offset, verificationTypeVisitor);
+        }
+    }
+
+
+    // Implementations for StackMapFrame.
+
+    public int getTag()
+    {
+        return MORE_ZERO_FRAME + additionalVariablesCount - 1;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        stackMapFrameVisitor.visitMoreZeroFrame(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        MoreZeroFrame other = (MoreZeroFrame)object;
+
+        if (this.u2offsetDelta            != other.u2offsetDelta ||
+            this.additionalVariablesCount != other.additionalVariablesCount)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < additionalVariablesCount; index++)
+        {
+            VerificationType thisType  = this.additionalVariables[index];
+            VerificationType otherType = other.additionalVariables[index];
+
+            if (!thisType.equals(otherType))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    public int hashCode()
+    {
+        int hashCode = super.hashCode();
+
+        for (int index = 0; index < additionalVariablesCount; index++)
+        {
+            hashCode ^= additionalVariables[index].hashCode();
+        }
+
+        return hashCode;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer(super.toString()).append("Var: ...");
+
+        for (int index = 0; index < additionalVariablesCount; index++)
+        {
+            buffer = buffer.append('[')
+                           .append(additionalVariables[index].toString())
+                           .append(']');
+        }
+
+        buffer.append(", Stack: (empty)");
+        
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/NullType.java b/src/proguard/classfile/attribute/preverification/NullType.java
new file mode 100644
index 0000000..add3094
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/NullType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Null type.
+ *
+ * @author Eric Lafortune
+ */
+public class NullType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return NULL_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitNullType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackNullType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesNullType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "n";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/ObjectType.java b/src/proguard/classfile/attribute/preverification/ObjectType.java
new file mode 100644
index 0000000..3762465
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/ObjectType.java
@@ -0,0 +1,107 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents an Object type.
+ *
+ * @author Eric Lafortune
+ */
+public class ObjectType extends VerificationType
+{
+    public int u2classIndex;
+
+
+
+    /**
+     * Creates an uninitialized ObjectType.
+     */
+    public ObjectType()
+    {
+    }
+
+
+    /**
+     * Creates an ObjectType that points to the given class constant.
+     */
+    public ObjectType(int u2classIndex)
+    {
+        this.u2classIndex = u2classIndex;
+    }
+
+
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return OBJECT_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitObjectType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackObjectType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesObjectType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        ObjectType other = (ObjectType)object;
+
+        return this.u2classIndex == other.u2classIndex;
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^
+               u2classIndex;
+    }
+
+
+    public String toString()
+    {
+        return "a:" + u2classIndex;
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/SameOneFrame.java b/src/proguard/classfile/attribute/preverification/SameOneFrame.java
new file mode 100644
index 0000000..6e329be
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/SameOneFrame.java
@@ -0,0 +1,115 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.*;
+
+/**
+ * This StackMapFrame represents a "same locals 1 stack item frame" or a
+ * "same locals 1 stack item frame extended".
+ *
+ * @author Eric Lafortune
+ */
+public class SameOneFrame extends StackMapFrame
+{
+    public VerificationType stackItem;
+
+
+    /**
+     * Creates an uninitialized SameOneFrame.
+     */
+    public SameOneFrame()
+    {
+    }
+
+
+    /**
+     * Creates a SameOneFrame with the given tag.
+     */
+    public SameOneFrame(int tag)
+    {
+        u2offsetDelta = tag - SAME_ONE_FRAME;
+    }
+
+
+    /**
+     * Creates a SameOneFrame with the given stack verification type.
+     */
+    public SameOneFrame(VerificationType stackItem)
+    {
+        this.stackItem = stackItem;
+    }
+
+
+    /**
+     * Applies the given verification type visitor to the stack item.
+     */
+    public void stackItemAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        stackItem.accept(clazz, method, codeAttribute, offset, verificationTypeVisitor);
+    }
+
+
+    // Implementations for StackMapFrame.
+
+    public int getTag()
+    {
+        return u2offsetDelta < 64 ?
+            SAME_ONE_FRAME + u2offsetDelta :
+            SAME_ONE_FRAME_EXTENDED;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        stackMapFrameVisitor.visitSameOneFrame(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        SameOneFrame other = (SameOneFrame)object;
+
+        return this.u2offsetDelta == other.u2offsetDelta &&
+               this.stackItem.equals(other.stackItem);
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^ stackItem.hashCode();
+    }
+
+
+    public String toString()
+    {
+        return super.toString()+"Var: ..., Stack: ["+stackItem.toString()+"]";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/SameZeroFrame.java b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java
new file mode 100644
index 0000000..04d8b79
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/SameZeroFrame.java
@@ -0,0 +1,74 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor;
+
+/**
+ * This StackMapFrame represents a "same frame" or a "same frame extended".
+ *
+ * @author Eric Lafortune
+ * @noinspection PointlessArithmeticExpression
+ */
+public class SameZeroFrame extends StackMapFrame
+{
+    /**
+     * Creates an uninitialized SameZeroFrame.
+     */
+    public SameZeroFrame()
+    {
+    }
+
+
+    /**
+     * Creates a SameZeroFrame with the given tag.
+     */
+    public SameZeroFrame(int tag)
+    {
+        u2offsetDelta = tag - SAME_ZERO_FRAME;
+    }
+
+
+    // Implementations for StackMapFrame.
+
+    public int getTag()
+    {
+        return u2offsetDelta < 64 ?
+            SAME_ZERO_FRAME + u2offsetDelta :
+            SAME_ZERO_FRAME_EXTENDED;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        stackMapFrameVisitor.visitSameZeroFrame(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return super.toString()+"Var: ..., Stack: (empty)";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/StackMapAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java
new file mode 100644
index 0000000..805d9f6
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/StackMapAttribute.java
@@ -0,0 +1,91 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents an exceptions attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class StackMapAttribute extends Attribute
+{
+    public int         u2stackMapFramesCount;
+    public FullFrame[] stackMapFrames;
+
+
+    /**
+     * Creates an uninitialized ExceptionsAttribute.
+     */
+    public StackMapAttribute()
+    {
+    }
+
+
+    /**
+     * Creates a StackMapTableAttribute with the given stack map frames.
+     */
+    public StackMapAttribute(FullFrame[] stackMapFrames)
+    {
+        this(stackMapFrames.length, stackMapFrames);
+    }
+
+
+    /**
+     * Creates a StackMapTableAttribute with the given stack map frames.
+     */
+    public StackMapAttribute(int         stackMapFramesCount,
+                             FullFrame[] stackMapFrames)
+    {
+        this.u2stackMapFramesCount = stackMapFramesCount;
+        this.stackMapFrames        = stackMapFrames;
+    }
+
+
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitStackMapAttribute(clazz, method, codeAttribute, this);
+    }
+
+
+    /**
+     * Applies the given stack map frame visitor to all stack map frames.
+     */
+    public void stackMapFramesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        for (int index = 0; index < u2stackMapFramesCount; index++)
+        {
+            FullFrame stackMapFrame = stackMapFrames[index];
+
+            // We don't need double dispatching here, since there is only one
+            // type of StackMapFrame.
+            stackMapFrameVisitor.visitFullFrame(clazz, method, codeAttribute, stackMapFrame.getOffsetDelta(), stackMapFrame);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/StackMapFrame.java b/src/proguard/classfile/attribute/preverification/StackMapFrame.java
new file mode 100644
index 0000000..2f3fad3
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/StackMapFrame.java
@@ -0,0 +1,117 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor;
+
+/**
+ * This abstract class represents a stack map frame. Specific types
+ * of entries are subclassed from it.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class StackMapFrame implements VisitorAccepter
+{
+    public static final int SAME_ZERO_FRAME          =   0;
+    public static final int SAME_ONE_FRAME           =  64;
+    public static final int SAME_ONE_FRAME_EXTENDED  = 247;
+    public static final int LESS_ZERO_FRAME          = 248;
+    public static final int SAME_ZERO_FRAME_EXTENDED = 251;
+    public static final int MORE_ZERO_FRAME          = 252;
+    public static final int FULL_FRAME               = 255;
+
+
+    public int u2offsetDelta;
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+
+    /**
+     * Returns the bytecode offset delta relative to the previous stack map
+     * frame.
+     */
+    public int getOffsetDelta()
+    {
+        return u2offsetDelta;
+    }
+
+
+    // Abstract methods to be implemented by extensions.
+
+    /**
+     * Returns the stack map frame tag that specifies the entry type.
+     */
+    public abstract int getTag();
+
+
+    /**
+     * Accepts the given visitor.
+     */
+    public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrameVisitor stackMapFrameVisitor);
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        StackMapFrame other = (StackMapFrame)object;
+
+        return this.u2offsetDelta == other.u2offsetDelta;
+    }
+
+
+    public int hashCode()
+    {
+        return getClass().hashCode() ^
+               u2offsetDelta;
+    }
+
+
+    public String toString()
+    {
+        return "[" + u2offsetDelta + "] ";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java
new file mode 100644
index 0000000..658f155
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/StackMapTableAttribute.java
@@ -0,0 +1,93 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.preverification.visitor.StackMapFrameVisitor;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This Attribute represents a stack map table attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class StackMapTableAttribute extends Attribute
+{
+    public int             u2stackMapFramesCount;
+    public StackMapFrame[] stackMapFrames;
+
+
+    /**
+     * Creates an uninitialized StackMapTableAttribute.
+     */
+    public StackMapTableAttribute()
+    {
+    }
+
+
+    /**
+     * Creates a StackMapTableAttribute with the given stack map frames.
+     */
+    public StackMapTableAttribute(StackMapFrame[] stackMapFrames)
+    {
+        this(stackMapFrames.length, stackMapFrames);
+    }
+
+
+    /**
+     * Creates a StackMapTableAttribute with the given stack map frames.
+     */
+    public StackMapTableAttribute(int             stackMapFramesCount,
+                                  StackMapFrame[] stackMapFrames)
+    {
+        this.u2stackMapFramesCount = stackMapFramesCount;
+        this.stackMapFrames        = stackMapFrames;
+    }
+
+
+    // Implementations for Attribute.
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, AttributeVisitor attributeVisitor)
+    {
+        attributeVisitor.visitStackMapTableAttribute(clazz, method, codeAttribute, this);
+    }
+
+
+    /**
+     * Applies the given stack map frame visitor to all stack map frames.
+     */
+    public void stackMapFramesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapFrameVisitor stackMapFrameVisitor)
+    {
+        int offset = 0;
+
+        for (int index = 0; index < u2stackMapFramesCount; index++)
+        {
+            StackMapFrame stackMapFrame = stackMapFrames[index];
+
+            // Note that the byte code offset is computed differently for the
+            // first stack map frame.
+            offset += stackMapFrame.getOffsetDelta() + (index == 0 ? 0 : 1);
+
+            stackMapFrame.accept(clazz, method, codeAttribute, offset, stackMapFrameVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/TopType.java b/src/proguard/classfile/attribute/preverification/TopType.java
new file mode 100644
index 0000000..ece00ed
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/TopType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Top type.
+ *
+ * @author Eric Lafortune
+ */
+public class TopType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return TOP_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitTopType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackTopType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesTopType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "T";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/UninitializedThisType.java b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java
new file mode 100644
index 0000000..7b5639e
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/UninitializedThisType.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a UninitializedThis type.
+ *
+ * @author Eric Lafortune
+ */
+public class UninitializedThisType extends VerificationType
+{
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return UNINITIALIZED_THIS_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitUninitializedThisType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackUninitializedThisType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesUninitializedThisType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return "u:this";
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/UninitializedType.java b/src/proguard/classfile/attribute/preverification/UninitializedType.java
new file mode 100644
index 0000000..9e406d6
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/UninitializedType.java
@@ -0,0 +1,106 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This VerificationType represents a Uninitialized type.
+ *
+ * @author Eric Lafortune
+ */
+public class UninitializedType extends VerificationType
+{
+    public int u2newInstructionOffset;
+
+
+    /**
+     * Creates an uninitialized UninitializedType.
+     */
+    public UninitializedType()
+    {
+    }
+
+
+    /**
+     * Creates an UninitializedType pointing to the given 'new' instruction.
+     */
+    public UninitializedType(int u2newInstructionOffset)
+    {
+        this.u2newInstructionOffset = u2newInstructionOffset;
+    }
+
+
+    // Implementations for VerificationType.
+
+    public int getTag()
+    {
+        return UNINITIALIZED_TYPE;
+    }
+
+
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitUninitializedType(clazz, method, codeAttribute, instructionOffset, this);
+    }
+
+
+    public void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitStackUninitializedType(clazz, method, codeAttribute, instructionOffset, stackIndex, this);
+    }
+
+
+    public void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor)
+    {
+        verificationTypeVisitor.visitVariablesUninitializedType(clazz, method, codeAttribute, instructionOffset, variableIndex, this);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (!super.equals(object))
+        {
+            return false;
+        }
+
+        UninitializedType other = (UninitializedType)object;
+
+        return this.u2newInstructionOffset == other.u2newInstructionOffset;
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^
+               u2newInstructionOffset;
+    }
+
+
+    public String toString()
+    {
+        return "u:" + u2newInstructionOffset;
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/VerificationType.java b/src/proguard/classfile/attribute/preverification/VerificationType.java
new file mode 100644
index 0000000..c6d546e
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/VerificationType.java
@@ -0,0 +1,103 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.visitor.VerificationTypeVisitor;
+
+/**
+ * This abstract class represents a verification type of a local variable or
+ * a stack element. Specific verification types are subclassed from it.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class VerificationType implements VisitorAccepter
+{
+    public static final int TOP_TYPE                = 0;
+    public static final int INTEGER_TYPE            = 1;
+    public static final int FLOAT_TYPE              = 2;
+    public static final int DOUBLE_TYPE             = 3;
+    public static final int LONG_TYPE               = 4;
+    public static final int NULL_TYPE               = 5;
+    public static final int UNINITIALIZED_THIS_TYPE = 6;
+    public static final int OBJECT_TYPE             = 7;
+    public static final int UNINITIALIZED_TYPE      = 8;
+
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    /**
+     * Returns the tag of the verification type.
+     */
+    public abstract int getTag();
+
+
+    /**
+     * Accepts the given visitor in the context of a method's code, either on
+     * a stack or as a variable.
+     */
+    public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, VerificationTypeVisitor verificationTypeVisitor);
+
+
+    /**
+     * Accepts the given visitor in the context of a stack in a method's code .
+     */
+    public abstract void stackAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int stackIndex, VerificationTypeVisitor verificationTypeVisitor);
+
+
+    /**
+     * Accepts the given visitor in the context of a variable in a method's code.
+     */
+    public abstract void variablesAccept(Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset, int variableIndex, VerificationTypeVisitor verificationTypeVisitor);
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        return object != null &&
+               this.getClass() == object.getClass();
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode();
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java
new file mode 100644
index 0000000..200ffb7
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/VerificationTypeFactory.java
@@ -0,0 +1,112 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification;
+
+/**
+ * This class provides methods to create and reuse IntegerType objects.
+ *
+ * @author Eric Lafortune
+ */
+public class VerificationTypeFactory
+{
+    // Shared copies of Type objects, to avoid creating a lot of objects.
+    static final IntegerType           INTEGER_TYPE            = new IntegerType();
+    static final LongType              LONG_TYPE               = new LongType();
+    static final FloatType             FLOAT_TYPE              = new FloatType();
+    static final DoubleType            DOUBLE_TYPE             = new DoubleType();
+    static final TopType               TOP_TYPE                = new TopType();
+    static final NullType              NULL_TYPE               = new NullType();
+    static final UninitializedThisType UNINITIALIZED_THIS_TYPE = new UninitializedThisType();
+
+
+    /**
+     * Creates a new IntegerType.
+     */
+    public static IntegerType createIntegerType()
+    {
+        return INTEGER_TYPE;
+    }
+
+    /**
+     * Creates a new LongType.
+     */
+    public static LongType createLongType()
+    {
+        return LONG_TYPE;
+    }
+
+    /**
+     * Creates a new FloatType.
+     */
+    public static FloatType createFloatType()
+    {
+        return FLOAT_TYPE;
+    }
+
+    /**
+     * Creates a new DoubleType.
+     */
+    public static DoubleType createDoubleType()
+    {
+        return DOUBLE_TYPE;
+    }
+
+    /**
+     * Creates a new TopType.
+     */
+    public static TopType createTopType()
+    {
+        return TOP_TYPE;
+    }
+
+    /**
+     * Creates a new NullType.
+     */
+    public static NullType createNullType()
+    {
+        return NULL_TYPE;
+    }
+
+    /**
+     * Creates a new UninitializedThisType.
+     */
+    public static UninitializedThisType createUninitializedThisType()
+    {
+        return UNINITIALIZED_THIS_TYPE;
+    }
+
+    /**
+     * Creates a new UninitializedType for an instance that was created at
+     * the given offset.
+     */
+    public static UninitializedType createUninitializedType(int newInstructionOffset)
+    {
+        return new UninitializedType(newInstructionOffset);
+    }
+
+    /**
+     * Creates a new ObjectType of the given type.
+     */
+    public static ObjectType createObjectType(int classIndex)
+    {
+        return new ObjectType(classIndex);
+    }
+}
diff --git a/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java
new file mode 100644
index 0000000..464fa0c
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/visitor/StackMapFrameVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.*;
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>StackMapFrame</code> objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface StackMapFrameVisitor
+{
+    public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame);
+    public void visitSameOneFrame( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame  sameOneFrame);
+    public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame);
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame);
+    public void visitFullFrame(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame     fullFrame);
+}
diff --git a/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java
new file mode 100644
index 0000000..1d1f54f
--- /dev/null
+++ b/src/proguard/classfile/attribute/preverification/visitor/VerificationTypeVisitor.java
@@ -0,0 +1,65 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.preverification.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.preverification.*;
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>VerificationType</code> objects. There a methods for stack entries
+ * and methods for variable entries.
+ *
+ * @author Eric Lafortune
+ */
+public interface VerificationTypeVisitor
+{
+    public void visitIntegerType(          Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType           integerType);
+    public void visitFloatType(            Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType             floatType);
+    public void visitLongType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType              longType);
+    public void visitDoubleType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType            doubleType);
+    public void visitTopType(              Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType               topType);
+    public void visitObjectType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType            objectType);
+    public void visitNullType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType              nullType);
+    public void visitUninitializedType(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType     uninitializedType);
+    public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType);
+
+    public void visitStackIntegerType(          Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType           integerType);
+    public void visitStackFloatType(            Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType             floatType);
+    public void visitStackLongType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType              longType);
+    public void visitStackDoubleType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType            doubleType);
+    public void visitStackTopType(              Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType               topType);
+    public void visitStackObjectType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType            objectType);
+    public void visitStackNullType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType              nullType);
+    public void visitStackUninitializedType(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType     uninitializedType);
+    public void visitStackUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType);
+
+    public void visitVariablesIntegerType(          Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType           integerType);
+    public void visitVariablesFloatType(            Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType             floatType);
+    public void visitVariablesLongType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType              longType);
+    public void visitVariablesDoubleType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType            doubleType);
+    public void visitVariablesTopType(              Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType               topType);
+    public void visitVariablesObjectType(           Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType            objectType);
+    public void visitVariablesNullType(             Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType              nullType);
+    public void visitVariablesUninitializedType(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType     uninitializedType);
+    public void visitVariablesUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType);
+}
diff --git a/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java
new file mode 100644
index 0000000..33de5a1
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/AllAttributeVisitor.java
@@ -0,0 +1,117 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is 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.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassVisitor, MemberVisitor, and AttributeVisitor lets a given
+ * AttributeVisitor visit all Attribute objects of the program classes,
+ * program class members, or code attributes, respectively, that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllAttributeVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             AttributeVisitor
+{
+    private final boolean          deep;
+    private final AttributeVisitor attributeVisitor;
+
+
+    /**
+     * Creates a new shallow AllAttributeVisitor.
+     * @param attributeVisitor the AttributeVisitor to which visits will be
+     *                         delegated.
+     */
+    public AllAttributeVisitor(AttributeVisitor attributeVisitor)
+    {
+        this(false, attributeVisitor);
+    }
+
+
+    /**
+     * Creates a new optionally deep AllAttributeVisitor.
+     * @param deep             specifies whether the attributes contained
+     *                         further down the class structure should be
+     *                         visited too.
+     * @param attributeVisitor the AttributeVisitor to which visits will be
+     *                         delegated.
+     */
+    public AllAttributeVisitor(boolean          deep,
+                               AttributeVisitor attributeVisitor)
+    {
+        this.deep             = deep;
+        this.attributeVisitor = attributeVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        programClass.attributesAccept(attributeVisitor);
+
+        // Visit the attributes further down the class structure, if required.
+        if (deep)
+        {
+            programClass.fieldsAccept(this);
+            programClass.methodsAccept(this);
+            programClass.attributesAccept(this);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass) {}
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        programMember.attributesAccept(programClass, attributeVisitor);
+
+        // Visit the attributes further down the member structure, if required.
+        if (deep)
+        {
+            programMember.attributesAccept(programClass, this);
+        }
+    }
+
+
+    public void visitLibraryMember(LibraryClass programClass, LibraryMember programMember) {}
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        codeAttribute.attributesAccept(clazz, method, attributeVisitor);
+    }
+}
diff --git a/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java
new file mode 100644
index 0000000..fa747ea
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/AttributeNameFilter.java
@@ -0,0 +1,345 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.util.StringMatcher;
+
+/**
+ * This AttributeVisitor delegates its visits another AttributeVisitor, but
+ * only when the visited attribute has a name that passes a given string
+ * matcher.
+ *
+ * @author Eric Lafortune
+ */
+public class AttributeNameFilter
+implements   AttributeVisitor
+{
+    private final StringMatcher    stringMatcher;
+    private final AttributeVisitor attributeVisitor;
+
+
+    /**
+     * Creates a new AttributeNameFilter.
+     * @param stringMatcher    the string matcher that will check the attribute
+     *                         names.
+     * @param attributeVisitor the <code>AttributeVisitor</code> to which
+     *                         visits will be delegated.
+     */
+    public AttributeNameFilter(StringMatcher    stringMatcher,
+                               AttributeVisitor attributeVisitor)
+    {
+        this.stringMatcher    = stringMatcher;
+        this.attributeVisitor = attributeVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        if (accepted(clazz, unknownAttribute))
+        {
+            unknownAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        if (accepted(clazz, sourceFileAttribute))
+        {
+            sourceFileAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        if (accepted(clazz, sourceDirAttribute))
+        {
+            sourceDirAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        if (accepted(clazz, innerClassesAttribute))
+        {
+            innerClassesAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        if (accepted(clazz, enclosingMethodAttribute))
+        {
+            enclosingMethodAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (accepted(clazz, deprecatedAttribute))
+        {
+            deprecatedAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (accepted(clazz, deprecatedAttribute))
+        {
+            deprecatedAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (accepted(clazz, deprecatedAttribute))
+        {
+            deprecatedAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        if (accepted(clazz, syntheticAttribute))
+        {
+            syntheticAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
+    {
+        if (accepted(clazz, syntheticAttribute))
+        {
+            syntheticAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
+    {
+        if (accepted(clazz, syntheticAttribute))
+        {
+            syntheticAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        if (accepted(clazz, signatureAttribute))
+        {
+            signatureAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute)
+    {
+        if (accepted(clazz, signatureAttribute))
+        {
+            signatureAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+    {
+        if (accepted(clazz, signatureAttribute))
+        {
+            signatureAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        if (accepted(clazz, constantValueAttribute))
+        {
+            constantValueAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        if (accepted(clazz, exceptionsAttribute))
+        {
+            exceptionsAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (accepted(clazz, codeAttribute))
+        {
+            codeAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        if (accepted(clazz, stackMapAttribute))
+        {
+            stackMapAttribute.accept(clazz, method, codeAttribute, attributeVisitor);
+        }
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        if (accepted(clazz, stackMapTableAttribute))
+        {
+            stackMapTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor);
+        }
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        if (accepted(clazz, lineNumberTableAttribute))
+        {
+            lineNumberTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor);
+        }
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        if (accepted(clazz, localVariableTableAttribute))
+        {
+            localVariableTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor);
+        }
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        if (accepted(clazz, localVariableTypeTableAttribute))
+        {
+            localVariableTypeTableAttribute.accept(clazz, method, codeAttribute, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeVisibleAnnotationsAttribute))
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeVisibleAnnotationsAttribute))
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeVisibleAnnotationsAttribute))
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeInvisibleAnnotationsAttribute))
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeInvisibleAnnotationsAttribute))
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, field, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeInvisibleAnnotationsAttribute))
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeVisibleParameterAnnotationsAttribute))
+        {
+            runtimeVisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        if (accepted(clazz, runtimeInvisibleParameterAnnotationsAttribute))
+        {
+            runtimeInvisibleParameterAnnotationsAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        if (accepted(clazz, annotationDefaultAttribute))
+        {
+            annotationDefaultAttribute.accept(clazz, method, attributeVisitor);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(Clazz clazz, Attribute attribute)
+    {
+        return stringMatcher.matches(attribute.getAttributeName(clazz));
+    }
+}
diff --git a/src/proguard/classfile/attribute/visitor/AttributeVisitor.java b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java
new file mode 100644
index 0000000..2b83db2
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/AttributeVisitor.java
@@ -0,0 +1,89 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+
+/**
+ * This interface specifies the methods for a visitor of <code>Attribute</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface AttributeVisitor
+{
+    // Attributes that are attached to classes.
+
+    public void visitUnknownAttribute(               Clazz clazz,                UnknownAttribute         unknownAttribute);
+    public void visitSourceFileAttribute(            Clazz clazz,                SourceFileAttribute      sourceFileAttribute);
+    public void visitSourceDirAttribute(             Clazz clazz,                SourceDirAttribute       sourceDirAttribute);
+    public void visitInnerClassesAttribute(          Clazz clazz,                InnerClassesAttribute    innerClassesAttribute);
+    public void visitEnclosingMethodAttribute(       Clazz clazz,                EnclosingMethodAttribute enclosingMethodAttribute);
+
+    // Attributes that are attached to classes, fields, and methods.
+
+    public void visitDeprecatedAttribute(            Clazz clazz,                DeprecatedAttribute deprecatedAttribute);
+    public void visitDeprecatedAttribute(            Clazz clazz, Field  field,  DeprecatedAttribute deprecatedAttribute);
+    public void visitDeprecatedAttribute(            Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute);
+
+    public void visitSyntheticAttribute(             Clazz clazz,                SyntheticAttribute  syntheticAttribute);
+    public void visitSyntheticAttribute(             Clazz clazz, Field  field,  SyntheticAttribute  syntheticAttribute);
+    public void visitSyntheticAttribute(             Clazz clazz, Method method, SyntheticAttribute  syntheticAttribute);
+
+    public void visitSignatureAttribute(             Clazz clazz,                SignatureAttribute  signatureAttribute);
+    public void visitSignatureAttribute(             Clazz clazz, Field  field,  SignatureAttribute  signatureAttribute);
+    public void visitSignatureAttribute(             Clazz clazz, Method method, SignatureAttribute  signatureAttribute);
+
+    // Attributes that are attached to fields.
+
+    public void visitConstantValueAttribute(         Clazz clazz, Field  field,  ConstantValueAttribute constantValueAttribute);
+
+    // Attributes that are attached to methods.
+
+    public void visitExceptionsAttribute(            Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute);
+    public void visitCodeAttribute(                  Clazz clazz, Method method, CodeAttribute       codeAttribute);
+
+    // Attributes that are attached to code attributes.
+
+    public void visitStackMapAttribute(              Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute               stackMapAttribute);
+    public void visitStackMapTableAttribute(         Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute          stackMapTableAttribute);
+    public void visitLineNumberTableAttribute(       Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute        lineNumberTableAttribute);
+    public void visitLocalVariableTableAttribute(    Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute     localVariableTableAttribute);
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute);
+
+    // Annotation attributes.
+
+    public void visitRuntimeVisibleAnnotationsAttribute(           Clazz clazz,                RuntimeVisibleAnnotationsAttribute   runtimeVisibleAnnotationsAttribute);
+    public void visitRuntimeVisibleAnnotationsAttribute(           Clazz clazz, Field  field,  RuntimeVisibleAnnotationsAttribute   runtimeVisibleAnnotationsAttribute);
+    public void visitRuntimeVisibleAnnotationsAttribute(           Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute   runtimeVisibleAnnotationsAttribute);
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(         Clazz clazz,                RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute);
+    public void visitRuntimeInvisibleAnnotationsAttribute(         Clazz clazz, Field  field,  RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute);
+    public void visitRuntimeInvisibleAnnotationsAttribute(         Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute);
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(  Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute   runtimeVisibleParameterAnnotationsAttribute);
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute);
+
+    public void visitAnnotationDefaultAttribute(                   Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute);
+}
diff --git a/src/proguard/classfile/attribute/ExceptionInfoVisitor.java b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
similarity index 77%
rename from src/proguard/classfile/attribute/ExceptionInfoVisitor.java
rename to src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
index 5f10cd4..a9d7d11 100644
--- a/src/proguard/classfile/attribute/ExceptionInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/ExceptionInfoVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ExceptionInfoVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,9 +18,10 @@
  * 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.attribute;
+package proguard.classfile.attribute.visitor;
 
 import proguard.classfile.*;
+import proguard.classfile.attribute.*;
 
 /**
  * This interface specifies the methods for a visitor of
@@ -32,5 +33,5 @@ import proguard.classfile.*;
  */
 public interface ExceptionInfoVisitor
 {
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo);
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo);
 }
diff --git a/src/proguard/classfile/attribute/InnerClassesInfoVisitor.java b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
similarity index 75%
rename from src/proguard/classfile/attribute/InnerClassesInfoVisitor.java
rename to src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
index c1d204c..835a5ae 100644
--- a/src/proguard/classfile/attribute/InnerClassesInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/InnerClassesInfoVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: InnerClassesInfoVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,10 +18,10 @@
  * 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.attribute;
+package proguard.classfile.attribute.visitor;
 
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.InnerClassesInfo;
 
 
 /**
@@ -34,5 +34,5 @@ import proguard.classfile.attribute.*;
  */
 public interface InnerClassesInfoVisitor
 {
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo);
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo);
 }
diff --git a/src/proguard/classfile/visitor/LineNumberInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
similarity index 78%
rename from src/proguard/classfile/visitor/LineNumberInfoVisitor.java
rename to src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
index 30ae411..a84531a 100644
--- a/src/proguard/classfile/visitor/LineNumberInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LineNumberInfoVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: LineNumberInfoVisitor.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * 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.visitor;
+package proguard.classfile.attribute.visitor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
@@ -34,5 +34,5 @@ import proguard.classfile.attribute.*;
  */
 public interface LineNumberInfoVisitor
 {
-    public void visitLineNumberInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfo lineNumberInfo);
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo);
 }
diff --git a/src/proguard/classfile/attribute/LocalVariableInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
similarity index 77%
rename from src/proguard/classfile/attribute/LocalVariableInfoVisitor.java
rename to src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
index d97e9e7..df5770f 100644
--- a/src/proguard/classfile/attribute/LocalVariableInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LocalVariableInfoVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: LocalVariableInfoVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * 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.attribute;
+package proguard.classfile.attribute.visitor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
@@ -34,5 +34,5 @@ import proguard.classfile.attribute.*;
  */
 public interface LocalVariableInfoVisitor
 {
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo);
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo);
 }
diff --git a/src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
similarity index 77%
rename from src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java
rename to src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
index f6d861a..fccfa46 100644
--- a/src/proguard/classfile/attribute/LocalVariableTypeInfoVisitor.java
+++ b/src/proguard/classfile/attribute/visitor/LocalVariableTypeInfoVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: LocalVariableTypeInfoVisitor.java,v 1.2.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * 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.attribute;
+package proguard.classfile.attribute.visitor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
@@ -34,5 +34,5 @@ import proguard.classfile.attribute.*;
  */
 public interface LocalVariableTypeInfoVisitor
 {
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo);
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo);
 }
diff --git a/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java
new file mode 100644
index 0000000..44a65ea
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/MultiAttributeVisitor.java
@@ -0,0 +1,356 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+
+/**
+ * This AttributeVisitor delegates all visits to each AttributeVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiAttributeVisitor implements AttributeVisitor
+{
+    private AttributeVisitor[] attributeVisitors;
+
+
+    public MultiAttributeVisitor()
+    {
+    }
+
+
+    public MultiAttributeVisitor(AttributeVisitor[] attributeVisitors)
+    {
+        this.attributeVisitors = attributeVisitors;
+    }
+
+
+    public void addAttributeVisitor(AttributeVisitor attributeVisitor)
+    {
+        incrementArraySize();
+
+        attributeVisitors[attributeVisitors.length - 1] = attributeVisitor;
+    }
+
+
+    private void incrementArraySize()
+    {
+        if (attributeVisitors == null)
+        {
+            attributeVisitors = new AttributeVisitor[1];
+        }
+        else
+        {
+            AttributeVisitor[] newAttributeVisitors =
+                new AttributeVisitor[attributeVisitors.length + 1];
+            System.arraycopy(attributeVisitors, 0,
+                             newAttributeVisitors, 0,
+                             attributeVisitors.length);
+            attributeVisitors = newAttributeVisitors;
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitUnknownAttribute(clazz, unknownAttribute);
+        }
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSourceFileAttribute(clazz, sourceFileAttribute);
+        }
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSourceDirAttribute(clazz, sourceDirAttribute);
+        }
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitInnerClassesAttribute(clazz, innerClassesAttribute);
+        }
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitEnclosingMethodAttribute(clazz, enclosingMethodAttribute);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitDeprecatedAttribute(clazz, deprecatedAttribute);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSyntheticAttribute(clazz, syntheticAttribute);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSignatureAttribute(clazz, syntheticAttribute);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitDeprecatedAttribute(clazz, field, deprecatedAttribute);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSyntheticAttribute(clazz, field, syntheticAttribute);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSignatureAttribute(clazz, field, syntheticAttribute);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitDeprecatedAttribute(clazz, method, deprecatedAttribute);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSyntheticAttribute(clazz, method, syntheticAttribute);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute syntheticAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitSignatureAttribute(clazz, method, syntheticAttribute);
+        }
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitConstantValueAttribute(clazz, field, constantValueAttribute);
+        }
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitExceptionsAttribute(clazz, method, exceptionsAttribute);
+        }
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitCodeAttribute(clazz, method, codeAttribute);
+        }
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute);
+        }
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute);
+        }
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute);
+        }
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute);
+        }
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute);
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        for (int index = 0; index < attributeVisitors.length; index++)
+        {
+            attributeVisitors[index].visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java
new file mode 100644
index 0000000..0246379
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/RequiredAttributeFilter.java
@@ -0,0 +1,351 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.obfuscate.AttributeShrinker;
+
+/**
+ * This AttributeVisitor delegates its visits to one of two other
+ * AttributeVisitor instances, depending on whether the visited attribute
+ * is strictly required or not.
+ *
+ * @see AttributeShrinker
+ *
+ * @author Eric Lafortune
+ */
+public class RequiredAttributeFilter
+implements   AttributeVisitor
+{
+    private final AttributeVisitor requiredAttributeVisitor;
+    private final AttributeVisitor optionalAttributeVisitor;
+
+
+    /**
+     * Creates a new RequiredAttributeFilter for visiting required attributes.
+     * @param requiredAttributeVisitor   the visitor that will visit required
+     *                                   attributes.
+     */
+    public RequiredAttributeFilter(AttributeVisitor requiredAttributeVisitor)
+    {
+        this(requiredAttributeVisitor, null);
+    }
+
+
+    /**
+     * Creates a new RequiredAttributeFilter for visiting required and
+     * optional attributes.
+     * @param requiredAttributeVisitor the visitor that will visit required
+     *                                 attributes.
+     * @param optionalAttributeVisitor the visitor that will visit optional
+     *                                 attributes.
+     */
+    public RequiredAttributeFilter(AttributeVisitor requiredAttributeVisitor,
+                                   AttributeVisitor optionalAttributeVisitor)
+    {
+        this.requiredAttributeVisitor = requiredAttributeVisitor;
+        this.optionalAttributeVisitor = optionalAttributeVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            unknownAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            sourceFileAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            sourceDirAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            innerClassesAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            enclosingMethodAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            deprecatedAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            deprecatedAttribute.accept(clazz, field, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            deprecatedAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            syntheticAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            syntheticAttribute.accept(clazz, field, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            syntheticAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            signatureAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            signatureAttribute.accept(clazz, field, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            signatureAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        if (requiredAttributeVisitor != null)
+        {
+            constantValueAttribute.accept(clazz, field, requiredAttributeVisitor);
+        }
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            exceptionsAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (requiredAttributeVisitor != null)
+        {
+            codeAttribute.accept(clazz, method, requiredAttributeVisitor);
+        }
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            stackMapAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        if (requiredAttributeVisitor != null)
+        {
+            stackMapTableAttribute.accept(clazz, method, codeAttribute, requiredAttributeVisitor);
+        }
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            lineNumberTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            localVariableTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            localVariableTypeTableAttribute.accept(clazz, method, codeAttribute, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, field, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeVisibleAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, field, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeInvisibleAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeVisibleParameterAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            runtimeInvisibleParameterAnnotationsAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        if (optionalAttributeVisitor != null)
+        {
+            annotationDefaultAttribute.accept(clazz, method, optionalAttributeVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/attribute/visitor/StackSizeComputer.java b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java
new file mode 100644
index 0000000..e543275
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/StackSizeComputer.java
@@ -0,0 +1,376 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.attribute.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.ClassPrinter;
+import proguard.classfile.attribute.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor computes the stack sizes at all instruction offsets
+ * of the code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class StackSizeComputer
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+
+    private boolean[] evaluated  = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]     stackSizes = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+
+    private boolean exitInstructionBlock;
+
+    private int stackSize;
+    private int maxStackSize;
+
+
+    /**
+     * Returns whether the instruction at the given offset is reachable in the
+     * most recently visited code attribute.
+     */
+    public boolean isReachable(int instructionOffset)
+    {
+        return evaluated[instructionOffset];
+    }
+
+
+    /**
+     * Returns the stack size at the given instruction offset of the most
+     * recently visited code attribute.
+     */
+    public int getStackSize(int instructionOffset)
+    {
+        if (!evaluated[instructionOffset])
+        {
+            throw new IllegalArgumentException("Unknown stack size at unreachable instruction offset ["+instructionOffset+"]");
+        }
+
+        return stackSizes[instructionOffset];
+    }
+
+
+    /**
+     * Returns the maximum stack size of the most recently visited code attribute.
+     */
+    public int getMaxStackSize()
+    {
+        return maxStackSize;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        // TODO: Remove this when the code has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
+        {
+            System.err.println("Unexpected error while computing stack sizes:");
+            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)
+    {
+        if (DEBUG)
+        {
+            System.out.println("StackSizeComputer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+        }
+
+        // Try to reuse the previous array.
+        int codeLength = codeAttribute.u4codeLength;
+        if (evaluated.length < codeLength)
+        {
+            evaluated  = new boolean[codeLength];
+            stackSizes = new int[codeLength];
+        }
+        else
+        {
+            for (int index = 0; index < codeLength; index++)
+            {
+                evaluated[index] = false;
+            }
+        }
+
+        // The initial stack is always empty.
+        stackSize    = 0;
+        maxStackSize = 0;
+
+        // Evaluate the instruction block starting at the entry point of the method.
+        evaluateInstructionBlock(clazz, method, codeAttribute, 0);
+
+        // Evaluate the exception handlers.
+        codeAttribute.exceptionsAccept(clazz, method, this);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        byte opcode = simpleInstruction.opcode;
+
+        // Some simple instructions exit from the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_IRETURN ||
+            opcode == InstructionConstants.OP_LRETURN ||
+            opcode == InstructionConstants.OP_FRETURN ||
+            opcode == InstructionConstants.OP_DRETURN ||
+            opcode == InstructionConstants.OP_ARETURN ||
+            opcode == InstructionConstants.OP_RETURN  ||
+            opcode == InstructionConstants.OP_ATHROW;
+    }
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        // Constant pool instructions never end the current instruction block.
+        exitInstructionBlock = false;
+    }
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        byte opcode = variableInstruction.opcode;
+
+        // The ret instruction end the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_RET;
+    }
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        byte opcode = branchInstruction.opcode;
+
+        // Evaluate the target instruction blocks.
+        evaluateInstructionBlock(clazz,
+                                 method,
+                                 codeAttribute,
+                                 offset +
+                                 branchInstruction.branchOffset);
+
+        // Evaluate the instructions after a subroutine branch.
+        if (opcode == InstructionConstants.OP_JSR ||
+            opcode == InstructionConstants.OP_JSR_W)
+        {
+            // We assume subroutine calls (jsr and jsr_w instructions) don't
+            // change the stack, other than popping the return value.
+            stackSize -= 1;
+
+            evaluateInstructionBlock(clazz,
+                                     method,
+                                     codeAttribute,
+                                     offset + branchInstruction.length(offset));
+        }
+
+        // Some branch instructions always end the current instruction block.
+        exitInstructionBlock =
+            opcode == InstructionConstants.OP_GOTO   ||
+            opcode == InstructionConstants.OP_GOTO_W ||
+            opcode == InstructionConstants.OP_JSR    ||
+            opcode == InstructionConstants.OP_JSR_W;
+    }
+
+
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        // Evaluate the target instruction blocks.
+
+        // Loop over all jump offsets.
+        int[] jumpOffsets = switchInstruction.jumpOffsets;
+
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            // Evaluate the jump instruction block.
+            evaluateInstructionBlock(clazz,
+                                     method,
+                                     codeAttribute,
+                                     offset + jumpOffsets[index]);
+        }
+
+        // Also evaluate the default instruction block.
+        evaluateInstructionBlock(clazz,
+                                 method,
+                                 codeAttribute,
+                                 offset + switchInstruction.defaultOffset);
+
+        // The switch instruction always ends the current instruction block.
+        exitInstructionBlock = true;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        if (DEBUG)
+        {
+            System.out.println("Exception:");
+        }
+
+        // The stack size when entering the exception handler is always 1.
+        stackSize = 1;
+
+        // Evaluate the instruction block starting at the entry point of the
+        // exception handler.
+        evaluateInstructionBlock(clazz,
+                                 method,
+                                 codeAttribute,
+                                 exceptionInfo.u2handlerPC);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Evaluates a block of instructions that hasn't been handled before,
+     * starting at the given offset and ending a branch instruction, a return
+     * instruction, or a throw instruction. Branch instructions are handled
+     * recursively.
+     */
+    private void evaluateInstructionBlock(Clazz         clazz,
+                                          Method        method,
+                                          CodeAttribute codeAttribute,
+                                          int           instructionOffset)
+    {
+        if (DEBUG)
+        {
+            if (evaluated[instructionOffset])
+            {
+                System.out.println("-- (instruction block at "+instructionOffset+" already evaluated)");
+            }
+            else
+            {
+                System.out.println("-- instruction block:");
+            }
+        }
+
+        // Remember the initial stack size.
+        int initialStackSize = stackSize;
+
+        // Remember the maximum stack size.
+        if (maxStackSize < stackSize)
+        {
+            maxStackSize = stackSize;
+        }
+
+        // Evaluate any instructions that haven't been evaluated before.
+        while (!evaluated[instructionOffset])
+        {
+            // Mark the instruction as evaluated.
+            evaluated[instructionOffset] = true;
+
+            Instruction instruction = InstructionFactory.create(codeAttribute.code,
+                                                                instructionOffset);
+
+            if (DEBUG)
+            {
+                int stackPushCount = instruction.stackPushCount(clazz);
+                int stackPopCount  = instruction.stackPopCount(clazz);
+                System.out.println("["+instructionOffset+"]: "+
+                                   stackSize+" - "+
+                                   stackPopCount+" + "+
+                                   stackPushCount+" = "+
+                                   (stackSize+stackPushCount-stackPopCount)+": "+
+                                   instruction.toString(instructionOffset));
+            }
+
+            // Compute the instruction's effect on the stack size.
+            stackSize -= instruction.stackPopCount(clazz);
+
+            if (stackSize < 0)
+            {
+                throw new IllegalArgumentException("Stack size becomes negative after instruction "+
+                                                   instruction.toString(instructionOffset)+" in ["+
+                                                   clazz.getName()+"."+
+                                                   method.getName(clazz)+
+                                                   method.getDescriptor(clazz)+"]");
+            }
+
+            stackSizes[instructionOffset] =
+            stackSize += instruction.stackPushCount(clazz);
+
+            // Remember the maximum stack size.
+            if (maxStackSize < stackSize)
+            {
+                maxStackSize = stackSize;
+            }
+
+            // Remember the next instruction offset.
+            int nextInstructionOffset = instructionOffset +
+                                        instruction.length(instructionOffset);
+
+            // Visit the instruction, in order to handle branches.
+            instruction.accept(clazz, method, codeAttribute, instructionOffset, this);
+
+            // Stop evaluating after a branch.
+            if (exitInstructionBlock)
+            {
+                break;
+            }
+
+            // Continue with the next instruction.
+            instructionOffset = nextInstructionOffset;
+
+            if (DEBUG)
+            {
+                if (evaluated[instructionOffset])
+                {
+                    System.out.println("-- (instruction at "+instructionOffset+" already evaluated)");
+                }
+            }
+        }
+
+        // Restore the stack size for possible subsequent instruction blocks.
+        this.stackSize = initialStackSize;
+    }
+}
diff --git a/src/proguard/classfile/attribute/visitor/package.html b/src/proguard/classfile/attribute/visitor/package.html
new file mode 100644
index 0000000..056244a
--- /dev/null
+++ b/src/proguard/classfile/attribute/visitor/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors for attributes and their components.
+</body>
diff --git a/src/proguard/classfile/constant/ClassConstant.java b/src/proguard/classfile/constant/ClassConstant.java
new file mode 100644
index 0000000..33db73f
--- /dev/null
+++ b/src/proguard/classfile/constant/ClassConstant.java
@@ -0,0 +1,105 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This Constant represents a class constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassConstant extends Constant
+{
+    public int u2nameIndex;
+
+    /**
+     * An extra field pointing to the referenced Clazz object.
+     * This field is filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer ClassReferenceInitializer}</code>.
+     */
+    public Clazz referencedClass;
+
+    /**
+     * An extra field pointing to the java.lang.Class Clazz object.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>..
+     */
+    public Clazz javaLangClassClass;
+
+
+    /**
+     * Creates an uninitialized ClassConstant.
+     */
+    public ClassConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new ClassConstant with the given name index.
+     * @param u2nameIndex     the index of the name in the constant pool.
+     * @param referencedClass the referenced class.
+     */
+    public ClassConstant(int   u2nameIndex,
+                         Clazz referencedClass)
+    {
+        this.u2nameIndex     = u2nameIndex;
+        this.referencedClass = referencedClass;
+    }
+
+
+    /**
+     * Returns the name.
+     */
+    public String getName(Clazz clazz)
+    {
+        return clazz.getString(u2nameIndex);
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Class;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitClassConstant(clazz, this);
+    }
+
+
+    /**
+     * Lets the referenced class accept the given visitor.
+     */
+    public void referencedClassAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClass != null)
+        {
+            referencedClass.accept(classVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/constant/Constant.java b/src/proguard/classfile/constant/Constant.java
new file mode 100644
index 0000000..cd9ea2a
--- /dev/null
+++ b/src/proguard/classfile/constant/Constant.java
@@ -0,0 +1,68 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This abstract class represents an entry in the ConstantPool. Specific types
+ * of entries are subclassed from it.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class Constant implements VisitorAccepter
+{
+    //public int  u1tag;
+    //public byte info[];
+
+    /**
+     * An extra field in which visitors can store information.
+     */
+    public Object visitorInfo;
+
+
+    // Abstract methods to be implemented by extensions.
+
+    /**
+     * Returns the constant pool info tag that specifies the entry type.
+     */
+    public abstract int getTag();
+
+
+    /**
+     * Accepts the given visitor.
+     */
+    public abstract void accept(Clazz clazz, ConstantVisitor constantVisitor);
+
+
+    // Implementations for VisitorAccepter.
+
+    public Object getVisitorInfo()
+    {
+        return visitorInfo;
+    }
+
+    public void setVisitorInfo(Object visitorInfo)
+    {
+        this.visitorInfo = visitorInfo;
+    }
+}
diff --git a/src/proguard/classfile/constant/DoubleConstant.java b/src/proguard/classfile/constant/DoubleConstant.java
new file mode 100644
index 0000000..1f496b0
--- /dev/null
+++ b/src/proguard/classfile/constant/DoubleConstant.java
@@ -0,0 +1,82 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a double constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class DoubleConstant extends Constant
+{
+    public double f8value;
+
+
+    /**
+     * Creates an uninitialized DoubleConstant.
+     */
+    public DoubleConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new DoubleConstant with the given double value.
+     */
+    public DoubleConstant(double value)
+    {
+        f8value = value;
+    }
+
+
+    /**
+     * Returns the double value of this DoubleConstant.
+     */
+    public double getValue()
+    {
+        return f8value;
+    }
+
+
+    /**
+     * Sets the double value of this DoubleConstant.
+     */
+    public void setValue(double value)
+    {
+        f8value = value;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Double;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitDoubleConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/FieldrefConstant.java b/src/proguard/classfile/constant/FieldrefConstant.java
new file mode 100644
index 0000000..0e73149
--- /dev/null
+++ b/src/proguard/classfile/constant/FieldrefConstant.java
@@ -0,0 +1,71 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a field reference constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class FieldrefConstant extends RefConstant
+{
+    /**
+     * Creates an uninitialized FieldrefConstant.
+     */
+    public FieldrefConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new FieldrefConstant with the given name and type indices.
+     * @param u2classIndex           the index of the class in the constant pool.
+     * @param u2nameAndTypeIndex     the index of the name and type entry in the constant pool.
+     * @param referencedClass        the referenced class.
+     * @param referencedMember       the referenced member info.
+     */
+    public FieldrefConstant(int    u2classIndex,
+                            int    u2nameAndTypeIndex,
+                            Clazz  referencedClass,
+                            Member referencedMember)
+    {
+        this.u2classIndex       = u2classIndex;
+        this.u2nameAndTypeIndex = u2nameAndTypeIndex;
+        this.referencedClass    = referencedClass;
+        this.referencedMember   = referencedMember;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Fieldref;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitFieldrefConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/FloatConstant.java b/src/proguard/classfile/constant/FloatConstant.java
new file mode 100644
index 0000000..be2318a
--- /dev/null
+++ b/src/proguard/classfile/constant/FloatConstant.java
@@ -0,0 +1,82 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a float constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class FloatConstant extends Constant
+{
+    public float f4value;
+
+
+    /**
+     * Creates an uninitialized FloatConstant.
+     */
+    public FloatConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new FloatConstant with the given float value.
+     */
+    public FloatConstant(float value)
+    {
+        f4value = value;
+    }
+
+
+    /**
+     * Returns the float value of this FloatConstant.
+     */
+    public float getValue()
+    {
+        return f4value;
+    }
+
+
+    /**
+     * Sets the float value of this FloatConstant.
+     */
+    public void setValue(float value)
+    {
+        f4value = value;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Float;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitFloatConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/IntegerConstant.java b/src/proguard/classfile/constant/IntegerConstant.java
new file mode 100644
index 0000000..23fcfc3
--- /dev/null
+++ b/src/proguard/classfile/constant/IntegerConstant.java
@@ -0,0 +1,82 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a integer constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class IntegerConstant extends Constant
+{
+    public int u4value;
+
+
+    /**
+     * Creates an uninitialized IntegerConstant.
+     */
+    public IntegerConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new IntegerConstant with the given integer value.
+     */
+    public IntegerConstant(int value)
+    {
+        u4value = value;
+    }
+
+
+    /**
+     * Returns the integer value of this IntegerConstant.
+     */
+    public int getValue()
+    {
+        return u4value;
+    }
+
+
+    /**
+     * Sets the integer value of this IntegerConstant.
+     */
+    public void setValue(int value)
+    {
+        u4value = value;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Integer;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitIntegerConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/InterfaceMethodrefConstant.java b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java
new file mode 100644
index 0000000..046fe9a
--- /dev/null
+++ b/src/proguard/classfile/constant/InterfaceMethodrefConstant.java
@@ -0,0 +1,71 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a interface method reference constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class InterfaceMethodrefConstant extends RefConstant
+{
+    /**
+     * Creates an uninitialized InterfaceMethodrefConstant.
+     */
+    public InterfaceMethodrefConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new InterfaceMethodrefConstant with the given name and type indices.
+     * @param u2classIndex         the index   of the class in the constant pool.
+     * @param u2nameAndTypeIndex   the index   of the name and type entry in the constant pool.
+     * @param referencedClass      the referenced class.
+     * @param referencedMember     the referenced member info.
+     */
+    public InterfaceMethodrefConstant(int    u2classIndex,
+                                      int    u2nameAndTypeIndex,
+                                      Clazz  referencedClass,
+                                      Member referencedMember)
+    {
+        this.u2classIndex       = u2classIndex;
+        this.u2nameAndTypeIndex = u2nameAndTypeIndex;
+        this.referencedClass    = referencedClass;
+        this.referencedMember   = referencedMember;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_InterfaceMethodref;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitInterfaceMethodrefConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/LongConstant.java b/src/proguard/classfile/constant/LongConstant.java
new file mode 100644
index 0000000..2a50142
--- /dev/null
+++ b/src/proguard/classfile/constant/LongConstant.java
@@ -0,0 +1,82 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a long constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class LongConstant extends Constant
+{
+    public long u8value;
+
+
+    /**
+     * Creates an uninitialized LongConstant.
+     */
+    public LongConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new LongConstant with the given long value.
+     */
+    public LongConstant(long value)
+    {
+        u8value = value;
+    }
+
+
+    /**
+     * Returns the long value of this LongConstant.
+     */
+    public long getValue()
+    {
+        return u8value;
+    }
+
+
+    /**
+     * Sets the long value of this LongConstant.
+     */
+    public void setValue(long value)
+    {
+        u8value = value;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Long;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitLongConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/constant/MethodrefConstant.java b/src/proguard/classfile/constant/MethodrefConstant.java
new file mode 100644
index 0000000..9d71986
--- /dev/null
+++ b/src/proguard/classfile/constant/MethodrefConstant.java
@@ -0,0 +1,71 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This Constant represents a method reference constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class MethodrefConstant extends RefConstant
+{
+    /**
+     * Creates an uninitialized MethodrefConstant.
+     */
+    public MethodrefConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new MethodrefConstant with the given name and type indices.
+     * @param u2classIndex         the index of the class in the constant pool.
+     * @param u2nameAndTypeIndex   the index of the name and type entry in the constant pool.
+     * @param referencedClass      the referenced class.
+     * @param referencedMember     the referenced member info.
+     */
+    public MethodrefConstant(int    u2classIndex,
+                             int    u2nameAndTypeIndex,
+                             Clazz  referencedClass,
+                             Member referencedMember)
+    {
+        this.u2classIndex       = u2classIndex;
+        this.u2nameAndTypeIndex = u2nameAndTypeIndex;
+        this.referencedClass    = referencedClass;
+        this.referencedMember   = referencedMember;
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Methodref;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitMethodrefConstant(clazz, this);
+    }
+}
diff --git a/src/proguard/classfile/NameAndTypeCpInfo.java b/src/proguard/classfile/constant/NameAndTypeConstant.java
similarity index 59%
rename from src/proguard/classfile/NameAndTypeCpInfo.java
rename to src/proguard/classfile/constant/NameAndTypeConstant.java
index 167ae54..15f89a1 100644
--- a/src/proguard/classfile/NameAndTypeCpInfo.java
+++ b/src/proguard/classfile/constant/NameAndTypeConstant.java
@@ -1,8 +1,7 @@
-/* $Id: NameAndTypeCpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,37 +18,38 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
-
-import proguard.classfile.visitor.*;
+package proguard.classfile.constant;
 
-import java.io.*;
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 
 /**
- * Representation of a 'name and type' entry in the ConstantPool.
+ * This Constant represents a name and type constant in the constant pool.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
-public class NameAndTypeCpInfo extends CpInfo implements Cloneable
+public class NameAndTypeConstant extends Constant
 {
     public int u2nameIndex;
     public int u2descriptorIndex;
 
 
-    protected NameAndTypeCpInfo()
+    /**
+     * Creates an uninitialized NameAndTypeConstant.
+     */
+    public NameAndTypeConstant()
     {
     }
 
 
     /**
-     * Creates a new NameAndTypeCpInfo with the given name and type indices.
+     * Creates a new NameAndTypeConstant with the given name and type indices.
      * @param u2nameIndex       the index of the name in the constant pool.
      * @param u2descriptorIndex the index of the descriptor in the constant
      *                          pool.
      */
-    public NameAndTypeCpInfo(int u2nameIndex,
-                             int u2descriptorIndex)
+    public NameAndTypeConstant(int u2nameIndex,
+                               int u2descriptorIndex)
     {
         this.u2nameIndex       = u2nameIndex;
         this.u2descriptorIndex = u2descriptorIndex;
@@ -91,41 +91,29 @@ public class NameAndTypeCpInfo extends CpInfo implements Cloneable
     /**
      * Returns the name.
      */
-    public String getName(ClassFile classFile)
+    public String getName(Clazz clazz)
     {
-        return classFile.getCpString(u2nameIndex);
+        return clazz.getString(u2nameIndex);
     }
 
     /**
      * Returns the type.
      */
-    public String getType(ClassFile classFile)
+    public String getType(Clazz clazz)
     {
-        return classFile.getCpString(u2descriptorIndex);
+        return clazz.getString(u2descriptorIndex);
     }
 
 
-    // Implementations for CpInfo.
+    // Implementations for Constant.
 
     public int getTag()
     {
         return ClassConstants.CONSTANT_NameAndType;
     }
 
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2nameIndex = din.readUnsignedShort();
-        u2descriptorIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2nameIndex);
-        dout.writeShort(u2descriptorIndex);
-    }
-
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
     {
-        cpInfoVisitor.visitNameAndTypeCpInfo(classFile, this);
+        constantVisitor.visitNameAndTypeConstant(clazz, this);
     }
 }
diff --git a/src/proguard/classfile/RefCpInfo.java b/src/proguard/classfile/constant/RefConstant.java
similarity index 51%
rename from src/proguard/classfile/RefCpInfo.java
rename to src/proguard/classfile/constant/RefConstant.java
index 83b5619..116feb9 100644
--- a/src/proguard/classfile/RefCpInfo.java
+++ b/src/proguard/classfile/constant/RefConstant.java
@@ -1,8 +1,7 @@
-/* $Id: RefCpInfo.java,v 1.22.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,39 +18,39 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
+package proguard.classfile.constant;
 
+import proguard.classfile.*;
 import proguard.classfile.visitor.*;
 
-import java.io.*;
-
 /**
- * Representation of a 'ref'-type entry in the ConstantPool.
+ * This Constant represents a ref constant in the constant pool.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
-public abstract class RefCpInfo extends CpInfo
+public abstract class RefConstant extends Constant
 {
     public int u2classIndex;
     public int u2nameAndTypeIndex;
 
     /**
-     * An extra field pointing to the referenced ClassFile object.
+     * An extra field pointing to the referenced Clazz object.
      * This field is typically filled out by the <code>{@link
-     * ClassFileReferenceInitializer}</code>.
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      */
-    public ClassFile referencedClassFile;
+    public Clazz referencedClass;
 
     /**
-     * An extra field optionally pointing to the referenced MemberInfo object.
+     * An extra field optionally pointing to the referenced Member object.
      * This field is typically filled out by the <code>{@link
-     * ClassFileReferenceInitializer}</code>.
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>.
      */
-    public MemberInfo referencedMemberInfo;
+    public Member referencedMember;
 
 
-    protected RefCpInfo()
+    protected RefConstant()
     {
     }
 
@@ -83,36 +82,36 @@ public abstract class RefCpInfo extends CpInfo
     /**
      * Returns the class name.
      */
-    public String getClassName(ClassFile classFile)
+    public String getClassName(Clazz clazz)
     {
-        return classFile.getCpClassNameString(u2classIndex);
+        return clazz.getClassName(u2classIndex);
     }
 
     /**
      * Returns the method/field name.
      */
-    public String getName(ClassFile classFile)
+    public String getName(Clazz clazz)
     {
-        return classFile.getCpNameString(u2nameAndTypeIndex);
+        return clazz.getName(u2nameAndTypeIndex);
     }
 
     /**
      * Returns the type.
      */
-    public String getType(ClassFile classFile)
+    public String getType(Clazz clazz)
     {
-        return classFile.getCpTypeString(u2nameAndTypeIndex);
+        return clazz.getType(u2nameAndTypeIndex);
     }
 
 
     /**
-     * Lets the referenced class file accept the given visitor.
+     * Lets the referenced class accept the given visitor.
      */
-    public void referencedClassAccept(ClassFileVisitor classFileVisitor)
+    public void referencedClassAccept(ClassVisitor classVisitor)
     {
-        if (referencedClassFile != null)
+        if (referencedClass != null)
         {
-            referencedClassFile.accept(classFileVisitor);
+            referencedClass.accept(classVisitor);
         }
     }
 
@@ -120,27 +119,12 @@ public abstract class RefCpInfo extends CpInfo
     /**
      * Lets the referenced class member accept the given visitor.
      */
-    public void referencedMemberInfoAccept(MemberInfoVisitor memberInfoVisitor)
+    public void referencedMemberAccept(MemberVisitor memberVisitor)
     {
-        if (referencedMemberInfo != null)
+        if (referencedMember != null)
         {
-            referencedMemberInfo.accept(referencedClassFile,
-                                        memberInfoVisitor);
+            referencedMember.accept(referencedClass,
+                                    memberVisitor);
         }
     }
-
-
-    // Implementations for CpInfo.
-
-    protected void readInfo(DataInput din) throws IOException
-    {
-        u2classIndex = din.readUnsignedShort();
-        u2nameAndTypeIndex = din.readUnsignedShort();
-    }
-
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        dout.writeShort(u2classIndex);
-        dout.writeShort(u2nameAndTypeIndex);
-    }
 }
diff --git a/src/proguard/classfile/constant/StringConstant.java b/src/proguard/classfile/constant/StringConstant.java
new file mode 100644
index 0000000..2c67715
--- /dev/null
+++ b/src/proguard/classfile/constant/StringConstant.java
@@ -0,0 +1,135 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This Constant represents a string constant in the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class StringConstant extends Constant
+{
+    public int u2stringIndex;
+
+    /**
+     * An extra field pointing to the referenced Clazz object, if this
+     * string is being used in Class.forName(), .class, or
+     * Class.getDeclaredField/Method constructs.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.DynamicClassReferenceInitializer
+     * DynamicClassReferenceInitializer}</code> or by the <code>{@link
+     * proguard.classfile.util.DynamicMemberReferenceInitializer
+     * DynamicMemberReferenceInitializer}</code>.
+     */
+    public Clazz referencedClass;
+
+    /**
+     * An extra field pointing to the referenced Member object, if this
+     * string is being used in Class.getDeclaredField/Method constructs.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.DynamicMemberReferenceInitializer
+     * DynamicMemberReferenceInitializer}</code>.
+     */
+    public Member referencedMember;
+
+    /**
+     * An extra field pointing to the java.lang.String Clazz object.
+     * This field is typically filled out by the <code>{@link
+     * proguard.classfile.util.ClassReferenceInitializer
+     * ClassReferenceInitializer}</code>..
+     */
+    public Clazz javaLangStringClass;
+
+
+    /**
+     * Creates an uninitialized StringConstant.
+     */
+    public StringConstant()
+    {
+    }
+
+
+    /**
+     * Creates a new StringConstant with the given string index.
+     * @param u2stringIndex   the index of the string in the constant pool.
+     * @param referencedClass the referenced class, if any.
+     * @param referenceMember the referenced class member, if any.
+     */
+    public StringConstant(int    u2stringIndex,
+                          Clazz  referencedClass,
+                          Member referenceMember)
+    {
+        this.u2stringIndex    = u2stringIndex;
+        this.referencedClass  = referencedClass;
+        this.referencedMember = referenceMember;
+    }
+
+
+    /**
+     * Returns the string value.
+     */
+    public String getString(Clazz clazz)
+    {
+        return clazz.getString(u2stringIndex);
+    }
+
+
+    // Implementations for Constant.
+
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_String;
+    }
+
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
+    {
+        constantVisitor.visitStringConstant(clazz, this);
+    }
+
+
+    /**
+     * Lets the referenced class accept the given visitor.
+     */
+    public void referencedClassAccept(ClassVisitor classVisitor)
+    {
+        if (referencedClass  != null &&
+            referencedMember == null)
+        {
+            referencedClass.accept(classVisitor);
+        }
+    }
+
+
+    /**
+     * Lets the referenced member accept the given visitor.
+     */
+    public void referencedMemberAccept(MemberVisitor memberVisitor)
+    {
+        if (referencedMember != null)
+        {
+            referencedMember.accept(referencedClass, memberVisitor);
+        }
+    }
+}
diff --git a/src/proguard/classfile/Utf8CpInfo.java b/src/proguard/classfile/constant/Utf8Constant.java
similarity index 78%
rename from src/proguard/classfile/Utf8CpInfo.java
rename to src/proguard/classfile/constant/Utf8Constant.java
index 96206b4..6d4fb86 100644
--- a/src/proguard/classfile/Utf8CpInfo.java
+++ b/src/proguard/classfile/constant/Utf8Constant.java
@@ -1,8 +1,7 @@
-/* $Id: Utf8CpInfo.java,v 1.21.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,19 +18,19 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
+package proguard.classfile.constant;
 
-import proguard.classfile.visitor.*;
+import proguard.classfile.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 
-import java.io.*;
+import java.io.UnsupportedEncodingException;
 
 /**
- * Representation of a 'UTF-8' entry in the ConstantPool.
+ * This Constant represents a UTF-8 constant in the constant pool.
  *
- * @author Mark Welsh
  * @author Eric Lafortune
  */
-public class Utf8CpInfo extends CpInfo
+public class Utf8Constant extends Constant
 {
     private static final String ENCODING = "UTF-8";
 
@@ -53,7 +52,7 @@ public class Utf8CpInfo extends CpInfo
     private static final byte THREE_BYTE_MASK3     = (byte)0x3f;
 
 
-    // There are a lot of Utf8CpInfo objects, so we're optimising their storage.
+    // There are a lot of Utf8Constant objects, so we're optimising their storage.
     // Initially, we're storing the UTF-8 bytes in a byte array.
     // When the corresponding String is requested, we ditch the array and just
     // store the String.
@@ -64,71 +63,88 @@ public class Utf8CpInfo extends CpInfo
     private String utf8string;
 
 
-    protected Utf8CpInfo()
+    /**
+     * Creates an uninitialized Utf8Constant.
+     *
+     */
+    public Utf8Constant()
     {
     }
 
+
     /**
-     * Constructor used when appending fresh UTF-8 entries to the constant pool.
+     * Creates a Utf8Constant containing the given string.
      */
-    public Utf8CpInfo(String utf8string)
+    public Utf8Constant(String utf8string)
     {
         this.bytes      = null;
         this.utf8string = utf8string;
     }
 
+
     /**
-     * Returns UTF-8 data as a String.
+     * Initializes the UTF-8 data with an array of bytes.
      */
-    public String getString()
+    public void setBytes(byte[] bytes)
+    {
+        this.bytes      = bytes;
+        this.utf8string = null;
+    }
+
+
+    /**
+     * Returns the UTF-8 data as an array of bytes.
+     */
+    public byte[] getBytes()
     {
         try
         {
-            switchToStringRepresentation();
+            return getByteArrayRepresentation();
         }
         catch (UnsupportedEncodingException ex)
         {
+            throw new RuntimeException(ex.getMessage());
         }
-
-        return utf8string;
     }
 
+
     /**
-     * Sets UTF-8 data as String.
+     * Initializes the UTF-8 data with a String.
      */
-    public void setString(String utf8String) throws Exception
+    public void setString(String utf8String)
     {
         this.bytes      = null;
         this.utf8string = utf8String;
     }
 
 
-    // Implementations for CpInfo.
-
-    public int getTag()
+    /**
+     * Returns the UTF-8 data as a String.
+     */
+    public String getString()
     {
-        return ClassConstants.CONSTANT_Utf8;
-    }
+        try
+        {
+            switchToStringRepresentation();
+        }
+        catch (UnsupportedEncodingException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
 
-    protected void readInfo(DataInput din) throws IOException
-    {
-        int u2length = din.readUnsignedShort();
-        bytes = new byte[u2length];
-        din.readFully(bytes);
+        return utf8string;
     }
 
-    protected void writeInfo(DataOutput dout) throws IOException
-    {
-        byte[] bytes    = getByteArrayRepresentation();
-        int    u2length = bytes.length;
+    // Implementations for Constant.
 
-        dout.writeShort(u2length);
-        dout.write(bytes);
+    public int getTag()
+    {
+        return ClassConstants.CONSTANT_Utf8;
     }
 
-    public void accept(ClassFile classFile, CpInfoVisitor cpInfoVisitor)
+    public void accept(Clazz clazz, ConstantVisitor constantVisitor)
     {
-        cpInfoVisitor.visitUtf8CpInfo(classFile, this);
+        constantVisitor.visitUtf8Constant(clazz, this);
     }
 
 
@@ -140,14 +156,14 @@ public class Utf8CpInfo extends CpInfo
         if (utf8string == null)
         {
             utf8string = new String(bytes, ENCODING);
-            bytes = null;
+            bytes      = null;
         }
     }
 
 
     /**
      * Transforms UTF-8 bytes to the slightly modified UTF-8 representation that
-     * is used by class files.
+     * is used by classes.
      */
     private byte[] getByteArrayRepresentation() throws UnsupportedEncodingException
     {
@@ -186,7 +202,7 @@ public class Utf8CpInfo extends CpInfo
             char c = utf8string.charAt(stringIndex);
             if (c == 0)
             {
-                // The 0 character gets a two-byte representation in class files.
+                // The 0 character gets a two-byte representation in classes.
                 bytes[byteIndex++] = TWO_BYTE_CONSTANT1;
                 bytes[byteIndex++] = TWO_BYTE_CONSTANT2;
             }
diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java
similarity index 53%
copy from src/proguard/classfile/visitor/ClassCounter.java
copy to src/proguard/classfile/constant/visitor/AllConstantVisitor.java
index 892bcbc..01c5487 100644
--- a/src/proguard/classfile/visitor/ClassCounter.java
+++ b/src/proguard/classfile/constant/visitor/AllConstantVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ClassCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,40 +18,36 @@
  * 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.visitor;
+package proguard.classfile.constant.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.visitor.ClassVisitor;
+
 
 /**
- * This ClassFileVisitor counts the number of classes that has been visited.
+ * This ClassVisitor lets a given ConstantVisitor visit all constant pool
+ * entries of the program classes it visits.
  *
  * @author Eric Lafortune
  */
-public class ClassCounter implements ClassFileVisitor
+public class AllConstantVisitor implements ClassVisitor
 {
-    private int count;
+    private final ConstantVisitor constantVisitor;
 
 
-    /**
-     * Returns the number of classes that has been visited so far.
-     */
-    public int getCount()
+    public AllConstantVisitor(ConstantVisitor constantVisitor)
     {
-        return count;
+        this.constantVisitor = constantVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        count++;
+        programClass.constantPoolEntriesAccept(constantVisitor);
     }
 
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        count++;
-    }
+    public void visitLibraryClass(LibraryClass libraryClass) {}
 }
diff --git a/src/proguard/classfile/constant/visitor/ConstantVisitor.java b/src/proguard/classfile/constant/visitor/ConstantVisitor.java
new file mode 100644
index 0000000..539c24d
--- /dev/null
+++ b/src/proguard/classfile/constant/visitor/ConstantVisitor.java
@@ -0,0 +1,46 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.constant.visitor;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.constant.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of <code>Constant</code>
+ * objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface ConstantVisitor
+{
+    public void visitIntegerConstant(           Clazz clazz, IntegerConstant            integerConstant);
+    public void visitLongConstant(              Clazz clazz, LongConstant               longConstant);
+    public void visitFloatConstant(             Clazz clazz, FloatConstant              floatConstant);
+    public void visitDoubleConstant(            Clazz clazz, DoubleConstant             doubleConstant);
+    public void visitStringConstant(            Clazz clazz, StringConstant             stringConstant);
+    public void visitUtf8Constant(              Clazz clazz, Utf8Constant               utf8Constant);
+    public void visitFieldrefConstant(          Clazz clazz, FieldrefConstant           fieldrefConstant);
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant);
+    public void visitMethodrefConstant(         Clazz clazz, MethodrefConstant          methodrefConstant);
+    public void visitClassConstant(             Clazz clazz, ClassConstant              classConstant);
+    public void visitNameAndTypeConstant(       Clazz clazz, NameAndTypeConstant        nameAndTypeConstant);
+}
diff --git a/src/proguard/classfile/constant/visitor/package.html b/src/proguard/classfile/constant/visitor/package.html
new file mode 100644
index 0000000..e20f48e
--- /dev/null
+++ b/src/proguard/classfile/constant/visitor/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors for class constants.
+</body>
diff --git a/src/proguard/classfile/editor/AccessFixer.java b/src/proguard/classfile/editor/AccessFixer.java
new file mode 100644
index 0000000..28d2ff4
--- /dev/null
+++ b/src/proguard/classfile/editor/AccessFixer.java
@@ -0,0 +1,138 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ConstantVisitor fixes the access modifiers of all classes and class
+ * members that are referenced by the constants that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AccessFixer
+extends      SimplifiedVisitor
+implements   ConstantVisitor,
+             ClassVisitor,
+             MemberVisitor
+{
+    private Clazz referencingClass;
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        referencingClass = clazz;
+
+        // Make sure the access flags of the referenced class or class member,
+        // if any, are acceptable.
+        stringConstant.referencedClassAccept(this);
+        stringConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        referencingClass = clazz;
+
+        // Make sure the access flags of the referenced class member are acceptable.
+        refConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        referencingClass = clazz;
+
+        // Make sure the access flags of the referenced class are acceptable.
+        classConstant.referencedClassAccept(this);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitLibraryClass(LibraryClass libraryClass) {}
+
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        int currentAccessFlags  = programClass.getAccessFlags();
+        int currentAccessLevel  = AccessUtil.accessLevel(currentAccessFlags);
+
+        // Compute the required access level.
+        Clazz referencingClass = this.referencingClass;
+        int requiredAccessLevel =
+            programClass.equals(this.referencingClass)    ? AccessUtil.PRIVATE         :
+            inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE :
+            referencingClass.extends_(programClass)       ? AccessUtil.PROTECTED       :
+                                                            AccessUtil.PUBLIC;
+
+        // Fix the class access flags if necessary.
+        if (currentAccessLevel < requiredAccessLevel)
+        {
+            programClass.u2accessFlags =
+                AccessUtil.replaceAccessFlags(currentAccessFlags,
+                                              AccessUtil.accessFlags(requiredAccessLevel));
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) {}
+
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        int currentAccessFlags  = programMember.getAccessFlags();
+        int currentAccessLevel  = AccessUtil.accessLevel(currentAccessFlags);
+
+        // Compute the required access level.
+        int requiredAccessLevel =
+            programClass.equals(referencingClass)         ? AccessUtil.PRIVATE         :
+            inSamePackage(programClass, referencingClass) ? AccessUtil.PACKAGE_VISIBLE :
+                                                            AccessUtil.PUBLIC;
+        // Fix the class member access flags if necessary.
+        if (currentAccessLevel < requiredAccessLevel)
+        {
+            programMember.u2accessFlags =
+                AccessUtil.replaceAccessFlags(currentAccessFlags,
+                                              AccessUtil.accessFlags(requiredAccessLevel));
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean inSamePackage(ProgramClass class1, Clazz class2)
+    {
+        return ClassUtil.internalPackageName(class1.getName()).equals(
+               ClassUtil.internalPackageName(class2.getName()));
+    }
+}
diff --git a/src/proguard/classfile/editor/AttributeAdder.java b/src/proguard/classfile/editor/AttributeAdder.java
new file mode 100644
index 0000000..805e710
--- /dev/null
+++ b/src/proguard/classfile/editor/AttributeAdder.java
@@ -0,0 +1,293 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+
+/**
+ * This AttributeVisitor adds all attributes that it visits to the given
+ * target class, class member, or attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class AttributeAdder
+implements   AttributeVisitor
+{
+    private static final int[] EMPTY_INTS = new int[0];
+
+
+    private final ProgramClass  targetClass;
+    private final ProgramMember targetMember;
+    private final Attribute     targetAttribute;
+
+    private final ConstantAdder    constantAdder    = new ConstantAdder();
+    private final AttributesEditor attributesEditor = new AttributesEditor();
+
+
+    /**
+     * Creates a new AttributeAdder that will copy attributes into the given
+     * target class.
+     */
+    public AttributeAdder(ProgramClass targetClass)
+    {
+        this(targetClass, null, null);
+    }
+
+
+    /**
+     * Creates a new AttributeAdder that will copy attributes into the given
+     * target class member.
+     */
+    public AttributeAdder(ProgramClass  targetClass,
+                          ProgramMember targetMember)
+    {
+        this(targetClass, targetMember, null);
+    }
+
+
+    /**
+     * Creates a new AttributeAdder that will copy attributes into the given
+     * target attribute.
+     */
+    public AttributeAdder(ProgramClass  targetClass,
+                          ProgramMember targetMember,
+                          Attribute     targetAttribute)
+    {
+        this.targetClass     = targetClass;
+        this.targetMember    = targetMember;
+        this.targetAttribute = targetAttribute;
+
+        constantAdder.setTargetClass(targetClass);
+    }
+
+
+    // Implementations for AttibuteVisitor.
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        // Create a new exceptions attribute.
+        ExceptionsAttribute newExceptionsAttribute = new ExceptionsAttribute();
+
+        // Make sure the name is set in the constant pool.
+        clazz.constantPoolEntryAccept(exceptionsAttribute.u2attributeNameIndex,
+                                      constantAdder);
+
+        newExceptionsAttribute.u2attributeNameIndex = constantAdder.getConstantIndex();
+
+        // Start with an empty exception index table.
+        newExceptionsAttribute.u2exceptionIndexTableLength = 0;
+        newExceptionsAttribute.u2exceptionIndexTable       = EMPTY_INTS;
+
+        // Add the exceptions.
+        exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz,
+                                                   new ExceptionAdder(targetClass,
+                                                                      newExceptionsAttribute));
+
+        // Add the completed exceptions attribute.
+        attributesEditor.addAttribute(targetClass,
+                                      (ProgramMethod)targetMember,
+                                      newExceptionsAttribute);
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        // TODO: Implement method.
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // TODO: Implement method.
+    }
+}
diff --git a/src/proguard/classfile/editor/AttributeSorter.java b/src/proguard/classfile/editor/AttributeSorter.java
new file mode 100644
index 0000000..0e24a75
--- /dev/null
+++ b/src/proguard/classfile/editor/AttributeSorter.java
@@ -0,0 +1,89 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+/**
+ * This ClassVisitor sorts the attributes of the classes that it visits.
+ * The sorting order is based on the types of the attributes.
+ *
+ * @author Eric Lafortune
+ */
+public class AttributeSorter
+extends      SimplifiedVisitor
+implements   ClassVisitor, MemberVisitor, AttributeVisitor, Comparator
+{
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Sort the attributes.
+        Arrays.sort(programClass.attributes, 0, programClass.u2attributesCount, this);
+
+        // Sort the attributes of the class members.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        // Sort the attributes.
+        Arrays.sort(programMember.attributes, 0, programMember.u2attributesCount, this);
+
+        // Sort the attributes of the attributes.
+        programMember.attributesAccept(programClass, this);
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Sort the attributes.
+        Arrays.sort(codeAttribute.attributes, 0, codeAttribute.u2attributesCount, this);
+    }
+
+
+    // Implementations for Comparator.
+
+    public int compare(Object object1, Object object2)
+    {
+        Attribute attribute1 = (Attribute)object1;
+        Attribute attribute2 = (Attribute)object2;
+
+        return attribute1.u2attributeNameIndex < attribute2.u2attributeNameIndex ? -1 :
+               attribute1.u2attributeNameIndex > attribute2.u2attributeNameIndex ?  1 :
+                                                                                    0;
+    }
+}
diff --git a/src/proguard/classfile/editor/AttributesEditor.java b/src/proguard/classfile/editor/AttributesEditor.java
new file mode 100644
index 0000000..3eb789c
--- /dev/null
+++ b/src/proguard/classfile/editor/AttributesEditor.java
@@ -0,0 +1,270 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+
+/**
+ * This class can add and delete attributes to and from classes, fields,
+ * methods, and code attributes. Attributes to be added must be filled out
+ * beforehand, including their references to the constant pool. Existing
+ * attributes of the same type are always replaced.
+ *
+ * @author Eric Lafortune
+ */
+public class AttributesEditor
+{
+    /**
+     * Adds the given attribute to the given class.
+     */
+    public void addAttribute(ProgramClass programClass,
+                             Attribute    attribute)
+    {
+        // Try to replace an existing attribute.
+        if (!replaceAttribute(programClass,
+                              programClass.u2attributesCount,
+                              programClass.attributes,
+                              attribute))
+        {
+            // Otherwise append the attribute.
+            programClass.attributes =
+                appendAttribute(programClass.u2attributesCount,
+                                programClass.attributes,
+                                attribute);
+
+            programClass.u2attributesCount++;
+        }
+    }
+
+
+    /**
+     * Adds the given attribute to the given field.
+     */
+    public void addAttribute(ProgramClass programClass,
+                             ProgramField programField,
+                             Attribute    attribute)
+    {
+        // Try to replace an existing attribute.
+        if (!replaceAttribute(programClass,
+                              programField.u2attributesCount,
+                              programField.attributes,
+                              attribute))
+        {
+            // Otherwise append the attribute.
+            programField.attributes =
+                appendAttribute(programField.u2attributesCount,
+                                programField.attributes,
+                                attribute);
+
+            programField.u2attributesCount++;
+        }
+    }
+
+
+    /**
+     * Adds the given attribute to the given method.
+     */
+    public void addAttribute(ProgramClass  programClass,
+                             ProgramMethod programMethod,
+                             Attribute     attribute)
+    {
+        // Try to replace an existing attribute.
+        if (!replaceAttribute(programClass,
+                              programMethod.u2attributesCount,
+                              programMethod.attributes,
+                              attribute))
+        {
+            // Otherwise append the attribute.
+            programMethod.attributes =
+                appendAttribute(programMethod.u2attributesCount,
+                                programMethod.attributes,
+                                attribute);
+
+            programMethod.u2attributesCount++;
+        }
+    }
+
+
+    /**
+     * Adds the given attribute to the given code attribute.
+     */
+    public void addAttribute(ProgramClass  programClass,
+                             ProgramMethod programMethod,
+                             CodeAttribute codeAttribute,
+                             Attribute     attribute)
+    {
+        // Try to replace an existing attribute.
+        if (!replaceAttribute(programClass,
+                              codeAttribute.u2attributesCount,
+                              codeAttribute.attributes,
+                              attribute))
+        {
+            // Otherwise append the attribute.
+            codeAttribute.attributes =
+                appendAttribute(codeAttribute.u2attributesCount,
+                                codeAttribute.attributes,
+                                attribute);
+
+            codeAttribute.u2attributesCount++;
+        }
+    }
+
+
+    /**
+     * Deletes the given attribute from the given class.
+     */
+    public void deleteAttribute(ProgramClass programClass,
+                                String       attributeName)
+    {
+        programClass.u2attributesCount =
+            deleteAttribute(programClass,
+                            programClass.u2attributesCount,
+                            programClass.attributes,
+                            attributeName);
+    }
+
+
+    /**
+     * Deletes the given attribute from the given field.
+     */
+    public void deleteAttribute(ProgramClass programClass,
+                                ProgramField programField,
+                                String       attributeName)
+    {
+        programField.u2attributesCount =
+            deleteAttribute(programClass,
+                            programField.u2attributesCount,
+                            programField.attributes,
+                            attributeName);
+    }
+
+
+    /**
+     * Deletes the given attribute from the given method.
+     */
+    public void deleteAttribute(ProgramClass  programClass,
+                                ProgramMethod programMethod,
+                                String        attributeName)
+    {
+        programMethod.u2attributesCount =
+            deleteAttribute(programClass,
+                            programMethod.u2attributesCount,
+                            programMethod.attributes,
+                            attributeName);
+    }
+
+
+    /**
+     * Deletes the given attribute from the given code attribute.
+     */
+    public void deleteAttribute(ProgramClass  programClass,
+                                ProgramMethod programMethod,
+                                CodeAttribute codeAttribute,
+                                String        attributeName)
+    {
+        codeAttribute.u2attributesCount =
+            deleteAttribute(programClass,
+                            codeAttribute.u2attributesCount,
+                            codeAttribute.attributes,
+                            attributeName);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Tries put the given attribute in place of an existing attribute of
+     * the same type.
+     */
+    private boolean replaceAttribute(Clazz       clazz,
+                                     int         attributesCount,
+                                     Attribute[] attributes,
+                                     Attribute   attribute)
+    {
+        String attributeName = attribute.getAttributeName(clazz);
+
+        for (int index = 0; index < attributesCount; index++)
+        {
+            if (attributes[index].getAttributeName(clazz).equals(attributeName))
+            {
+                attributes[index] = attribute;
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Appends the given attribute to the given array of attributes, creating
+     * a new array if necessary.
+     */
+    private Attribute[] appendAttribute(int         attributesCount,
+                                        Attribute[] attributes,
+                                        Attribute   attribute)
+    {
+        // Is the array too small to contain the additional attribute?
+        if (attributes.length <= attributesCount)
+        {
+            // Create a new array and copy the attributes into it.
+            Attribute[] newAttributes = new Attribute[attributesCount + 1];
+            System.arraycopy(attributes, 0, newAttributes, 0, attributesCount);
+            attributes = newAttributes;
+        }
+
+        // Append the attribute.
+        attributes[attributesCount] = attribute;
+
+        return attributes;
+    }
+
+
+    /**
+     * Deletes attributes with the given name, and returns the new number of
+     * attributes.
+     */
+    private int deleteAttribute(Clazz       clazz,
+                                int         attributesCount,
+                                Attribute[] attributes,
+                                String      attributeName)
+    {
+        int newIndex = 0;
+
+        // Shift the other attributes in the array.
+        for (int index = 0; index < attributesCount; index++)
+        {
+            if (!attributes[index].getAttributeName(clazz).equals(attributeName))
+            {
+                attributes[newIndex++] = attributes[index];
+            }
+        }
+
+        // Clear the remaining entries in the array.
+        for (int index = newIndex; index < attributesCount; index++)
+        {
+            attributes[index] = null;
+        }
+
+        return newIndex;
+    }
+}
diff --git a/src/proguard/classfile/editor/ClassElementSorter.java b/src/proguard/classfile/editor/ClassElementSorter.java
new file mode 100644
index 0000000..dccf241
--- /dev/null
+++ b/src/proguard/classfile/editor/ClassElementSorter.java
@@ -0,0 +1,52 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.ProgramClass;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This ClassVisitor sorts the various elements of the classes that it visits:
+ * interfaces, constants, fields, methods, and attributes.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassElementSorter
+extends      SimplifiedVisitor
+implements   ClassVisitor
+{
+    private final ClassVisitor interfaceSorter    = new InterfaceSorter();
+    private final ClassVisitor constantPoolSorter = new ConstantPoolSorter();
+//  private ClassVisitor classMemberSorter  = new ClassMemberSorter();
+    private final ClassVisitor attributeSorter    = new AttributeSorter();
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        programClass.accept(constantPoolSorter);
+        programClass.accept(interfaceSorter);
+//      programClass.accept(classMemberSorter);
+        programClass.accept(attributeSorter);
+    }
+}
diff --git a/src/proguard/classfile/editor/ClassFileReferenceFixer.java b/src/proguard/classfile/editor/ClassFileReferenceFixer.java
deleted file mode 100644
index b9c0ffd..0000000
--- a/src/proguard/classfile/editor/ClassFileReferenceFixer.java
+++ /dev/null
@@ -1,542 +0,0 @@
-/* $Id: ClassFileReferenceFixer.java,v 1.4.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.editor;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassFileVisitor fixes constant pool references to classes whose
- * names have changed. Descriptors of member references are not updated yet.
- *
- * @see MemberReferenceFixer
- * @author Eric Lafortune
- */
-public class ClassFileReferenceFixer
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
-             LocalVariableInfoVisitor,
-             LocalVariableTypeInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor
-{
-    private boolean ensureUniqueMemberNames;
-
-
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
-
-
-    /**
-     * Creates a new ClassFileReferenceFixer.
-     * @param ensureUniqueMemberNames specifies whether class members whose
-     *                                descriptor changes should get new, unique
-     *                                names, in order to avoid naming conflicts
-     *                                with similar methods.
-     */
-    public ClassFileReferenceFixer(boolean ensureUniqueMemberNames)
-    {
-        this.ensureUniqueMemberNames = ensureUniqueMemberNames;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Fix the constant pool.
-        programClassFile.constantPoolEntriesAccept(this);
-
-        // Fix class members.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        // Fix the attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Fix class members.
-        libraryClassFile.fieldsAccept(this);
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        // Has the descriptor changed?
-        String descriptor    = programFieldInfo.getDescriptor(programClassFile);
-        String newDescriptor = newDescriptor(descriptor,
-                                             programFieldInfo.referencedClassFile);
-
-        if (!descriptor.equals(newDescriptor))
-        {
-            // Update the descriptor.
-            programFieldInfo.u2descriptorIndex =
-                constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
-
-            // Update the name, if requested.
-            if (ensureUniqueMemberNames)
-            {
-                String name    = programFieldInfo.getName(programClassFile);
-                String newName = newUniqueMemberName(name, descriptor);
-                programFieldInfo.u2nameIndex =
-                    constantPoolEditor.addUtf8CpInfo(programClassFile, newName);
-            }
-        }
-
-        // Fix the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Has the descriptor changed?
-        String descriptor    = programMethodInfo.getDescriptor(programClassFile);
-        String newDescriptor = newDescriptor(descriptor,
-                                             programMethodInfo.referencedClassFiles);
-
-        if (!descriptor.equals(newDescriptor))
-        {
-            // Update the descriptor.
-            programMethodInfo.u2descriptorIndex =
-                constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
-
-            // Update the name, if requested.
-            if (ensureUniqueMemberNames)
-            {
-                String name    = programMethodInfo.getName(programClassFile);
-                String newName = newUniqueMemberName(name, descriptor);
-                programMethodInfo.u2nameIndex =
-                    constantPoolEditor.addUtf8CpInfo(programClassFile, newName);
-            }
-        }
-
-        // Fix the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Has the descriptor changed?
-        String descriptor    = libraryFieldInfo.getDescriptor(libraryClassFile);
-        String newDescriptor = newDescriptor(descriptor,
-                                             libraryFieldInfo.referencedClassFile);
-
-        // Update the descriptor.
-        libraryFieldInfo.descriptor = newDescriptor;
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Has the descriptor changed?
-        String descriptor    = libraryMethodInfo.getDescriptor(libraryClassFile);
-        String newDescriptor = newDescriptor(descriptor,
-                                             libraryMethodInfo.referencedClassFiles);
-
-        // Update the descriptor.
-        libraryMethodInfo.descriptor = newDescriptor;
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        // Does the string refer to a class file, due to a Class.forName construct?
-        ClassFile referencedClassFile = stringCpInfo.referencedClassFile;
-        if (referencedClassFile != null)
-        {
-            // Reconstruct the new class name.
-            String externalClassName    = stringCpInfo.getString(classFile);
-            String internalClassName    = ClassUtil.internalClassName(externalClassName);
-            String newInternalClassName = newClassName(internalClassName,
-                                                       referencedClassFile);
-
-            // Update the String entry if required.
-            if (!newInternalClassName.equals(internalClassName))
-            {
-                String newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
-
-                // Refer to a new Utf8 entry.
-                stringCpInfo.u2stringIndex =
-                    constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                     newExternalClassName);
-            }
-        }
-    }
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        // Do we know the referenced class file?
-        ClassFile referencedClassFile = classCpInfo.referencedClassFile;
-        if (referencedClassFile != null)
-        {
-            // Has the class name changed?
-            String className    = classCpInfo.getName(classFile);
-            String newClassName = newClassName(className, referencedClassFile);
-            if (!className.equals(newClassName))
-            {
-                // Refer to a new Utf8 entry.
-                classCpInfo.u2nameIndex =
-                    constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                     newClassName);
-            }
-        }
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Fix the attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        // Fix the types of the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        // Fix the signatures of the local variables.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        // Compute the new signature.
-        String signature    = classFile.getCpString(signatureAttrInfo.u2signatureIndex);
-        String newSignature = newDescriptor(signature,
-                                            signatureAttrInfo.referencedClassFiles);
-
-        if (!signature.equals(newSignature))
-        {
-            signatureAttrInfo.u2signatureIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newSignature);
-        }
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        // Fix the annotation.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
-    }
-
-
-    // Implementations for LocalVariableInfoVisitor.
-
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
-    {
-        // Has the descriptor changed?
-        String descriptor    = classFile.getCpString(localVariableInfo.u2descriptorIndex);
-        String newDescriptor = newDescriptor(descriptor,
-                                             localVariableInfo.referencedClassFile);
-
-        if (!descriptor.equals(newDescriptor))
-        {
-            // Refer to a new Utf8 entry.
-            localVariableInfo.u2descriptorIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newDescriptor);
-        }
-    }
-
-
-    // Implementations for LocalVariableTypeInfoVisitor.
-
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
-    {
-        // Has the signature changed?
-        String signature    = classFile.getCpString(localVariableTypeInfo.u2signatureIndex);
-        String newSignature = newDescriptor(signature,
-                                            localVariableTypeInfo.referencedClassFiles);
-
-        if (!signature.equals(newSignature))
-        {
-            localVariableTypeInfo.u2signatureIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newSignature);
-        }
-    }
-
-
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
-    {
-        // Compute the new type name.
-        String typeName    = classFile.getCpString(annotation.u2typeIndex);
-        String newTypeName = newDescriptor(typeName,
-                                           annotation.referencedClassFiles);
-
-        if (!typeName.equals(newTypeName))
-        {
-            // Refer to a new Utf8 entry.
-            annotation.u2typeIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newTypeName);
-        }
-
-        // Fix the element values.
-        annotation.elementValuesAccept(classFile, this);
-    }
-
-
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
-    {
-    }
-
-
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        // Compute the new type name.
-        String typeName    = classFile.getCpString(enumConstantElementValue.u2typeNameIndex);
-        String newTypeName = newDescriptor(typeName,
-                                           enumConstantElementValue.referencedClassFiles);
-
-        if (!typeName.equals(newTypeName))
-        {
-            // Refer to a new Utf8 entry.
-            enumConstantElementValue.u2typeNameIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newTypeName);
-        }
-    }
-
-
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
-    {
-        // Compute the new class name.
-        String className    = classFile.getCpString(classElementValue.u2classInfoIndex);
-        String newClassName = newDescriptor(className,
-                                            classElementValue.referencedClassFiles);
-
-        if (!className.equals(newClassName))
-        {
-            // Refer to a new Utf8 entry.
-            classElementValue.u2classInfoIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newClassName);
-        }
-    }
-
-
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
-    {
-        // Fix the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
-    }
-
-
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        // Fix the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-    }
-
-
-    // Small utility methods.
-
-    private static String newDescriptor(String    descriptor,
-                                        ClassFile referencedClassFile)
-    {
-        // If there is no referenced class, the descriptor won't change.
-        if (referencedClassFile == null)
-        {
-            return descriptor;
-        }
-
-        // Unravel and reconstruct the class element of the descriptor.
-        DescriptorClassEnumeration descriptorClassEnumeration =
-            new DescriptorClassEnumeration(descriptor);
-
-        StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
-        newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
-
-        // Only if the descriptor contains a class name (e.g. with an array of
-        // primitive types), the descriptor can change.
-        if (descriptorClassEnumeration.hasMoreClassNames())
-        {
-            String className = descriptorClassEnumeration.nextClassName();
-            String fluff     = descriptorClassEnumeration.nextFluff();
-
-            String newClassName = newClassName(className,
-                                               referencedClassFile);
-
-            newDescriptorBuffer.append(newClassName);
-            newDescriptorBuffer.append(fluff);
-        }
-
-        return newDescriptorBuffer.toString();
-    }
-
-
-    private static String newDescriptor(String      descriptor,
-                                        ClassFile[] referencedClassFiles)
-    {
-        // If there are no referenced classes, the descriptor won't change.
-        if (referencedClassFiles == null ||
-            referencedClassFiles.length == 0)
-        {
-            return descriptor;
-        }
-
-        // Unravel and reconstruct the class elements of the descriptor.
-        DescriptorClassEnumeration descriptorClassEnumeration =
-            new DescriptorClassEnumeration(descriptor);
-
-        StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
-        newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
-
-        int index = 0;
-        while (descriptorClassEnumeration.hasMoreClassNames())
-        {
-            String className = descriptorClassEnumeration.nextClassName();
-            String fluff     = descriptorClassEnumeration.nextFluff();
-
-            String newClassName = newClassName(className,
-                                               referencedClassFiles[index++]);
-
-            newDescriptorBuffer.append(newClassName);
-            newDescriptorBuffer.append(fluff);
-        }
-
-        return newDescriptorBuffer.toString();
-    }
-
-
-    /**
-     * Returns a new unique class member name, based on the given name and
-     * descriptor.
-     */
-    private String newUniqueMemberName(String name, String descriptor)
-    {
-        // TODO: Avoid duplicate constructors.
-        return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
-            ClassConstants.INTERNAL_METHOD_NAME_INIT :
-            name + '$' + Long.toHexString(Math.abs((descriptor).hashCode()));
-    }
-
-
-    /**
-     * Returns the new class name based on the given class name and the new
-     * name of the given referenced class file. Class names of array types
-     * are handled properly.
-     */
-    private static String newClassName(String    className,
-                                       ClassFile referencedClassFile)
-    {
-        // If there is no referenced class, the class name won't change.
-        if (referencedClassFile == null)
-        {
-            return className;
-        }
-
-        // Reconstruct the class name.
-        String newClassName = referencedClassFile.getName();
-
-        // Is it an array type?
-        if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
-        {
-            // Add the array prefixes and suffix "[L...;".
-            newClassName =
-                 className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
-                 newClassName +
-                 ClassConstants.INTERNAL_TYPE_CLASS_END;
-        }
-
-        return newClassName;
-    }
-}
diff --git a/src/proguard/classfile/editor/ClassMemberSorter.java b/src/proguard/classfile/editor/ClassMemberSorter.java
new file mode 100644
index 0000000..483f184
--- /dev/null
+++ b/src/proguard/classfile/editor/ClassMemberSorter.java
@@ -0,0 +1,69 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.visitor.ClassVisitor;
+
+import java.util.*;
+
+/**
+ * This ClassVisitor sorts the class members of the classes that it visits.
+ * The sorting order is based on the access flags, the names, and the
+ * descriptors.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassMemberSorter implements ClassVisitor, Comparator
+{
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Sort the fields.
+        Arrays.sort(programClass.fields, 0, programClass.u2fieldsCount, this);
+
+        // Sort the methods.
+        Arrays.sort(programClass.methods, 0, programClass.u2methodsCount, this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+    }
+
+
+    // Implementations for Comparator.
+
+    public int compare(Object object1, Object object2)
+    {
+        ProgramMember member1 = (ProgramMember)object1;
+        ProgramMember member2 = (ProgramMember)object2;
+
+        return member1.u2accessFlags     < member2.u2accessFlags     ? -1 :
+               member1.u2accessFlags     > member2.u2accessFlags     ?  1 :
+               member1.u2nameIndex       < member2.u2nameIndex       ? -1 :
+               member1.u2nameIndex       > member2.u2nameIndex       ?  1 :
+               member1.u2descriptorIndex < member2.u2descriptorIndex ? -1 :
+               member1.u2descriptorIndex > member2.u2descriptorIndex ?  1 :
+                                                                        0;
+    }
+}
diff --git a/src/proguard/classfile/editor/ClassReferenceFixer.java b/src/proguard/classfile/editor/ClassReferenceFixer.java
new file mode 100644
index 0000000..029da5b
--- /dev/null
+++ b/src/proguard/classfile/editor/ClassReferenceFixer.java
@@ -0,0 +1,548 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassVisitor fixes references of constant pool entries, fields,
+ * methods, and attributes to classes whose names have changed. Descriptors
+ * of member references are not updated yet.
+ *
+ * @see MemberReferenceFixer
+ * @author Eric Lafortune
+ */
+public class ClassReferenceFixer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
+             InnerClassesInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private final boolean ensureUniqueMemberNames;
+
+
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+
+
+    /**
+     * Creates a new ClassReferenceFixer.
+     * @param ensureUniqueMemberNames specifies whether class members whose
+     *                                descriptor changes should get new, unique
+     *                                names, in order to avoid naming conflicts
+     *                                with similar methods.
+     */
+    public ClassReferenceFixer(boolean ensureUniqueMemberNames)
+    {
+        this.ensureUniqueMemberNames = ensureUniqueMemberNames;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Fix the constant pool.
+        programClass.constantPoolEntriesAccept(this);
+
+        // Fix class members.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+
+        // Fix the attributes.
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Fix class members.
+        libraryClass.fieldsAccept(this);
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Has the descriptor changed?
+        String descriptor    = programField.getDescriptor(programClass);
+        String newDescriptor = newDescriptor(descriptor,
+                                             programField.referencedClass);
+
+        if (!descriptor.equals(newDescriptor))
+        {
+            // Update the descriptor.
+            programField.u2descriptorIndex =
+                constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
+
+            // Update the name, if requested.
+            if (ensureUniqueMemberNames)
+            {
+                String name    = programField.getName(programClass);
+                String newName = newUniqueMemberName(name, descriptor);
+                programField.u2nameIndex =
+                    constantPoolEditor.addUtf8Constant(programClass, newName);
+            }
+        }
+
+        // Fix the attributes.
+        programField.attributesAccept(programClass, this);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Has the descriptor changed?
+        String descriptor    = programMethod.getDescriptor(programClass);
+        String newDescriptor = newDescriptor(descriptor,
+                                             programMethod.referencedClasses);
+
+        if (!descriptor.equals(newDescriptor))
+        {
+            // Update the descriptor.
+            programMethod.u2descriptorIndex =
+                constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
+
+            // Update the name, if requested.
+            if (ensureUniqueMemberNames)
+            {
+                String name    = programMethod.getName(programClass);
+                String newName = newUniqueMemberName(name, descriptor);
+                programMethod.u2nameIndex =
+                    constantPoolEditor.addUtf8Constant(programClass, newName);
+            }
+        }
+
+        // Fix the attributes.
+        programMethod.attributesAccept(programClass, this);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        // Has the descriptor changed?
+        String descriptor    = libraryField.getDescriptor(libraryClass);
+        String newDescriptor = newDescriptor(descriptor,
+                                             libraryField.referencedClass);
+
+        // Update the descriptor.
+        libraryField.descriptor = newDescriptor;
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        // Has the descriptor changed?
+        String descriptor    = libraryMethod.getDescriptor(libraryClass);
+        String newDescriptor = newDescriptor(descriptor,
+                                             libraryMethod.referencedClasses);
+
+        // Update the descriptor.
+        libraryMethod.descriptor = newDescriptor;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Does the string refer to a class, due to a Class.forName construct?
+        Clazz  referencedClass  = stringConstant.referencedClass;
+        Member referencedMember = stringConstant.referencedMember;
+        if (referencedClass  != null &&
+            referencedMember == null)
+        {
+            // Reconstruct the new class name.
+            String externalClassName    = stringConstant.getString(clazz);
+            String internalClassName    = ClassUtil.internalClassName(externalClassName);
+            String newInternalClassName = newClassName(internalClassName,
+                                                       referencedClass);
+
+            // Update the String entry if required.
+            if (!newInternalClassName.equals(internalClassName))
+            {
+                String newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
+
+                // Refer to a new Utf8 entry.
+                stringConstant.u2stringIndex =
+                    constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                       newExternalClassName);
+            }
+        }
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Do we know the referenced class?
+        Clazz referencedClass = classConstant.referencedClass;
+        if (referencedClass != null)
+        {
+            // Has the class name changed?
+            String className    = classConstant.getName(clazz);
+            String newClassName = newClassName(className, referencedClass);
+            if (!className.equals(newClassName))
+            {
+                // Refer to a new Utf8 entry.
+                classConstant.u2nameIndex =
+                    constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                       newClassName);
+            }
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        // Fix the inner class names.
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Fix the attributes.
+        codeAttribute.attributesAccept(clazz, method, this);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Fix the types of the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Fix the signatures of the local variables.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        // Compute the new signature.
+        String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
+        String newSignature = newDescriptor(signature,
+                                            signatureAttribute.referencedClasses);
+
+        if (!signature.equals(newSignature))
+        {
+            signatureAttribute.u2signatureIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newSignature);
+        }
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Fix the annotations.
+        annotationsAttribute.annotationsAccept(clazz, this);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Fix the annotations.
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // Fix the annotation.
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+    {
+        // Fix the inner class name.
+        int innerClassIndex = innerClassesInfo.u2innerClassIndex;
+        int innerNameIndex  = innerClassesInfo.u2innerNameIndex;
+        if (innerClassIndex != 0 &&
+            innerNameIndex  != 0)
+        {
+            String newInnerName = clazz.getClassName(innerClassIndex);
+            int index = newInnerName.lastIndexOf(ClassConstants.INNER_CLASS_SEPARATOR);
+            if (index >= 0)
+            {
+                innerClassesInfo.u2innerNameIndex =
+                    constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                       newInnerName.substring(index + 1));
+            }
+        }
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        // Has the descriptor changed?
+        String descriptor    = clazz.getString(localVariableInfo.u2descriptorIndex);
+        String newDescriptor = newDescriptor(descriptor,
+                                             localVariableInfo.referencedClass);
+
+        if (!descriptor.equals(newDescriptor))
+        {
+            // Refer to a new Utf8 entry.
+            localVariableInfo.u2descriptorIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newDescriptor);
+        }
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Has the signature changed?
+        String signature    = clazz.getString(localVariableTypeInfo.u2signatureIndex);
+        String newSignature = newDescriptor(signature,
+                                            localVariableTypeInfo.referencedClasses);
+
+        if (!signature.equals(newSignature))
+        {
+            localVariableTypeInfo.u2signatureIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newSignature);
+        }
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        // Compute the new type name.
+        String typeName    = clazz.getString(annotation.u2typeIndex);
+        String newTypeName = newDescriptor(typeName,
+                                           annotation.referencedClasses);
+
+        if (!typeName.equals(newTypeName))
+        {
+            // Refer to a new Utf8 entry.
+            annotation.u2typeIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newTypeName);
+        }
+
+        // Fix the element values.
+        annotation.elementValuesAccept(clazz, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        // Compute the new type name.
+        String typeName    = clazz.getString(enumConstantElementValue.u2typeNameIndex);
+        String newTypeName = newDescriptor(typeName,
+                                           enumConstantElementValue.referencedClasses);
+
+        if (!typeName.equals(newTypeName))
+        {
+            // Refer to a new Utf8 entry.
+            enumConstantElementValue.u2typeNameIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newTypeName);
+        }
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        // Compute the new class name.
+        String className    = clazz.getString(classElementValue.u2classInfoIndex);
+        String newClassName = newDescriptor(className,
+                                            classElementValue.referencedClasses);
+
+        if (!className.equals(newClassName))
+        {
+            // Refer to a new Utf8 entry.
+            classElementValue.u2classInfoIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newClassName);
+        }
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        // Fix the annotation.
+        annotationElementValue.annotationAccept(clazz, this);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        // Fix the element values.
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+    }
+
+
+    // Small utility methods.
+
+    private static String newDescriptor(String descriptor,
+                                        Clazz  referencedClass)
+    {
+        // If there is no referenced class, the descriptor won't change.
+        if (referencedClass == null)
+        {
+            return descriptor;
+        }
+
+        // Unravel and reconstruct the class element of the descriptor.
+        DescriptorClassEnumeration descriptorClassEnumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
+        newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
+
+        // Only if the descriptor contains a class name (e.g. with an array of
+        // primitive types), the descriptor can change.
+        if (descriptorClassEnumeration.hasMoreClassNames())
+        {
+            String className = descriptorClassEnumeration.nextClassName();
+            String fluff     = descriptorClassEnumeration.nextFluff();
+
+            String newClassName = newClassName(className,
+                                               referencedClass);
+
+            newDescriptorBuffer.append(newClassName);
+            newDescriptorBuffer.append(fluff);
+        }
+
+        return newDescriptorBuffer.toString();
+    }
+
+
+    private static String newDescriptor(String  descriptor,
+                                        Clazz[] referencedClasses)
+    {
+        // If there are no referenced classes, the descriptor won't change.
+        if (referencedClasses == null ||
+            referencedClasses.length == 0)
+        {
+            return descriptor;
+        }
+
+        // Unravel and reconstruct the class elements of the descriptor.
+        DescriptorClassEnumeration descriptorClassEnumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
+        newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
+
+        int index = 0;
+        while (descriptorClassEnumeration.hasMoreClassNames())
+        {
+            String className = descriptorClassEnumeration.nextClassName();
+            String fluff     = descriptorClassEnumeration.nextFluff();
+
+            String newClassName = newClassName(className,
+                                               referencedClasses[index++]);
+
+            newDescriptorBuffer.append(newClassName);
+            newDescriptorBuffer.append(fluff);
+        }
+
+        return newDescriptorBuffer.toString();
+    }
+
+
+    /**
+     * Returns a unique class member name, based on the given name and descriptor.
+     */
+    private String newUniqueMemberName(String name, String descriptor)
+    {
+        // TODO: Avoid duplicate constructors.
+        return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
+            ClassConstants.INTERNAL_METHOD_NAME_INIT :
+            name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
+    }
+
+
+    /**
+     * Returns the new class name based on the given class name and the new
+     * name of the given referenced class. Class names of array types
+     * are handled properly.
+     */
+    private static String newClassName(String className,
+                                       Clazz  referencedClass)
+    {
+        // If there is no referenced class, the class name won't change.
+        if (referencedClass == null)
+        {
+            return className;
+        }
+
+        // Reconstruct the class name.
+        String newClassName = referencedClass.getName();
+
+        // Is it an array type?
+        if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
+        {
+            // Add the array prefixes and suffix "[L...;".
+            newClassName =
+                 className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
+                 newClassName +
+                 ClassConstants.INTERNAL_TYPE_CLASS_END;
+        }
+
+        return newClassName;
+    }
+}
diff --git a/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java b/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java
deleted file mode 100644
index d1d4200..0000000
--- a/src/proguard/classfile/editor/CodeAttrInfoEditorResetter.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/* $Id: CodeAttrInfoEditorResetter.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.editor;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This AttrInfoVisitor resets it CodeAttrInfoEditor whenever it visits a
- * code attribute.
- *
- * @author Eric Lafortune
- */
-public class CodeAttrInfoEditorResetter implements AttrInfoVisitor
-{
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-
-
-    /**
-     * Creates a new CodeAttrInfoEditorResetter.
-     * @param codeAttrInfoEditor the code attribute editor that will be reset.
-     */
-    public CodeAttrInfoEditorResetter(CodeAttrInfoEditor codeAttrInfoEditor)
-    {
-        this.codeAttrInfoEditor = codeAttrInfoEditor;
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
-    }
-}
diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java
new file mode 100644
index 0000000..716cd81
--- /dev/null
+++ b/src/proguard/classfile/editor/CodeAttributeComposer.java
@@ -0,0 +1,729 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor accumulates instructions and exceptions, and then
+ * copies them into code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeAttributeComposer
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
+             LineNumberInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    public  static       boolean DEBUG = true;
+    //*/
+
+
+    private static final int MAXIMUM_LEVELS = 32;
+
+
+    private int maximumCodeLength;
+    private int codeLength;
+    private int exceptionTableLength;
+    private int level = -1;
+
+    private byte[]  code                  = new byte[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]   oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+
+    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 ExceptionInfo[] exceptionTable   =  new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
+
+    private int expectedStackMapFrameOffset;
+
+    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
+    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
+
+
+    /**
+     * Starts a new code definition.
+     */
+    public void reset()
+    {
+        maximumCodeLength    = 0;
+        codeLength           = 0;
+        exceptionTableLength = 0;
+        level                = -1;
+    }
+
+
+    /**
+     * Starts a new code fragment. Branch instructions that are added are
+     * assumed to be relative within such code fragments.
+     * @param maximumCodeFragmentLength the maximum length of the code that will
+     *                                  be added as part of this fragment.
+     */
+    public void beginCodeFragment(int maximumCodeFragmentLength)
+    {
+        level++;
+
+        if (level >= MAXIMUM_LEVELS)
+        {
+            throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]");
+        }
+
+        // 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;
+        }
+
+        // Try to reuse the previous array for this code fragment.
+        if (instructionOffsetMap[level].length <= maximumCodeFragmentLength)
+        {
+            instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1];
+        }
+
+        // Remember the location of the code fragment.
+        codeFragmentOffsets[level] = codeLength;
+        codeFragmentLengths[level] = maximumCodeFragmentLength;
+    }
+
+
+    /**
+     * Appends the given instruction with the given old offset.
+     * @param oldInstructionOffset the old offset of the instruction, to which
+     *                             branches and other references in the current
+     *                             code fragment are pointing.
+     * @param instruction          the instruction to be appended.
+     */
+    public void appendInstruction(int         oldInstructionOffset,
+                                  Instruction instruction)
+    {
+        if (DEBUG)
+        {
+            println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
+        }
+
+        // Remember the old offset of the appended instruction.
+        oldInstructionOffsets[codeLength] = oldInstructionOffset;
+
+        // Write the instruction.
+        instruction.write(code, codeLength);
+
+        // Fill out the new offset of the appended instruction.
+        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
+
+        // Continue appending at the next instruction offset.
+        codeLength += instruction.length(codeLength);
+    }
+
+
+    /**
+     * Appends the given label with the given old offset.
+     * @param oldInstructionOffset the old offset of the label, to which
+     *                             branches and other references in the current
+     *                             code fragment are pointing.
+     */
+    public void appendLabel(int oldInstructionOffset)
+    {
+        if (DEBUG)
+        {
+            println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)");
+        }
+
+        // Fill out the new offset of the appended instruction.
+        instructionOffsetMap[level][oldInstructionOffset] = codeLength;
+    }
+
+
+    /**
+     * Appends the given exception to the exception table.
+     * @param exceptionInfo the exception to be appended.
+     */
+    public void appendException(ExceptionInfo exceptionInfo)
+    {
+        if (DEBUG)
+        {
+            print("         ", "Exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]");
+        }
+
+        // Remap the exception right away.
+        visitExceptionInfo(null, null, null, exceptionInfo);
+
+        if (DEBUG)
+        {
+            System.out.println(" -> ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]");
+        }
+
+        // Don't add the exception if its instruction range is empty.
+        if (exceptionInfo.u2startPC == exceptionInfo.u2endPC)
+        {
+            if (DEBUG)
+            {
+                println("         ", "  (not added because of empty instruction range)");
+            }
+
+            return;
+        }
+
+        // Make sure there is sufficient space in the exception table.
+        if (exceptionTable.length <= exceptionTableLength)
+        {
+            ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1];
+            System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength);
+            exceptionTable = newExceptionTable;
+        }
+
+        // Add the exception.
+        exceptionTable[exceptionTableLength++] = exceptionInfo;
+    }
+
+
+    /**
+     * Wraps up the current code fragment, continuing with the previous one on
+     * the stack.
+     */
+    public void endCodeFragment()
+    {
+        if (level < 0)
+        {
+            throw new IllegalArgumentException("Code fragment not begun ["+level+"]");
+        }
+
+        // Remap the instructions of the code fragment.
+        int instructionOffset = codeFragmentOffsets[level];
+        while (instructionOffset < codeLength)
+        {
+            // Get the next instruction.
+            Instruction instruction = InstructionFactory.create(code, instructionOffset);
+
+            // Does this instruction still have to be remapped?
+            if (oldInstructionOffsets[instructionOffset] >= 0)
+            {
+                // Adapt the instruction for its new offset.
+                instruction.accept(null, null, null, instructionOffset, this);
+
+                // Write the instruction back.
+                instruction.write(code, instructionOffset);
+
+                // Don't remap this instruction again.
+                oldInstructionOffsets[instructionOffset] = -1;
+            }
+
+            // Continue remapping at the next instruction offset.
+            instructionOffset += instruction.length(instructionOffset);
+        }
+
+        // Correct the estimated maximum code length, now that we know the
+        // actual length of this code fragment.
+        maximumCodeLength += codeLength - codeFragmentOffsets[level] -
+                             codeFragmentLengths[level];
+
+        level--;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (DEBUG)
+        {
+            System.out.println("CodeAttributeComposer: putting results in ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+        }
+
+        if (level != -1)
+        {
+            throw new IllegalArgumentException("Code fragment not ended ["+level+"]");
+        }
+
+        level++;
+
+        // Make sure the code attribute has sufficient space for the composed
+        // code.
+        if (codeAttribute.u4codeLength < codeLength)
+        {
+            codeAttribute.code = new byte[codeLength];
+        }
+
+        // Copy the composed code over into the code attribute.
+        codeAttribute.u4codeLength = codeLength;
+        System.arraycopy(code, 0, codeAttribute.code, 0, codeLength);
+
+        // Remove exceptions with empty code blocks (done before).
+        //exceptionTableLength =
+        //    removeEmptyExceptions(exceptionTable, exceptionTableLength);
+
+        // Make sure the exception table has sufficient space for the composed
+        // exceptions.
+        if (codeAttribute.exceptionTable.length < exceptionTableLength)
+        {
+            codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength];
+        }
+
+        // Copy the exception table.
+        codeAttribute.u2exceptionTableLength = exceptionTableLength;
+        System.arraycopy(exceptionTable, 0, codeAttribute.exceptionTable, 0, exceptionTableLength);
+
+        // Update the maximum stack size and local variable frame size.
+        stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+        variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Remap  the line number table and the local variable table.
+        codeAttribute.attributesAccept(clazz, method, this);
+
+        // Remap the exception table.
+        //codeAttribute.exceptionsAccept(clazz, method, this);
+
+        // Remove exceptions with empty code blocks (done before).
+        //codeAttribute.u2exceptionTableLength =
+        //    removeEmptyExceptions(codeAttribute.exceptionTable,
+        //                          codeAttribute.u2exceptionTableLength);
+
+        level--;
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        // Remap all stack map entries.
+        expectedStackMapFrameOffset = -1;
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        // Remap all stack map table entries.
+        expectedStackMapFrameOffset = 0;
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        // Remap all line number table entries.
+        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
+
+        // Remove line numbers with empty code blocks.
+        lineNumberTableAttribute.u2lineNumberTableLength =
+           removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
+                                  lineNumberTableAttribute.u2lineNumberTableLength,
+                                  codeAttribute.u4codeLength);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Remap all local variable table entries.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+
+        // Remove local variables with empty code blocks.
+        localVariableTableAttribute.u2localVariableTableLength =
+            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
+                                      localVariableTableAttribute.u2localVariableTableLength,
+                                      codeAttribute.u2maxLocals);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Remap all local variable table entries.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+
+        // Remove local variables with empty code blocks.
+        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
+            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
+                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength,
+                                          codeAttribute.u2maxLocals);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        // Adjust the branch offset.
+        branchInstruction.branchOffset = remapBranchOffset(offset,
+                                                           branchInstruction.branchOffset);
+    }
+
+
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        // Adjust the default jump offset.
+        switchInstruction.defaultOffset = remapBranchOffset(offset,
+                                                            switchInstruction.defaultOffset);
+
+        // Adjust the jump offsets.
+        remapJumpOffsets(offset,
+                         switchInstruction.jumpOffsets);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        // Remap the code offsets. Note that the instruction offset map also has
+        // an entry for the first offset after the code, for u2endPC.
+        exceptionInfo.u2startPC   = remapInstructionOffset(exceptionInfo.u2startPC);
+        exceptionInfo.u2endPC     = remapInstructionOffset(exceptionInfo.u2endPC);
+        exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
+    {
+        // Remap the stack map frame offset.
+        int stackMapFrameOffset = remapInstructionOffset(offset);
+
+        int offsetDelta = stackMapFrameOffset;
+
+        // Compute the offset delta if the frame is part of a stack map frame
+        // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
+        if (expectedStackMapFrameOffset >= 0)
+        {
+            offsetDelta -= expectedStackMapFrameOffset;
+
+            expectedStackMapFrameOffset = stackMapFrameOffset + 1;
+        }
+
+        stackMapFrame.u2offsetDelta = offsetDelta;
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
+
+        // Remap the verification type offset.
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
+
+        // Remap the verification type offsets.
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
+
+        // Remap the verification type offsets.
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
+
+
+    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+    {
+        // Remap the offset of the 'new' instruction.
+        uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
+    {
+        // Remap the code offset.
+        lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        // Remap the code offset and length.
+        // TODO: The local variable frame might not be strictly preserved.
+        int startPC = remapInstructionOffset(localVariableInfo.u2startPC);
+        int endPC   = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length);
+
+        localVariableInfo.u2startPC = startPC;
+        localVariableInfo.u2length  = endPC - startPC;
+    }
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Remap the code offset and length.
+        // TODO: The local variable frame might not be strictly preserved.
+        int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
+        int endPC   = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length);
+
+        localVariableTypeInfo.u2startPC = startPC;
+        localVariableTypeInfo.u2length  = endPC - startPC;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Adjusts the given jump offsets for the instruction at the given offset.
+     */
+    private void remapJumpOffsets(int offset, int[] jumpOffsets)
+    {
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
+        }
+    }
+
+
+    /**
+     * Computes the new branch offset for the instruction at the given new offset
+     * with the given old branch offset.
+     */
+    private int remapBranchOffset(int newInstructionOffset, int branchOffset)
+    {
+        if (newInstructionOffset < 0 ||
+            newInstructionOffset > codeLength)
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]");
+        }
+
+        int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
+
+        return remapInstructionOffset(oldInstructionOffset + branchOffset) -
+               remapInstructionOffset(oldInstructionOffset);
+    }
+
+
+    /**
+     * Computes the new instruction offset for the instruction at the given old
+     * offset.
+     */
+    private int remapInstructionOffset(int oldInstructionOffset)
+    {
+        if (oldInstructionOffset < 0 ||
+            oldInstructionOffset > codeFragmentLengths[level])
+        {
+            throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment with length ["+codeFragmentLengths[level]+"]");
+        }
+
+        return instructionOffsetMap[level][oldInstructionOffset];
+    }
+
+
+    /**
+     * Returns the given list of exceptions, without the ones that have empty
+     * code blocks.
+     */
+    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
+                                      int             exceptionInfoCount)
+    {
+        // Overwrite all empty exceptions.
+        int newIndex = 0;
+        for (int index = 0; index < exceptionInfoCount; index++)
+        {
+            ExceptionInfo exceptionInfo = exceptionInfos[index];
+            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
+            {
+                exceptionInfos[newIndex++] = exceptionInfo;
+            }
+        }
+
+        // Clear the unused array entries.
+        for (int index = newIndex; index < exceptionInfoCount; index++)
+        {
+            exceptionInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of line numbers, without the ones that have empty
+     * code blocks or that exceed the code size.
+     */
+    private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
+                                       int              lineNumberInfoCount,
+                                       int              codeLength)
+    {
+        // Overwrite all empty line number entries.
+        int newIndex = 0;
+        for (int index = 0; index < lineNumberInfoCount; index++)
+        {
+            LineNumberInfo lineNumberInfo = lineNumberInfos[index];
+            int startPC = lineNumberInfo.u2startPC;
+            if (startPC < codeLength &&
+                (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
+            {
+                lineNumberInfos[newIndex++] = lineNumberInfo;
+            }
+        }
+
+        // Clear the unused array entries.
+        for (int index = newIndex; index < lineNumberInfoCount; index++)
+        {
+            lineNumberInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of local variables, without the ones that have empty
+     * code blocks or that exceed the actual number of local variables.
+     */
+    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++)
+        {
+            LocalVariableInfo localVariableInfo = localVariableInfos[index];
+            if (localVariableInfo.u2length > 0 &&
+                localVariableInfo.u2index < maxLocals)
+            {
+                localVariableInfos[newIndex++] = localVariableInfo;
+            }
+        }
+
+        // Clear the unused array entries.
+        for (int index = newIndex; index < localVariableInfoCount; index++)
+        {
+            localVariableInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+
+
+    /**
+     * Returns the given list of local variable types, without the ones that
+     * have empty code blocks or that exceed the actual number of local variables.
+     */
+    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++)
+        {
+            LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
+            if (localVariableTypeInfo.u2length > 0 &&
+                localVariableTypeInfo.u2index < maxLocals)
+            {
+                localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
+            }
+        }
+
+        // Clear the unused array entries.
+        for (int index = newIndex; index < localVariableTypeInfoCount; index++)
+        {
+            localVariableTypeInfos[index] = null;
+        }
+
+        return newIndex;
+    }
+
+
+    private void println(String string1, String string2)
+    {
+        print(string1, string2);
+
+        System.out.println();
+    }
+
+    private void print(String string1, String string2)
+    {
+        System.out.print(string1);
+
+        for (int index = 0; index < level; index++)
+        {
+            System.out.print("  ");
+        }
+
+        System.out.print(string2);
+    }
+
+
+    public static void main(String[] args)
+    {
+        CodeAttributeComposer composer = new CodeAttributeComposer();
+
+        composer.beginCodeFragment(10);
+        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.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));
+        composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3));
+        composer.endCodeFragment();
+
+        composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN));
+        composer.endCodeFragment();
+    }
+}
diff --git a/src/proguard/classfile/editor/CodeAttrInfoEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java
similarity index 52%
rename from src/proguard/classfile/editor/CodeAttrInfoEditor.java
rename to src/proguard/classfile/editor/CodeAttributeEditor.java
index 1f53efb..9b80d77 100644
--- a/src/proguard/classfile/editor/CodeAttrInfoEditor.java
+++ b/src/proguard/classfile/editor/CodeAttributeEditor.java
@@ -1,6 +1,6 @@
-/* $Id: CodeAttrInfoEditor.java,v 1.14.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,58 +21,57 @@
 package proguard.classfile.editor;
 
 import proguard.classfile.*;
+import proguard.classfile.visitor.ClassPrinter;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
 import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttrInfoVisitor accumulates specified changes to code, and then applies
+ * This AttributeVisitor accumulates specified changes to code, and then applies
  * these accumulated changes to the code attributes that it visits.
  *
  * @author Eric Lafortune
  */
-public class CodeAttrInfoEditor
-  implements AttrInfoVisitor,
+public class CodeAttributeEditor
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
              InstructionVisitor,
              ExceptionInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
              LineNumberInfoVisitor,
              LocalVariableInfoVisitor,
              LocalVariableTypeInfoVisitor
 {
-    private int              codeLength;
-    private boolean          modified;
-    private boolean          inserted;
-
-    /*private*/public Instruction[]    preInsertions;
-    /*private*/public Instruction[]    replacements;
-    /*private*/public Instruction[]    postInsertions;
-    private boolean[]        deleted;
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
 
-    private int[]   instructionOffsetMap;
-    private int     newOffset;
-    private boolean lengthIncreased;
 
-    private StackSizeUpdater  stackSizeUpdater;
-    private InstructionWriter instructionWriter = new InstructionWriter();
+    private int     codeLength;
+    private boolean modified;
+    private boolean simple;
 
+    /*private*/public Instruction[]    preInsertions  = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
+    /*private*/public Instruction[]    replacements   = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
+    /*private*/public Instruction[]    postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
+    private boolean[]        deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
 
-    /**
-     * Creates a new CodeAttrInfoEditor.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public CodeAttrInfoEditor(int codeLength)
-    {
-        this.codeLength = codeLength;
+    private int[]   instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int     newOffset;
+    private boolean lengthIncreased;
 
-        preInsertions    = new Instruction[codeLength];
-        replacements     = new Instruction[codeLength];
-        postInsertions   = new Instruction[codeLength];
-        deleted          = new boolean[codeLength];
+    private int expectedStackMapFrameOffset;
 
-        stackSizeUpdater = new StackSizeUpdater(codeLength);
-    }
+    private final StackSizeUpdater    stackSizeUpdater    = new StackSizeUpdater();
+    private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
+    private final InstructionWriter   instructionWriter   = new InstructionWriter();
 
 
     /**
@@ -103,7 +102,8 @@ public class CodeAttrInfoEditor
         }
 
         modified = false;
-        inserted = false;
+        simple   = true;
+
     }
 
 
@@ -115,6 +115,11 @@ public class CodeAttrInfoEditor
      */
     public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
     {
+        if (DEBUG)
+        {
+            System.out.println("Inserting instruction before ["+instructionOffset+"]: "+instruction);
+        }
+
         if (instructionOffset < 0 ||
             instructionOffset >= codeLength)
         {
@@ -124,7 +129,8 @@ public class CodeAttrInfoEditor
         preInsertions[instructionOffset] = instruction;
 
         modified = true;
-        inserted = true;
+        simple   = false;
+
     }
 
 
@@ -165,7 +171,7 @@ public class CodeAttrInfoEditor
         postInsertions[instructionOffset] = instruction;
 
         modified = true;
-        inserted = true;
+        simple   = false;
     }
 
 
@@ -184,7 +190,7 @@ public class CodeAttrInfoEditor
         deleted[instructionOffset] = true;
 
         modified = true;
-        inserted = true;
+        simple   = false;
     }
 
 
@@ -217,36 +223,48 @@ public class CodeAttrInfoEditor
     }
 
 
-    /**
-     * Returns whether any instruction has been modified in any way.
-     */
-    public boolean isModified()
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        return modified;
-    }
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
 
+        // TODO: Remove this when the code has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
+        {
+            System.err.println("Unexpected error while editing code:");
+            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()+")");
 
-    // Implementations for AttrInfoVisitor.
+            if (DEBUG)
+            {
+                method.accept(clazz, new ClassPrinter());
+            }
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+            throw ex;
+        }
+    }
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
+        if (DEBUG)
+        {
+            System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]");
+        }
+
         // Avoid doing any work if nothing is changing anyway.
         if (!modified)
         {
@@ -254,92 +272,112 @@ public class CodeAttrInfoEditor
         }
 
         // Check if we can perform a faster simple replacement of instructions.
-        if (canPerformSimpleReplacements(codeAttrInfo))
+        if (canPerformSimpleReplacements(codeAttribute))
         {
             // Simply overwrite the instructions.
-            performSimpleReplacements(codeAttrInfo);
+            performSimpleReplacements(codeAttribute);
 
-            // Update the maximum stack size.
-            stackSizeUpdater.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+            // Update the maximum stack size and local variable frame size.
+            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+            variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
         }
         else
         {
             // Move and remap the instructions.
-            codeAttrInfo.u4codeLength =
-                updateInstructions(classFile, methodInfo, codeAttrInfo);
+            codeAttribute.u4codeLength =
+                updateInstructions(clazz, method, codeAttribute);
 
             // Remap the exception table.
-            codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
-
-            // Remap  the line number table and the local variable table.
-            codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+            codeAttribute.exceptionsAccept(clazz, method, this);
 
             // Remove exceptions with empty code blocks.
-            codeAttrInfo.u2exceptionTableLength =
-                 removeEmptyExceptions(codeAttrInfo.exceptionTable,
-                                       codeAttrInfo.u2exceptionTableLength);
+            codeAttribute.u2exceptionTableLength =
+                removeEmptyExceptions(codeAttribute.exceptionTable,
+                                      codeAttribute.u2exceptionTableLength);
+
+            // Update the maximum stack size and local variable frame size.
+            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
+            variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
 
-            // Update the maximum stack size.
-            stackSizeUpdater.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+            // Remap the line number table and the local variable table.
+            codeAttribute.attributesAccept(clazz, method, this);
 
             // Make sure instructions are widened if necessary.
-            instructionWriter.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+            instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
         }
     }
 
 
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        // Remap all stack map entries.
+        expectedStackMapFrameOffset = -1;
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        // Remap all stack map table entries.
+        expectedStackMapFrameOffset = 0;
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
         // Remap all line number table entries.
-        lineNumberTableAttrInfo.lineNumbersAccept(classFile, methodInfo, codeAttrInfo, this);
+        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
 
         // Remove line numbers with empty code blocks.
-        lineNumberTableAttrInfo.u2lineNumberTableLength =
-           removeEmptyLineNumbers(lineNumberTableAttrInfo.lineNumberTable,
-                                  lineNumberTableAttrInfo.u2lineNumberTableLength,
-                                  codeAttrInfo.u4codeLength);
+        lineNumberTableAttribute.u2lineNumberTableLength =
+           removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
+                                  lineNumberTableAttribute.u2lineNumberTableLength,
+                                  codeAttribute.u4codeLength);
     }
 
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
         // Remap all local variable table entries.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
         // Remove local variables with empty code blocks.
-        localVariableTableAttrInfo.u2localVariableTableLength =
-            removeEmptyLocalVariables(localVariableTableAttrInfo.localVariableTable,
-                                      localVariableTableAttrInfo.u2localVariableTableLength);
+        localVariableTableAttribute.u2localVariableTableLength =
+            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
+                                      localVariableTableAttribute.u2localVariableTableLength,
+                                      codeAttribute.u2maxLocals);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
         // Remap all local variable table entries.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
         // Remove local variables with empty code blocks.
-        localVariableTypeTableAttrInfo.u2localVariableTypeTableLength =
-            removeEmptyLocalVariableTypes(localVariableTypeTableAttrInfo.localVariableTypeTable,
-                                          localVariableTypeTableAttrInfo.u2localVariableTypeTableLength);
+        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
+            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
+                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength,
+                                          codeAttribute.u2maxLocals);
     }
 
 
     /**
      * Checks if it is possible to modifies the given code without having to
      * update any offsets.
-     * @param codeAttrInfo the code to be changed.
+     * @param codeAttribute the code to be changed.
      * @return the new code length.
      */
-    private boolean canPerformSimpleReplacements(CodeAttrInfo codeAttrInfo)
+    private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute)
     {
-        if (inserted)
+        if (!simple)
         {
             return false;
         }
 
-        byte[] code       = codeAttrInfo.code;
-        int    codeLength = codeAttrInfo.u4codeLength;
+        byte[] code       = codeAttribute.code;
+        int    codeLength = codeAttribute.u4codeLength;
 
         // Go over all replacement instructions.
         for (int offset = 0; offset < codeLength; offset++)
@@ -361,11 +399,11 @@ public class CodeAttrInfoEditor
 
     /**
      * Modifies the given code without updating any offsets.
-     * @param codeAttrInfo the code to be changed.
+     * @param codeAttribute the code to be changed.
      */
-    private void performSimpleReplacements(CodeAttrInfo codeAttrInfo)
+    private void performSimpleReplacements(CodeAttribute codeAttribute)
     {
-        int codeLength = codeAttrInfo.u4codeLength;
+        int codeLength = codeAttribute.u4codeLength;
 
         // Go over all replacement instructions.
         for (int offset = 0; offset < codeLength; offset++)
@@ -375,7 +413,7 @@ public class CodeAttrInfoEditor
             Instruction replacementInstruction = replacements[offset];
             if (replacementInstruction != null)
             {
-                replacementInstruction.write(codeAttrInfo, offset);
+                replacementInstruction.write(codeAttribute, offset);
             }
         }
     }
@@ -383,17 +421,17 @@ public class CodeAttrInfoEditor
 
     /**
      * Modifies the given code based on the previously specified changes.
-     * @param classFile    the class file of the code to be changed.
-     * @param methodInfo   the method of the code to be changed.
-     * @param codeAttrInfo the code to be changed.
+     * @param clazz         the class file of the code to be changed.
+     * @param method        the method of the code to be changed.
+     * @param codeAttribute the code to be changed.
      * @return the new code length.
      */
-    private int updateInstructions(ClassFile    classFile,
-                                   MethodInfo   methodInfo,
-                                   CodeAttrInfo codeAttrInfo)
+    private int updateInstructions(Clazz    clazz,
+                                   Method   method,
+                                   CodeAttribute codeAttribute)
     {
-        byte[] oldCode   = codeAttrInfo.code;
-        int    oldLength = codeAttrInfo.u4codeLength;
+        byte[] oldCode   = codeAttribute.code;
+        int    oldLength = codeAttribute.u4codeLength;
 
         // Make sure there is a sufficiently large instruction offset map.
         if (instructionOffsetMap == null ||
@@ -409,16 +447,16 @@ public class CodeAttrInfoEditor
         // Create a new code array if necessary.
         if (lengthIncreased)
         {
-            codeAttrInfo.code = new byte[newLength];
+            codeAttribute.code = new byte[newLength];
         }
 
         // Prepare for possible widening of instructions.
         instructionWriter.reset(newLength);
 
         // Move the instructions into the new code array.
-        moveInstructions(classFile,
-                         methodInfo,
-                         codeAttrInfo,
+        moveInstructions(clazz,
+                         method,
+                         codeAttribute,
                          oldCode,
                          oldLength);
 
@@ -506,17 +544,17 @@ public class CodeAttrInfoEditor
 
     /**
      * Moves the given code block to the new offsets.
-     * @param classFile    the class file of the code to be changed.
-     * @param methodInfo   the method of the code to be changed.
-     * @param codeAttrInfo the code to be changed.
-     * @param oldCode      the original code to be moved.
-     * @param oldLength    the original code length.
+     * @param clazz         the class file of the code to be changed.
+     * @param method        the method of the code to be changed.
+     * @param codeAttribute the code to be changed.
+     * @param oldCode       the original code to be moved.
+     * @param oldLength     the original code length.
      */
-    private void moveInstructions(ClassFile    classFile,
-                                  MethodInfo   methodInfo,
-                                  CodeAttrInfo codeAttrInfo,
-                                  byte[]       oldCode,
-                                  int          oldLength)
+    private void moveInstructions(Clazz         clazz,
+                                  Method        method,
+                                  CodeAttribute codeAttribute,
+                                  byte[]        oldCode,
+                                  int           oldLength)
     {
         // Start writing instructions at the beginning.
         newOffset = 0;
@@ -528,9 +566,9 @@ public class CodeAttrInfoEditor
             Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
 
             // Move the instruction to its new offset.
-            moveInstruction(classFile,
-                            methodInfo,
-                            codeAttrInfo,
+            moveInstruction(clazz,
+                            method,
+                            codeAttribute,
                             oldOffset,
                             instruction);
 
@@ -542,24 +580,29 @@ public class CodeAttrInfoEditor
 
     /**
      * Moves the given instruction to its new offset.
-     * @param classFile    the class file of the code to be changed.
-     * @param methodInfo   the method of the code to be changed.
-     * @param codeAttrInfo the code to be changed.
-     * @param oldOffset    the original instruction offset.
-     * @param instruction  the original instruction.
+     * @param clazz         the class file of the code to be changed.
+     * @param method        the method of the code to be changed.
+     * @param codeAttribute the code to be changed.
+     * @param oldOffset     the original instruction offset.
+     * @param instruction   the original instruction.
      */
-    private void moveInstruction(ClassFile    classFile,
-                                 MethodInfo   methodInfo,
-                                 CodeAttrInfo codeAttrInfo,
-                                 int          oldOffset,
-                                 Instruction  instruction)
+    private void moveInstruction(Clazz         clazz,
+                                 Method        method,
+                                 CodeAttribute codeAttribute,
+                                 int           oldOffset,
+                                 Instruction   instruction)
     {
         // Remap and insert the pre-inserted instruction, if any.
         Instruction preInstruction = preInsertions[oldOffset];
         if (preInstruction != null)
         {
             // Remap the instruction.
-            preInstruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+            preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
+
+            if (DEBUG)
+            {
+                System.out.println("  Pre-inserted  "+preInstruction.toString(newOffset));
+            }
 
             newOffset += preInstruction.length(newOffset);
         }
@@ -570,14 +613,24 @@ public class CodeAttrInfoEditor
         if (replacementInstruction != null)
         {
             // Remap the instruction.
-            replacementInstruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+            replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
+
+            if (DEBUG)
+            {
+                System.out.println("  Replaced      "+replacementInstruction.toString(newOffset));
+            }
 
             newOffset += replacementInstruction.length(newOffset);
         }
         else if (!deleted[oldOffset])
         {
             // Remap the instruction.
-            instruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+            instruction.accept(clazz, method, codeAttribute, oldOffset, this);
+
+            if (DEBUG)
+            {
+                System.out.println("  Copied        "+instruction.toString(newOffset));
+            }
 
             newOffset += instruction.length(newOffset);
         }
@@ -587,7 +640,12 @@ public class CodeAttrInfoEditor
         if (postInstruction != null)
         {
             // Remap the instruction.
-            postInstruction.accept(classFile, methodInfo, codeAttrInfo, oldOffset, this);
+            postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
+
+            if (DEBUG)
+            {
+                System.out.println("  Post-inserted "+postInstruction.toString(newOffset));
+            }
 
             newOffset += postInstruction.length(newOffset);
         }
@@ -596,55 +654,55 @@ public class CodeAttrInfoEditor
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         // Write out the instruction.
-        instructionWriter.visitSimpleInstruction(classFile,
-                                                 methodInfo,
-                                                 codeAttrInfo,
+        instructionWriter.visitSimpleInstruction(clazz,
+                                                 method,
+                                                 codeAttribute,
                                                  newOffset,
                                                  simpleInstruction);
     }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
         // Write out the instruction.
-        instructionWriter.visitCpInstruction(classFile,
-                                             methodInfo,
-                                             codeAttrInfo,
-                                             newOffset,
-                                             cpInstruction);
+        instructionWriter.visitConstantInstruction(clazz,
+                                                   method,
+                                                   codeAttribute,
+                                                   newOffset,
+                                                   constantInstruction);
     }
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         // Write out the instruction.
-        instructionWriter.visitVariableInstruction(classFile,
-                                                   methodInfo,
-                                                   codeAttrInfo,
+        instructionWriter.visitVariableInstruction(clazz,
+                                                   method,
+                                                   codeAttribute,
                                                    newOffset,
                                                    variableInstruction);
     }
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         // Adjust the branch offset.
         branchInstruction.branchOffset = remapBranchOffset(offset,
                                                            branchInstruction.branchOffset);
 
         // Write out the instruction.
-        instructionWriter.visitBranchInstruction(classFile,
-                                                 methodInfo,
-                                                 codeAttrInfo,
+        instructionWriter.visitBranchInstruction(clazz,
+                                                 method,
+                                                 codeAttribute,
                                                  newOffset,
                                                  branchInstruction);
     }
 
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
     {
         // Adjust the default jump offset.
         tableSwitchInstruction.defaultOffset = remapBranchOffset(offset,
@@ -652,20 +710,18 @@ public class CodeAttrInfoEditor
 
         // Adjust the jump offsets.
         remapJumpOffsets(offset,
-                         tableSwitchInstruction.jumpOffsets,
-                         tableSwitchInstruction.highCase -
-                         tableSwitchInstruction.lowCase + 1);
+                         tableSwitchInstruction.jumpOffsets);
 
         // Write out the instruction.
-        instructionWriter.visitTableSwitchInstruction(classFile,
-                                                      methodInfo,
-                                                      codeAttrInfo,
+        instructionWriter.visitTableSwitchInstruction(clazz,
+                                                      method,
+                                                      codeAttribute,
                                                       newOffset,
                                                       tableSwitchInstruction);
     }
 
 
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
     {
         // Adjust the default jump offset.
         lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset,
@@ -673,13 +729,12 @@ public class CodeAttrInfoEditor
 
         // Adjust the jump offsets.
         remapJumpOffsets(offset,
-                         lookUpSwitchInstruction.jumpOffsets,
-                         lookUpSwitchInstruction.jumpOffsetCount);
+                         lookUpSwitchInstruction.jumpOffsets);
 
         // Write out the instruction.
-        instructionWriter.visitLookUpSwitchInstruction(classFile,
-                                                       methodInfo,
-                                                       codeAttrInfo,
+        instructionWriter.visitLookUpSwitchInstruction(clazz,
+                                                       method,
+                                                       codeAttribute,
                                                        newOffset,
                                                        lookUpSwitchInstruction);
     }
@@ -687,44 +742,111 @@ public class CodeAttrInfoEditor
 
     // Implementations for ExceptionInfoVisitor.
 
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
         // Remap the code offsets. Note that the instruction offset map also has
-        // an entry for the first offset after the code, for u2endpc.
-        exceptionInfo.u2startpc   = remapInstructionOffset(exceptionInfo.u2startpc);
-        exceptionInfo.u2endpc     = remapInstructionOffset(exceptionInfo.u2endpc);
-        exceptionInfo.u2handlerpc = remapInstructionOffset(exceptionInfo.u2handlerpc);
+        // an entry for the first offset after the code, for u2endPC.
+        exceptionInfo.u2startPC   = remapInstructionOffset(exceptionInfo.u2startPC);
+        exceptionInfo.u2endPC     = remapInstructionOffset(exceptionInfo.u2endPC);
+        exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
+    {
+        // Remap the stack map frame offset.
+        int stackMapFrameOffset = remapInstructionOffset(offset);
+
+        int offsetDelta = stackMapFrameOffset;
+
+        // Compute the offset delta if the frame is part of a stack map frame
+        // table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
+        if (expectedStackMapFrameOffset >= 0)
+        {
+            offsetDelta -= expectedStackMapFrameOffset;
+
+            expectedStackMapFrameOffset = stackMapFrameOffset + 1;
+        }
+
+        stackMapFrame.u2offsetDelta = offsetDelta;
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
+
+        // Remap the verification type offset.
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
+
+        // Remap the verification type offsets.
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        // Remap the stack map frame offset.
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
+
+        // Remap the verification type offsets.
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
+
+
+    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+    {
+        // Remap the offset of the 'new' instruction.
+        uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
     }
 
 
     // Implementations for LineNumberInfoVisitor.
 
-    public void visitLineNumberInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberInfo lineNumberInfo)
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
     {
         // Remap the code offset.
-        lineNumberInfo.u2startpc = remapInstructionOffset(lineNumberInfo.u2startpc);
+        lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
     }
 
 
     // Implementations for LocalVariableInfoVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
     {
         // Remap the code offset and length.
-        localVariableInfo.u2length  = remapBranchOffset(localVariableInfo.u2startpc,
+        // TODO: The local variable frame might not be strictly preserved.
+        localVariableInfo.u2length  = remapBranchOffset(localVariableInfo.u2startPC,
                                                         localVariableInfo.u2length);
-        localVariableInfo.u2startpc = remapInstructionOffset(localVariableInfo.u2startpc);
+        localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC);
     }
 
 
     // Implementations for LocalVariableTypeInfoVisitor.
 
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
     {
         // Remap the code offset and length.
-        localVariableTypeInfo.u2length  = remapBranchOffset(localVariableTypeInfo.u2startpc,
+        // TODO: The local variable frame might not be strictly preserved.
+        localVariableTypeInfo.u2length  = remapBranchOffset(localVariableTypeInfo.u2startPC,
                                                             localVariableTypeInfo.u2length);
-        localVariableTypeInfo.u2startpc = remapInstructionOffset(localVariableTypeInfo.u2startpc);
+        localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
     }
 
 
@@ -733,9 +855,9 @@ public class CodeAttrInfoEditor
     /**
      * Adjusts the given jump offsets for the instruction at the given offset.
      */
-    private void remapJumpOffsets(int offset, int[] jumpOffsets, int length)
+    private void remapJumpOffsets(int offset, int[] jumpOffsets)
     {
-        for (int index = 0; index < length; index++)
+        for (int index = 0; index < jumpOffsets.length; index++)
         {
             jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
         }
@@ -780,7 +902,7 @@ public class CodeAttrInfoEditor
         for (int index = 0; index < exceptionInfoCount; index++)
         {
             ExceptionInfo exceptionInfo = exceptionInfos[index];
-            if (exceptionInfo.u2startpc < exceptionInfo.u2endpc)
+            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
             {
                 exceptionInfos[newIndex++] = exceptionInfo;
             }
@@ -792,20 +914,20 @@ public class CodeAttrInfoEditor
 
     /**
      * Returns the given list of line numbers, without the ones that have empty
-     * code blocks.
+     * code blocks or that exceed the code size.
      */
     private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
                                        int              lineNumberInfoCount,
                                        int              codeLength)
     {
-        // Overwrite all empty localVariables.
+        // Overwrite all empty line number entries.
         int newIndex = 0;
         for (int index = 0; index < lineNumberInfoCount; index++)
         {
             LineNumberInfo lineNumberInfo = lineNumberInfos[index];
-            int startpc = lineNumberInfo.u2startpc;
-            if (               startpc < codeLength &&
-                (index == 0 || startpc > lineNumberInfos[index-1].u2startpc))
+            int startPC = lineNumberInfo.u2startPC;
+            if (startPC < codeLength &&
+                (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
             {
                 lineNumberInfos[newIndex++] = lineNumberInfo;
             }
@@ -817,17 +939,19 @@ public class CodeAttrInfoEditor
 
     /**
      * Returns the given list of local variables, without the ones that have empty
-     * code blocks.
+     * code blocks or that exceed the actual number of local variables.
      */
     private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
-                                          int                 localVariableInfoCount)
+                                          int                 localVariableInfoCount,
+                                          int                 maxLocals)
     {
-        // Overwrite all empty exceptions.
+        // Overwrite all empty local variable entries.
         int newIndex = 0;
         for (int index = 0; index < localVariableInfoCount; index++)
         {
             LocalVariableInfo localVariableInfo = localVariableInfos[index];
-            if (localVariableInfo.u2length > 0)
+            if (localVariableInfo.u2length > 0 &&
+                localVariableInfo.u2index < maxLocals)
             {
                 localVariableInfos[newIndex++] = localVariableInfo;
             }
@@ -839,17 +963,19 @@ public class CodeAttrInfoEditor
 
     /**
      * Returns the given list of local variable types, without the ones that
-     * have empty code blocks.
+     * have empty code blocks or that exceed the actual number of local variables.
      */
     private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
-                                              int                     localVariableTypeInfoCount)
+                                              int                     localVariableTypeInfoCount,
+                                              int                     maxLocals)
     {
-        // Overwrite all empty exceptions.
+        // Overwrite all empty local variable type entries.
         int newIndex = 0;
         for (int index = 0; index < localVariableTypeInfoCount; index++)
         {
             LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
-            if (localVariableTypeInfo.u2length > 0)
+            if (localVariableTypeInfo.u2length > 0 &&
+                localVariableTypeInfo.u2index < maxLocals)
             {
                 localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
             }
diff --git a/src/proguard/classfile/editor/CodeAttributeEditorResetter.java b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java
new file mode 100644
index 0000000..bbe8f01
--- /dev/null
+++ b/src/proguard/classfile/editor/CodeAttributeEditorResetter.java
@@ -0,0 +1,60 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor resets it CodeAttributeEditor whenever it visits a
+ * code attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeAttributeEditorResetter
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private final CodeAttributeEditor codeAttributeEditor;
+
+
+    /**
+     * Creates a new CodeAttributeEditorResetter.
+     * @param codeAttributeEditor the code attribute editor that will be reset.
+     */
+    public CodeAttributeEditorResetter(CodeAttributeEditor codeAttributeEditor)
+    {
+        this.codeAttributeEditor = codeAttributeEditor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
+    }
+}
diff --git a/src/proguard/classfile/editor/ComparableConstant.java b/src/proguard/classfile/editor/ComparableConstant.java
new file mode 100644
index 0000000..5d7cbbe
--- /dev/null
+++ b/src/proguard/classfile/editor/ComparableConstant.java
@@ -0,0 +1,182 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+
+/**
+ * This class is a <code>Comparable</code> wrapper of <code>Constant</code>
+ * objects. It can store an index, in order to identify the constant   pool
+ * entry after it has been sorted. The comparison is primarily based   on the
+ * types of the constant pool entries, and secondarily on the contents of
+ * the constant pool entries.
+ *
+ * @author Eric Lafortune
+ */
+final class      ComparableConstant
+extends    SimplifiedVisitor
+implements Comparable, ConstantVisitor
+{
+    private static final int[] PRIORITIES = new int[13];
+    static
+    {
+        PRIORITIES[ClassConstants.CONSTANT_Integer]            = 0;
+        PRIORITIES[ClassConstants.CONSTANT_Float]              = 1;
+        PRIORITIES[ClassConstants.CONSTANT_Long]               = 2;
+        PRIORITIES[ClassConstants.CONSTANT_Double]             = 3;
+        PRIORITIES[ClassConstants.CONSTANT_String]             = 4;
+        PRIORITIES[ClassConstants.CONSTANT_Class]              = 5;
+        PRIORITIES[ClassConstants.CONSTANT_Fieldref]           = 6;
+        PRIORITIES[ClassConstants.CONSTANT_Methodref]          = 7;
+        PRIORITIES[ClassConstants.CONSTANT_InterfaceMethodref] = 8;
+        PRIORITIES[ClassConstants.CONSTANT_NameAndType]        = 9;
+        PRIORITIES[ClassConstants.CONSTANT_Utf8]               = 10;
+    }
+
+    private final Clazz    clazz;
+    private final int      thisIndex;
+    private final Constant thisConstant;
+    private Constant otherConstant;
+    private int      result;
+
+
+    public ComparableConstant(Clazz clazz, int index, Constant constant)
+    {
+        this.clazz        = clazz;
+        this.thisIndex    = index;
+        this.thisConstant = constant;
+    }
+
+
+    public int getIndex()
+    {
+        return thisIndex;
+    }
+
+
+    public Constant getConstant()
+    {
+        return thisConstant;
+    }
+
+
+    // Implementations for Comparable.
+
+    public int compareTo(Object other)
+    {
+        ComparableConstant otherComparableConstant = (ComparableConstant)other;
+
+        otherConstant = otherComparableConstant.thisConstant;
+
+        // Compare based on the original indices, if the actual constant pool
+        // entries are the same.
+        if (thisConstant == otherConstant)
+        {
+            int otherIndex = otherComparableConstant.thisIndex;
+
+            return thisIndex <  otherIndex ? -1 :
+                   thisIndex == otherIndex ?  0 :
+                   1;
+        }
+
+        // Compare based on the tags, if they are different.
+        int thisTag  = thisConstant.getTag();
+        int otherTag = otherConstant.getTag();
+
+        if (thisTag != otherTag)
+        {
+            return PRIORITIES[thisTag] < PRIORITIES[otherTag] ? -1 : 1;
+        }
+
+        // Otherwise compare based on the contents of the Constant objects.
+        thisConstant.accept(clazz, this);
+
+        return result;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        // In JDK 1.4, we can use Integer.compare(a,b).
+        result = new Integer(integerConstant.getValue()).compareTo(new Integer(((IntegerConstant)otherConstant).getValue()));
+    }
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        // In JDK 1.4, we can use Long.compare(a,b).
+        result = new Long(longConstant.getValue()).compareTo(new Long(((LongConstant)otherConstant).getValue()));
+    }
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        // In JDK 1.4, we can use Float.compare(a,b).
+        result = new Float(floatConstant.getValue()).compareTo(new Float(((FloatConstant)otherConstant).getValue()));
+    }
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        // In JDK 1.4, we can use Double.compare(a,b).
+        result = new Double(doubleConstant.getValue()).compareTo(new Double(((DoubleConstant)otherConstant).getValue()));
+    }
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        result = stringConstant.getString(clazz).compareTo(((StringConstant)otherConstant).getString(clazz));
+    }
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        result = utf8Constant.getString().compareTo(((Utf8Constant)otherConstant).getString());
+    }
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        RefConstant otherRefConstant = (RefConstant)otherConstant;
+        result = (refConstant.getClassName(clazz) + ' ' +
+                  refConstant.getName(clazz)      + ' ' +
+                  refConstant.getType(clazz))
+                 .compareTo
+                 (otherRefConstant.getClassName(clazz) + ' ' +
+                  otherRefConstant.getName(clazz)      + ' ' +
+                  otherRefConstant.getType(clazz));
+    }
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        result = classConstant.getName(clazz).compareTo(((ClassConstant)otherConstant).getName(clazz));
+    }
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        NameAndTypeConstant otherNameAndTypeConstant = (NameAndTypeConstant)otherConstant;
+        result = (nameAndTypeConstant.getName(clazz) + ' ' +
+                  nameAndTypeConstant.getType(clazz))
+                 .compareTo
+                 (otherNameAndTypeConstant.getName(clazz) + ' ' +
+                  otherNameAndTypeConstant.getType(clazz));
+    }
+}
diff --git a/src/proguard/classfile/editor/ComparableCpInfo.java b/src/proguard/classfile/editor/ComparableCpInfo.java
deleted file mode 100644
index e632486..0000000
--- a/src/proguard/classfile/editor/ComparableCpInfo.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/* $Id: ComparableCpInfo.java,v 1.5.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.editor;
-
-import proguard.classfile.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This class is a <code>Comparable</code> wrapper of <code>CpInfo</code>
- * objects. It can store an index, in order to identify the constant pool
- * entry after it has been sorted. The comparison is primarily based on the
- * types of the constant pool entries, and secondarily on the contents of
- * the constant pool entries.
- *
- * @author Eric Lafortune
- */
-class ComparableCpInfo implements Comparable, CpInfoVisitor
-{
-    private static int[] PRIORITIES = new int[13];
-    static
-    {
-        PRIORITIES[ClassConstants.CONSTANT_Class]              = 0;
-        PRIORITIES[ClassConstants.CONSTANT_Fieldref]           = 1;
-        PRIORITIES[ClassConstants.CONSTANT_Methodref]          = 2;
-        PRIORITIES[ClassConstants.CONSTANT_InterfaceMethodref] = 3;
-        PRIORITIES[ClassConstants.CONSTANT_String]             = 4;
-        PRIORITIES[ClassConstants.CONSTANT_Integer]            = 5;
-        PRIORITIES[ClassConstants.CONSTANT_Float]              = 6;
-        PRIORITIES[ClassConstants.CONSTANT_Long]               = 7;
-        PRIORITIES[ClassConstants.CONSTANT_Double]             = 8;
-        PRIORITIES[ClassConstants.CONSTANT_NameAndType]        = 9;
-        PRIORITIES[ClassConstants.CONSTANT_Utf8]               = 10;
-    }
-
-    private ClassFile classFile;
-    private int       thisIndex;
-    private CpInfo    thisCpInfo;
-    private CpInfo    otherCpInfo;
-    private int       result;
-
-
-    public ComparableCpInfo(ClassFile classFile, int index, CpInfo cpInfo)
-    {
-        this.classFile  = classFile;
-        this.thisIndex  = index;
-        this.thisCpInfo = cpInfo;
-    }
-
-
-    public int getIndex()
-    {
-        return thisIndex;
-    }
-
-
-    public CpInfo getCpInfo()
-    {
-        return thisCpInfo;
-    }
-
-
-    // Implementations for Comparable.
-
-    public int compareTo(Object other)
-    {
-        ComparableCpInfo otherComparableCpInfo = (ComparableCpInfo)other;
-
-        otherCpInfo = otherComparableCpInfo.thisCpInfo;
-
-        // Compare based on the original indices, if the actual constant pool
-        // entries are the same.
-        if (thisCpInfo == otherCpInfo)
-        {
-            int otherIndex = otherComparableCpInfo.thisIndex;
-
-            return thisIndex <  otherIndex ? -1 :
-                   thisIndex == otherIndex ?  0 :
-                                              1;
-        }
-
-        // Compare based on the tags, if they are different.
-        int thisTag  = thisCpInfo.getTag();
-        int otherTag = otherCpInfo.getTag();
-
-        if (thisTag != otherTag)
-        {
-            return PRIORITIES[thisTag] < PRIORITIES[otherTag] ? -1 : 1;
-        }
-
-        // Otherwise compare based on the contents of the CpInfo objects.
-        thisCpInfo.accept(classFile, this);
-
-        return result;
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
-    {
-        // In JDK 1.4, we can use Integer.compare(a,b).
-        result = new Integer(integerCpInfo.getValue()).compareTo(new Integer(((IntegerCpInfo)otherCpInfo).getValue()));
-    }
-
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
-    {
-        // In JDK 1.4, we can use Long.compare(a,b).
-        result = new Long(longCpInfo.getValue()).compareTo(new Long(((LongCpInfo)otherCpInfo).getValue()));
-    }
-
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
-    {
-        // In JDK 1.4, we can use Float.compare(a,b).
-        result = new Float(floatCpInfo.getValue()).compareTo(new Float(((FloatCpInfo)otherCpInfo).getValue()));
-    }
-
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
-    {
-        // In JDK 1.4, we can use Double.compare(a,b).
-        result = new Double(doubleCpInfo.getValue()).compareTo(new Double(((DoubleCpInfo)otherCpInfo).getValue()));
-    }
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        result = stringCpInfo.getString(classFile).compareTo(((StringCpInfo)otherCpInfo).getString(classFile));
-    }
-
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
-    {
-        result = utf8CpInfo.getString().compareTo(((Utf8CpInfo)otherCpInfo).getString());
-    }
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        result = fieldrefCpInfo.getName(classFile).compareTo(((FieldrefCpInfo)otherCpInfo).getName(classFile));
-    }
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        result = interfaceMethodrefCpInfo.getName(classFile).compareTo(((InterfaceMethodrefCpInfo)otherCpInfo).getName(classFile));
-    }
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        result = methodrefCpInfo.getName(classFile).compareTo(((MethodrefCpInfo)otherCpInfo).getName(classFile));
-    }
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        result = classCpInfo.getName(classFile).compareTo(((ClassCpInfo)otherCpInfo).getName(classFile));
-    }
-
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
-    {
-        result = nameAndTypeCpInfo.getName(classFile).compareTo(((NameAndTypeCpInfo)otherCpInfo).getName(classFile));
-    }
-}
diff --git a/src/proguard/classfile/editor/ConstantAdder.java b/src/proguard/classfile/editor/ConstantAdder.java
new file mode 100644
index 0000000..8895fee
--- /dev/null
+++ b/src/proguard/classfile/editor/ConstantAdder.java
@@ -0,0 +1,190 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+
+/**
+ * This ConstantVisitor adds all constants that it visits to the constant pool
+ * of a given target class.
+ *
+ * @author Eric Lafortune
+ */
+public class ConstantAdder
+implements   ConstantVisitor
+{
+    private ProgramClass targetClass;
+    private int          constantIndex;
+
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+
+
+    /**
+     * Sets the class to which visited constants will be copied.
+     */
+    public void setTargetClass(ProgramClass targetClass)
+    {
+        this.targetClass = targetClass;
+    }
+
+
+    /**
+     * Returns the class to which visited constants will be copied.
+     */
+    public ProgramClass getTargetClass()
+    {
+        return targetClass;
+    }
+
+
+    /**
+     * Returns the index of the most recently created constant in the constant
+     * pool of the target class.
+     */
+    public int getConstantIndex()
+    {
+        return constantIndex;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addIntegerConstant(targetClass,
+                                                  integerConstant.getValue());
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addLongConstant(targetClass,
+                                               longConstant.getValue());
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addFloatConstant(targetClass,
+                                                floatConstant.getValue());
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addDoubleConstant(targetClass,
+                                                 doubleConstant.getValue());
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addStringConstant(targetClass,
+                                                 stringConstant.getString(clazz),
+                                                 stringConstant.referencedClass,
+                                                 stringConstant.referencedMember);
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        constantIndex =
+            constantPoolEditor.addUtf8Constant(targetClass,
+                                               utf8Constant.getString());
+    }
+
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        // First add the referenced class constant, with its own referenced class.
+        clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this);
+
+        // Then add the actual field reference constant, with its referenced
+        // class and class member.
+        constantIndex =
+            constantPoolEditor.addFieldrefConstant(targetClass,
+                                                   constantIndex,
+                                                   fieldrefConstant.getName(clazz),
+                                                   fieldrefConstant.getType(clazz),
+                                                   fieldrefConstant.referencedClass,
+                                                   fieldrefConstant.referencedMember);
+    }
+
+
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
+    {
+        // First add the referenced class constant, with its own referenced class.
+        clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this);
+
+        // Then add the actual interface method reference constant, with its
+        // referenced class and class member.
+        constantIndex =
+            constantPoolEditor.addInterfaceMethodrefConstant(targetClass,
+                                                             constantIndex,
+                                                             interfaceMethodrefConstant.getName(clazz),
+                                                             interfaceMethodrefConstant.getType(clazz),
+                                                             interfaceMethodrefConstant.referencedClass,
+                                                             interfaceMethodrefConstant.referencedMember);
+    }
+
+
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+    {
+        // First add the referenced class constant, with its own referenced class.
+        clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this);
+
+        // Then add the actual method reference constant, with its referenced
+        // class and class member.
+        constantIndex =
+            constantPoolEditor.addMethodrefConstant(targetClass,
+                                                    constantIndex,
+                                                    methodrefConstant.getName(clazz),
+                                                    methodrefConstant.getType(clazz),
+                                                    methodrefConstant.referencedClass,
+                                                    methodrefConstant.referencedMember);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Add the class constant, with its referenced class..
+        constantIndex =
+            constantPoolEditor.addClassConstant(targetClass,
+                                                classConstant.getName(clazz),
+                                                classConstant.referencedClass);
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        constantIndex =
+            constantPoolEditor.addNameAndTypeConstant(targetClass,
+                                                      nameAndTypeConstant.getName(clazz),
+                                                      nameAndTypeConstant.getType(clazz));
+    }
+}
diff --git a/src/proguard/classfile/editor/ConstantPoolEditor.java b/src/proguard/classfile/editor/ConstantPoolEditor.java
index 43cf8c4..7fb0224 100644
--- a/src/proguard/classfile/editor/ConstantPoolEditor.java
+++ b/src/proguard/classfile/editor/ConstantPoolEditor.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantPoolEditor.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,10 @@
 package proguard.classfile.editor;
 
 import proguard.classfile.*;
+import proguard.classfile.constant.*;
 
 /**
- * This class can add constant pool entries to given class files.
+ * This class can add constant pool entries to given classes.
  *
  * @author Eric Lafortune
  */
@@ -33,560 +34,695 @@ public class ConstantPoolEditor
 
 
     /**
-     * Finds or creates a StringCpInfo constant pool entry with the given value,
-     * in the given class file.
-     * @return the constant pool index of the ClassCpInfo.
+     * Finds or creates a IntegerConstant constant pool entry with the given value,
+     * in the given class.
+     * @return the constant pool index of the   Utf8Constant.
+     */
+    public int addIntegerConstant(ProgramClass programClass,
+                                  int          value)
+    {
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            Constant constant = constantPool[index];
+
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Integer)
+            {
+                IntegerConstant integerConstant = (IntegerConstant)constant;
+                if (integerConstant.getValue() == value)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addConstant(programClass, new IntegerConstant(value));
+    }
+
+
+    /**
+     * Finds or creates a LongConstant constant pool entry with the given value,
+     * in the given class.
+     * @return the constant pool index of the   Utf8Constant.
+     */
+    public int addLongConstant(ProgramClass programClass,
+                               long         value)
+    {
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            Constant constant = constantPool[index];
+
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Long)
+            {
+                LongConstant longConstant = (LongConstant)constant;
+                if (longConstant.getValue() == value)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addConstant(programClass, new LongConstant(value));
+    }
+
+
+    /**
+     * Finds or creates a FloatConstant constant pool entry with the given value,
+     * in the given class.
+     * @return the constant pool index of the   Utf8Constant.
+     */
+    public int addFloatConstant(ProgramClass programClass,
+                                float        value)
+    {
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            Constant constant = constantPool[index];
+
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Float)
+            {
+                FloatConstant floatConstant = (FloatConstant)constant;
+                if (floatConstant.getValue() == value)
+                {
+                    return index;
+                }
+            }
+        }
+
+        return addConstant(programClass, new FloatConstant(value));
+    }
+
+
+    /**
+     * Finds or creates a DoubleConstant constant pool entry with the given value,
+     * in the given class.
+     * @return the constant pool index of the   Utf8Constant.
      */
-    public int addStringCpInfo(ProgramClassFile programClassFile,
-                              String            string,
-                              ClassFile         referencedClassFile)
+    public int addDoubleConstant(ProgramClass programClass,
+                                  double      value)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_String)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Double)
             {
-                StringCpInfo classCpInfo = (StringCpInfo)cpInfo;
-                if (classCpInfo.getString(programClassFile).equals(string))
+                DoubleConstant doubleConstant = (DoubleConstant)constant;
+                if (doubleConstant.getValue() == value)
                 {
                     return index;
                 }
             }
         }
 
-        int nameIndex = addUtf8CpInfo(programClassFile, string);
+        return addConstant(programClass, new DoubleConstant(value));
+    }
+
 
-        return addCpInfo(programClassFile,
-                         new StringCpInfo(nameIndex,
-                                          referencedClassFile));
+    /**
+     * Finds or creates a StringConstant constant pool entry with the given value,
+     * in the given class.
+     * @return the constant pool index of the ClassConstant.
+     */
+    public int addStringConstant(ProgramClass programClass,
+                                 String       string,
+                                 Clazz        referencedClass,
+                                 Member       referencedMember)
+    {
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
+
+        // Check if the entry already exists.
+        for (int index = 1; index < constantPoolCount; index++)
+        {
+            Constant constant = constantPool[index];
+
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_String)
+            {
+                StringConstant classConstant = (StringConstant)constant;
+                if (classConstant.getString(programClass).equals(string))
+                {
+                    return index;
+                }
+            }
+        }
+
+        int nameIndex = addUtf8Constant(programClass, string);
+
+        return addConstant(programClass,
+                           new StringConstant(nameIndex,
+                                              referencedClass,
+                                              referencedMember));
     }
 
 
     /**
-     * Finds or creates a FieldrefCpInfo constant pool entry for the given
-     * class and field, in the given class file.
-     * @return the constant pool index of the FieldrefCpInfo.
+     * Finds or creates a FieldrefConstant constant pool entry for the given
+     * class and field, in the given class.
+     * @return the constant pool index of the FieldrefConstant.
      */
-    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
-                                 ClassFile        referencedClassFile,
-                                 MemberInfo       referencedMemberInfo)
+    public int addFieldrefConstant(ProgramClass programClass,
+                                   Clazz        referencedClass,
+                                   Member       referencedMember)
     {
-        return addFieldrefCpInfo(programClassFile,
-                                 referencedClassFile.getName(),
-                                 referencedMemberInfo.getName(referencedClassFile),
-                                 referencedMemberInfo.getDescriptor(referencedClassFile),
-                                 referencedClassFile,
-                                 referencedMemberInfo);
+        return addFieldrefConstant(programClass,
+                                   referencedClass.getName(),
+                                   referencedMember.getName(referencedClass),
+                                   referencedMember.getDescriptor(referencedClass),
+                                   referencedClass,
+                                   referencedMember);
     }
 
 
     /**
-     * Finds or creates a FieldrefCpInfo constant pool entry with the given
-     * class name, field name, and descriptor, in the given class file.
-     * @return the constant pool index of the FieldrefCpInfo.
+     * Finds or creates a FieldrefConstant constant pool entry with the given
+     * class name, field name, and descriptor, in the given class.
+     * @return the constant pool index of the FieldrefConstant.
      */
-    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
-                                 String           className,
-                                 String           name,
-                                 String           descriptor,
-                                 ClassFile        referencedClassFile,
-                                 MemberInfo       referencedMemberInfo)
+    public int addFieldrefConstant(ProgramClass programClass,
+                                   String       className,
+                                   String       name,
+                                   String       descriptor,
+                                   Clazz        referencedClass,
+                                   Member       referencedMember)
     {
-        return addFieldrefCpInfo(programClassFile,
-                                 className,
-                                 addNameAndTypeCpInfo(programClassFile,
-                                                      name,
-                                                      descriptor),
-                                 referencedClassFile,
-                                 referencedMemberInfo);
+        return addFieldrefConstant(programClass,
+                                   className,
+                                   addNameAndTypeConstant(programClass,
+                                                          name,
+                                                          descriptor),
+                                   referencedClass,
+                                   referencedMember);
     }
 
 
     /**
-     * Finds or creates a FieldrefCpInfo constant pool entry with the given
-     * class name, field name, and descriptor, in the given class file.
-     * @return the constant pool index of the FieldrefCpInfo.
+     * Finds or creates a FieldrefConstant constant pool entry with the given
+     * class name, field name, and descriptor, in the given class.
+     * @return the constant pool index of the FieldrefConstant.
      */
-    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
-                                 String           className,
-                                 int              nameAndTypeIndex,
-                                 ClassFile        referencedClassFile,
-                                 MemberInfo       referencedMemberInfo)
+    public int addFieldrefConstant(ProgramClass programClass,
+                                   String       className,
+                                   int          nameAndTypeIndex,
+                                   Clazz        referencedClass,
+                                   Member       referencedMember)
     {
-        return addFieldrefCpInfo(programClassFile,
-                                 addClassCpInfo(programClassFile,
-                                                className,
-                                                referencedClassFile),
-                                 nameAndTypeIndex,
-                                 referencedClassFile,
-                                 referencedMemberInfo);
+        return addFieldrefConstant(programClass,
+                                   addClassConstant(programClass,
+                                                    className,
+                                                    referencedClass),
+                                   nameAndTypeIndex,
+                                   referencedClass,
+                                   referencedMember);
     }
 
 
     /**
-     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * Finds or creates a FieldrefConstant constant pool entry with the given
      * class constant pool entry index, field name, and descriptor, in the
-     * given class file.
-     * @return the constant pool index of the FieldrefCpInfo.
+     * given class.
+     * @return the constant pool index of the   FieldrefConstant.
      */
-    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
-                                 int              classIndex,
-                                 String           name,
-                                 String           descriptor,
-                                 ClassFile        referencedClassFile,
-                                 MemberInfo       referencedMemberInfo)
+    public int addFieldrefConstant(ProgramClass programClass,
+                                   int          classIndex,
+                                   String       name,
+                                   String       descriptor,
+                                   Clazz        referencedClass,
+                                   Member       referencedMember)
     {
-        return addFieldrefCpInfo(programClassFile,
-                                 classIndex,
-                                 addNameAndTypeCpInfo(programClassFile,
-                                                      name,
-                                                      descriptor),
-                                 referencedClassFile,
-                                 referencedMemberInfo);
+        return addFieldrefConstant(programClass,
+                                   classIndex,
+                                   addNameAndTypeConstant(programClass,
+                                                          name,
+                                                          descriptor),
+                                   referencedClass,
+                                   referencedMember);
     }
 
 
     /**
-     * Finds or creates a FieldrefCpInfo constant pool entry with the given
+     * Finds or creates a FieldrefConstant constant pool entry with the given
      * class constant pool entry index and name and type constant pool entry index
-     * the given class file.
-     * @return the constant pool index of the FieldrefCpInfo.
+     * the given class.
+     * @return the constant pool index of the FieldrefConstant.
      */
-    public int addFieldrefCpInfo(ProgramClassFile programClassFile,
-                                 int              classIndex,
-                                 int              nameAndTypeIndex,
-                                 ClassFile        referencedClassFile,
-                                 MemberInfo       referencedMemberInfo)
+    public int addFieldrefConstant(ProgramClass programClass,
+                                   int          classIndex,
+                                   int          nameAndTypeIndex,
+                                   Clazz        referencedClass,
+                                   Member       referencedMember)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_Fieldref)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Fieldref)
             {
-                FieldrefCpInfo fieldrefCpInfo = (FieldrefCpInfo)cpInfo;
-                if (fieldrefCpInfo.u2classIndex       == classIndex &&
-                    fieldrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                FieldrefConstant fieldrefConstant = (FieldrefConstant)constant;
+                if (fieldrefConstant.u2classIndex         == classIndex &&
+                    fieldrefConstant.u2nameAndTypeIndex   == nameAndTypeIndex)
                 {
                     return index;
                 }
             }
         }
 
-        return addCpInfo(programClassFile,
-                         new FieldrefCpInfo(classIndex,
-                                            nameAndTypeIndex,
-                                            referencedClassFile,
-                                            referencedMemberInfo));
+        return addConstant(programClass,
+                           new FieldrefConstant(classIndex,
+                                                nameAndTypeIndex,
+                                                referencedClass,
+                                                referencedMember));
     }
 
 
     /**
-     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
-     * given class name, method name, and descriptor, in the given class file.
-     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
+     * given class name, method name, and descriptor, in the given class.
+     * @return the constant pool index of the InterfaceMethodrefConstant.
      */
-    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
-                                           String           className,
-                                           String           name,
-                                           String           descriptor,
-                                           ClassFile        referencedClassFile,
-                                           MemberInfo       referencedMemberInfo)
+    public int addInterfaceMethodrefConstant(ProgramClass programClass,
+                                             String       className,
+                                             String       name,
+                                             String       descriptor,
+                                             Clazz        referencedClass,
+                                             Member       referencedMember)
     {
-        return addInterfaceMethodrefCpInfo(programClassFile,
-                                           className,
-                                           addNameAndTypeCpInfo(programClassFile,
-                                                                name,
-                                                                descriptor),
-                                                                referencedClassFile,
-                                                                referencedMemberInfo);
+        return addInterfaceMethodrefConstant(programClass,
+                                             className,
+                                             addNameAndTypeConstant(programClass,
+                                                                    name,
+                                                                    descriptor),
+                                                                    referencedClass,
+                                                                    referencedMember);
     }
 
 
     /**
-     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
-     * given class name, method name, and descriptor, in the given class file.
-     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
+     * given class name, method name, and descriptor, in the given class.
+     * @return the constant pool index of the InterfaceMethodrefConstant.
      */
-    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
-                                           String           className,
-                                           int              nameAndTypeIndex,
-                                           ClassFile        referencedClassFile,
-                                           MemberInfo       referencedMemberInfo)
+    public int addInterfaceMethodrefConstant(ProgramClass programClass,
+                                             String       className,
+                                             int          nameAndTypeIndex,
+                                             Clazz        referencedClass,
+                                             Member       referencedMember)
     {
-        return addInterfaceMethodrefCpInfo(programClassFile,
-                                           addClassCpInfo(programClassFile,
-                                                          className,
-                                                          referencedClassFile),
-                                                          nameAndTypeIndex,
-                                                          referencedClassFile,
-                                                          referencedMemberInfo);
+        return addInterfaceMethodrefConstant(programClass,
+                                             addClassConstant(programClass,
+                                                              className,
+                                                              referencedClass),
+                                                              nameAndTypeIndex,
+                                                              referencedClass,
+                                                              referencedMember);
     }
 
 
     /**
-     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry for the
-     * given class and method, in the given class file.
-     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     * Finds or creates a InterfaceMethodrefConstant constant pool entry for the
+     * given class and method, in the given class.
+     * @return the constant pool index of the InterfaceMethodrefConstant.
      */
-    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
-                                           ClassFile        referencedClassFile,
-                                           MemberInfo       referencedMemberInfo)
+    public int addInterfaceMethodrefConstant(ProgramClass programClass,
+                                             Clazz        referencedClass,
+                                             Member       referencedMember)
     {
-        return addInterfaceMethodrefCpInfo(programClassFile,
-                                           referencedClassFile.getName(),
-                                           referencedMemberInfo.getName(referencedClassFile),
-                                           referencedMemberInfo.getDescriptor(referencedClassFile),
-                                           referencedClassFile,
-                                           referencedMemberInfo);
+        return addInterfaceMethodrefConstant(programClass,
+                                             referencedClass.getName(),
+                                             referencedMember.getName(referencedClass),
+                                             referencedMember.getDescriptor(referencedClass),
+                                             referencedClass,
+                                             referencedMember);
     }
 
 
     /**
-     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
      * given class constant pool entry index, method name, and descriptor, in
-     * the given class file.
-     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     * the given class.
+     * @return the constant pool index of the InterfaceMethodrefConstant.
      */
-    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
-                                           int              classIndex,
-                                           String           name,
-                                           String           descriptor,
-                                           ClassFile        referencedClassFile,
-                                           MemberInfo       referencedMemberInfo)
+    public int addInterfaceMethodrefConstant(ProgramClass programClass,
+                                             int          classIndex,
+                                             String       name,
+                                             String       descriptor,
+                                             Clazz        referencedClass,
+                                             Member       referencedMember)
     {
-        return addInterfaceMethodrefCpInfo(programClassFile,
-                                           classIndex,
-                                           addNameAndTypeCpInfo(programClassFile,
-                                                                name,
-                                                                descriptor),
-                                           referencedClassFile,
-                                           referencedMemberInfo);
+        return addInterfaceMethodrefConstant(programClass,
+                                             classIndex,
+                                             addNameAndTypeConstant(programClass,
+                                                                    name,
+                                                                    descriptor),
+                                             referencedClass,
+                                             referencedMember);
     }
 
 
     /**
-     * Finds or creates a InterfaceMethodrefCpInfo constant pool entry with the
+     * Finds or creates a InterfaceMethodrefConstant constant pool entry with the
      * given class constant pool entry index and name and type constant pool
-     * entry index the given class file.
-     * @return the constant pool index of the InterfaceMethodrefCpInfo.
+     * entry index the given class.
+     * @return the constant pool index of the InterfaceMethodrefConstant.
      */
-    public int addInterfaceMethodrefCpInfo(ProgramClassFile programClassFile,
-                                           int              classIndex,
-                                           int              nameAndTypeIndex,
-                                           ClassFile        referencedClassFile,
-                                           MemberInfo       referencedMemberInfo)
+    public int addInterfaceMethodrefConstant(ProgramClass programClass,
+                                             int          classIndex,
+                                             int          nameAndTypeIndex,
+                                             Clazz        referencedClass,
+                                             Member       referencedMember)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                            cpInfo.getTag() == ClassConstants.CONSTANT_InterfaceMethodref)
+            if (constant != null &&
+                            constant.getTag() == ClassConstants.CONSTANT_InterfaceMethodref)
             {
-                InterfaceMethodrefCpInfo methodrefCpInfo = (InterfaceMethodrefCpInfo)cpInfo;
-                if (methodrefCpInfo.u2classIndex       == classIndex &&
-                                methodrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                InterfaceMethodrefConstant methodrefConstant = (InterfaceMethodrefConstant)constant;
+                if (methodrefConstant.u2classIndex       == classIndex &&
+                                methodrefConstant.u2nameAndTypeIndex ==   nameAndTypeIndex)
                 {
                     return index;
                 }
             }
         }
 
-        return addCpInfo(programClassFile,
-                         new InterfaceMethodrefCpInfo(classIndex,
-                                                      nameAndTypeIndex,
-                                                      referencedClassFile,
-                                                      referencedMemberInfo));
+        return addConstant(programClass,
+                           new InterfaceMethodrefConstant(classIndex,
+                                                          nameAndTypeIndex,
+                                                          referencedClass,
+                                                          referencedMember));
     }
 
 
     /**
-     * Finds or creates a MethodrefCpInfo constant pool entry for the given
-     * class and method, in the given class file.
-     * @return the constant pool index of the MethodrefCpInfo.
+     * Finds or creates a MethodrefConstant constant pool entry for the given
+     * class and method, in the given class.
+     * @return the constant pool index of   the MethodrefConstant.
      */
-    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
-                                  ClassFile        referencedClassFile,
-                                  MemberInfo       referencedMemberInfo)
+    public int addMethodrefConstant(ProgramClass programClass,
+                                    Clazz        referencedClass,
+                                    Member       referencedMember)
     {
-        return addMethodrefCpInfo(programClassFile,
-                                  referencedClassFile.getName(),
-                                  referencedMemberInfo.getName(referencedClassFile),
-                                  referencedMemberInfo.getDescriptor(referencedClassFile),
-                                  referencedClassFile,
-                                  referencedMemberInfo);
+        return addMethodrefConstant(programClass,
+                                    referencedClass.getName(),
+                                    referencedMember.getName(referencedClass),
+                                    referencedMember.getDescriptor(referencedClass),
+                                    referencedClass,
+                                    referencedMember);
     }
 
 
     /**
-     * Finds or creates a MethodrefCpInfo constant pool entry with the given
-     * class name, method name, and descriptor, in the given class file.
-     * @return the constant pool index of the MethodrefCpInfo.
+     * Finds or creates a MethodrefConstant constant pool entry with the given
+     * class name, method name, and descriptor, in the given class.
+     * @return the constant pool index of   the MethodrefConstant.
      */
-    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
-                                  String           className,
-                                  String           name,
-                                  String           descriptor,
-                                  ClassFile        referencedClassFile,
-                                  MemberInfo       referencedMemberInfo)
+    public int addMethodrefConstant(ProgramClass programClass,
+                                    String       className,
+                                    String       name,
+                                    String       descriptor,
+                                    Clazz        referencedClass,
+                                    Member       referencedMember)
     {
-        return addMethodrefCpInfo(programClassFile,
-                                  className,
-                                  addNameAndTypeCpInfo(programClassFile,
-                                                       name,
-                                                       descriptor),
-                                  referencedClassFile,
-                                  referencedMemberInfo);
+        return addMethodrefConstant(programClass,
+                                    className,
+                                    addNameAndTypeConstant(programClass,
+                                                           name,
+                                                           descriptor),
+                                    referencedClass,
+                                    referencedMember);
     }
 
 
     /**
-     * Finds or creates a MethodrefCpInfo constant pool entry with the given
-     * class name, method name, and descriptor, in the given class file.
-     * @return the constant pool index of the MethodrefCpInfo.
+     * Finds or creates a MethodrefConstant constant pool entry with the given
+     * class name, method name, and descriptor, in the given class.
+     * @return the constant pool index of   the MethodrefConstant.
      */
-    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
-                                  String           className,
-                                  int              nameAndTypeIndex,
-                                  ClassFile        referencedClassFile,
-                                  MemberInfo       referencedMemberInfo)
+    public int addMethodrefConstant(ProgramClass programClass,
+                                    String       className,
+                                    int          nameAndTypeIndex,
+                                    Clazz        referencedClass,
+                                    Member       referencedMember)
     {
-        return addMethodrefCpInfo(programClassFile,
-                                  addClassCpInfo(programClassFile,
-                                                 className,
-                                                 referencedClassFile),
-                                  nameAndTypeIndex,
-                                  referencedClassFile,
-                                  referencedMemberInfo);
+        return addMethodrefConstant(programClass,
+                                    addClassConstant(programClass,
+                                                     className,
+                                                     referencedClass),
+                                    nameAndTypeIndex,
+                                    referencedClass,
+                                    referencedMember);
     }
 
 
     /**
-     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * Finds or creates a MethodrefConstant constant pool entry with the given
      * class constant pool entry index, method name, and descriptor, in the
-     * given class file.
-     * @return the constant pool index of the MethodrefCpInfo.
+     * given class.
+     * @return the constant pool index of   the MethodrefConstant.
      */
-    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
-                                  int              classIndex,
-                                  String           name,
-                                  String           descriptor,
-                                  ClassFile        referencedClassFile,
-                                  MemberInfo       referencedMemberInfo)
+    public int addMethodrefConstant(ProgramClass programClass,
+                                    int          classIndex,
+                                    String       name,
+                                    String       descriptor,
+                                    Clazz        referencedClass,
+                                    Member       referencedMember)
     {
-        return addMethodrefCpInfo(programClassFile,
-                                  classIndex,
-                                  addNameAndTypeCpInfo(programClassFile,
-                                                       name,
-                                                       descriptor),
-                                  referencedClassFile,
-                                  referencedMemberInfo);
+        return addMethodrefConstant(programClass,
+                                    classIndex,
+                                    addNameAndTypeConstant(programClass,
+                                                           name,
+                                                           descriptor),
+                                    referencedClass,
+                                    referencedMember);
     }
 
 
     /**
-     * Finds or creates a MethodrefCpInfo constant pool entry with the given
+     * Finds or creates a MethodrefConstant constant pool entry with the given
      * class constant pool entry index and name and type constant pool entry index
-     * the given class file.
-     * @return the constant pool index of the MethodrefCpInfo.
+     * the given class.
+     * @return the constant pool index of the MethodrefConstant.
      */
-    public int addMethodrefCpInfo(ProgramClassFile programClassFile,
-                                  int              classIndex,
-                                  int              nameAndTypeIndex,
-                                  ClassFile        referencedClassFile,
-                                  MemberInfo       referencedMemberInfo)
+    public int addMethodrefConstant(ProgramClass programClass,
+                                    int          classIndex,
+                                    int          nameAndTypeIndex,
+                                    Clazz        referencedClass,
+                                    Member       referencedMember)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_Methodref)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Methodref)
             {
-                MethodrefCpInfo methodrefCpInfo = (MethodrefCpInfo)cpInfo;
-                if (methodrefCpInfo.u2classIndex       == classIndex &&
-                    methodrefCpInfo.u2nameAndTypeIndex == nameAndTypeIndex)
+                MethodrefConstant methodrefConstant = (MethodrefConstant)constant;
+                if (methodrefConstant.u2classIndex         == classIndex &&
+                    methodrefConstant.u2nameAndTypeIndex   == nameAndTypeIndex)
                 {
                     return index;
                 }
             }
         }
 
-        return addCpInfo(programClassFile,
-                         new MethodrefCpInfo(classIndex,
-                                             nameAndTypeIndex,
-                                             referencedClassFile,
-                                             referencedMemberInfo));
+        return addConstant(programClass,
+                           new MethodrefConstant(classIndex,
+                                                 nameAndTypeIndex,
+                                                 referencedClass,
+                                                 referencedMember));
     }
 
 
     /**
-     * Finds or creates a ClassCpInfo constant pool entry for the given class,
-     * in the given class file.
-     * @return the constant pool index of the ClassCpInfo.
+     * Finds or creates a ClassConstant constant pool entry for the given class,
+     * in the given class.
+     * @return the constant pool index of the ClassConstant.
      */
-    public int addClassCpInfo(ProgramClassFile programClassFile,
-                              ClassFile        referencedClassFile)
+    public int addClassConstant(ProgramClass programClass,
+                                Clazz        referencedClass)
     {
-        return addClassCpInfo(programClassFile,
-                              referencedClassFile.getName(),
-                              referencedClassFile);
+        return addClassConstant(programClass,
+                                referencedClass.getName(),
+                                referencedClass);
     }
 
 
     /**
-     * Finds or creates a ClassCpInfo constant pool entry with the given name,
-     * in the given class file.
-     * @return the constant pool index of the ClassCpInfo.
+     * Finds or creates a ClassConstant constant pool entry with the given name,
+     * in the given class.
+     * @return the constant pool index of the ClassConstant.
      */
-    public int addClassCpInfo(ProgramClassFile programClassFile,
-                              String           name,
-                              ClassFile        referencedClassFile)
+    public int addClassConstant(ProgramClass programClass,
+                                String       name,
+                                Clazz        referencedClass)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_Class)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Class)
             {
-                ClassCpInfo classCpInfo = (ClassCpInfo)cpInfo;
-                if (classCpInfo.getName(programClassFile).equals(name))
+                ClassConstant classConstant = (ClassConstant)constant;
+                if (classConstant.getName(programClass).equals(name))
                 {
                     return index;
                 }
             }
         }
 
-        int nameIndex = addUtf8CpInfo(programClassFile, name);
+        int nameIndex = addUtf8Constant(programClass, name);
 
-        return addCpInfo(programClassFile,
-                         new ClassCpInfo(nameIndex,
-                                         referencedClassFile));
+        return addConstant(programClass,
+                           new ClassConstant(nameIndex,
+                                             referencedClass));
     }
 
 
     /**
-     * Finds or creates a NameAndTypeCpInfo constant pool entry with the given
-     * name and type, in the given class file.
-     * @return the constant pool index of the NameAndTypeCpInfo.
+     * Finds or creates a NameAndTypeConstant constant pool entry with the given
+     * name and type, in the given class.
+     * @return the constant pool index of the NameAndTypeConstant.
      */
-    public int addNameAndTypeCpInfo(ProgramClassFile programClassFile,
-                                    String           name,
-                                    String           type)
+    public int addNameAndTypeConstant(ProgramClass programClass,
+                                      String       name,
+                                      String       type)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_NameAndType)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_NameAndType)
             {
-                NameAndTypeCpInfo nameAndTypeCpInfo = (NameAndTypeCpInfo)cpInfo;
-                if (nameAndTypeCpInfo.getName(programClassFile).equals(name) &&
-                    nameAndTypeCpInfo.getType(programClassFile).equals(type))
+                NameAndTypeConstant nameAndTypeConstant = (NameAndTypeConstant)constant;
+                if (nameAndTypeConstant.getName(programClass).equals(name) &&
+                    nameAndTypeConstant.getType(programClass).equals(type))
                 {
                     return index;
                 }
             }
         }
 
-        int nameIndex       = addUtf8CpInfo(programClassFile, name);
-        int descriptorIndex = addUtf8CpInfo(programClassFile, type);
+        int nameIndex       = addUtf8Constant(programClass, name);
+        int descriptorIndex = addUtf8Constant(programClass, type);
 
-        return addCpInfo(programClassFile,
-                         new NameAndTypeCpInfo(nameIndex,
-                                               descriptorIndex));
+        return addConstant(programClass,
+                           new NameAndTypeConstant(nameIndex,
+                                                   descriptorIndex));
     }
 
 
     /**
-     * Finds or creates a Utf8CpInfo constant pool entry for the given string,
-     * in the given class file.
-     * @return the constant pool index of the Utf8CpInfo.
+     * Finds or creates a Utf8Constant constant pool entry for the given string,
+     * in the given class.
+     * @return the constant pool index of the   Utf8Constant.
      */
-    public int addUtf8CpInfo(ProgramClassFile programClassFile,
-                             String           string)
+    public int addUtf8Constant(ProgramClass programClass,
+                               String       string)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Check if the entry already exists.
         for (int index = 1; index < constantPoolCount; index++)
         {
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
-            if (cpInfo != null &&
-                cpInfo.getTag() == ClassConstants.CONSTANT_Utf8)
+            if (constant != null &&
+                constant.getTag() == ClassConstants.CONSTANT_Utf8)
             {
-                Utf8CpInfo utf8CpInfo = (Utf8CpInfo)cpInfo;
-                if (utf8CpInfo.getString().equals(string))
+                Utf8Constant utf8Constant = (Utf8Constant)constant;
+                if (utf8Constant.getString().equals(string))
                 {
                     return index;
                 }
             }
         }
 
-        return addCpInfo(programClassFile, new Utf8CpInfo(string));
+        return addConstant(programClass, new Utf8Constant(string));
     }
 
 
     /**
      * Adds a given constant pool entry to the end of the constant pool
-     * in the given class file.
+     * in the given class.
      * @return the constant pool index for the added entry.
      */
-    public int addCpInfo(ProgramClassFile programClassFile,
-                         CpInfo           cpInfo)
+    public int addConstant(ProgramClass programClass,
+                           Constant     constant)
     {
-        CpInfo[] constantPool      = programClassFile.constantPool;
-        int      constantPoolCount = programClassFile.u2constantPoolCount;
+        int        constantPoolCount = programClass.u2constantPoolCount;
+        Constant[] constantPool      = programClass.constantPool;
 
         // Make sure there is enough space for another constant pool entry.
-        if (constantPoolCount == constantPool.length)
+        if (constantPool.length < constantPoolCount+2)
         {
-            programClassFile.constantPool = new CpInfo[constantPoolCount+1];
+            programClass.constantPool = new Constant[constantPoolCount+2];
             System.arraycopy(constantPool, 0,
-                             programClassFile.constantPool, 0,
+                             programClass.constantPool, 0,
                              constantPoolCount);
-            constantPool = programClassFile.constantPool;
+            constantPool = programClass.constantPool;
         }
 
         if (DEBUG)
         {
-            System.out.println(programClassFile.getName()+": adding ["+cpInfo.getClass().getName()+"] at index "+programClassFile.u2constantPoolCount);
+            System.out.println(programClass.getName()+": adding ["+constant.getClass().getName()+"] at index "+programClass.u2constantPoolCount);
         }
 
-        // Create a new Utf8CpInfo for the given string.
-        constantPool[programClassFile.u2constantPoolCount++] = cpInfo;
+        // Create a new Utf8Constant for the given string.
+        constantPool[programClass.u2constantPoolCount++] = constant;
+
+        // Long constants and double constants take up two entries in the
+        // constant pool.
+        int tag = constant.getTag();
+        if (tag == ClassConstants.CONSTANT_Long ||
+            tag == ClassConstants.CONSTANT_Double)
+        {
+            constantPool[programClass.u2constantPoolCount++] = null;
+        }
 
         return constantPoolCount;
     }
diff --git a/src/proguard/classfile/editor/ConstantPoolRemapper.java b/src/proguard/classfile/editor/ConstantPoolRemapper.java
index 245894f..8005ee9 100644
--- a/src/proguard/classfile/editor/ConstantPoolRemapper.java
+++ b/src/proguard/classfile/editor/ConstantPoolRemapper.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantPoolRemapper.java,v 1.11.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,536 +23,572 @@ package proguard.classfile.editor;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.*;
 
 /**
- * This ClassFileVisitor remaps all possible references to constant pool entries
+ * This ClassVisitor remaps all possible references to constant pool entries
  * of the classes that it visits, based on a given index map. It is assumed that
  * the constant pool entries themselves have already been remapped.
  *
  * @author Eric Lafortune
  */
 public class ConstantPoolRemapper
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
              InstructionVisitor,
              InnerClassesInfoVisitor,
              ExceptionInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
              LocalVariableInfoVisitor,
              LocalVariableTypeInfoVisitor,
              AnnotationVisitor,
              ElementValueVisitor
 {
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-
-    private int[] cpIndexMap;
-
+    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
 
-    /**
-     * Creates a new ConstantPoolRemapper.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public ConstantPoolRemapper(int codeLength)
-    {
-        codeAttrInfoEditor = new CodeAttrInfoEditor(codeLength);
-    }
+    private int[] constantIndexMap;
 
 
     /**
      * Sets the given mapping of old constant pool entry indexes to their new
      * indexes.
      */
-    public void setCpIndexMap(int[] cpIndexMap)
+    public void setConstantIndexMap(int[] constantIndexMap)
     {
-        this.cpIndexMap = cpIndexMap;
+        this.constantIndexMap = constantIndexMap;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Remap the local constant pool references.
-        programClassFile.u2thisClass  = remapCpIndex(programClassFile.u2thisClass);
-        programClassFile.u2superClass = remapCpIndex(programClassFile.u2superClass);
+        programClass.u2thisClass  = remapConstantIndex(programClass.u2thisClass);
+        programClass.u2superClass = remapConstantIndex(programClass.u2superClass);
 
-        remapCpIndexArray(programClassFile.u2interfaces,
-                          programClassFile.u2interfacesCount);
+        remapConstantIndexArray(programClass.u2interfaces,
+                                programClass.u2interfacesCount);
 
         // Remap the references of the contant pool entries themselves.
-        programClassFile.constantPoolEntriesAccept(this);
+        programClass.constantPoolEntriesAccept(this);
 
         // Remap the references in all fields, methods, and attributes.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-        programClassFile.attributesAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+        programClass.attributesAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        classCpInfo.u2nameIndex =
-            remapCpIndex(classCpInfo.u2nameIndex);
+        classConstant.u2nameIndex =
+            remapConstantIndex(classConstant.u2nameIndex);
     }
 
 
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
     {
         // Nothing to do.
     }
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
-        fieldrefCpInfo.u2classIndex =
-            remapCpIndex(fieldrefCpInfo.u2classIndex);
-        fieldrefCpInfo.u2nameAndTypeIndex =
-            remapCpIndex(fieldrefCpInfo.u2nameAndTypeIndex);
+        fieldrefConstant.u2classIndex =
+            remapConstantIndex(fieldrefConstant.u2classIndex);
+        fieldrefConstant.u2nameAndTypeIndex =
+            remapConstantIndex(fieldrefConstant.u2nameAndTypeIndex);
     }
 
 
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
     {
         // Nothing to do.
     }
 
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
     {
         // Nothing to do.
     }
 
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
-        interfaceMethodrefCpInfo.u2classIndex =
-            remapCpIndex(interfaceMethodrefCpInfo.u2classIndex);
-        interfaceMethodrefCpInfo.u2nameAndTypeIndex =
-            remapCpIndex(interfaceMethodrefCpInfo.u2nameAndTypeIndex);
+        interfaceMethodrefConstant.u2classIndex =
+            remapConstantIndex(interfaceMethodrefConstant.u2classIndex);
+        interfaceMethodrefConstant.u2nameAndTypeIndex =
+            remapConstantIndex(interfaceMethodrefConstant.u2nameAndTypeIndex);
     }
 
 
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
     {
         // Nothing to do.
     }
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
-        methodrefCpInfo.u2classIndex =
-            remapCpIndex(methodrefCpInfo.u2classIndex);
-        methodrefCpInfo.u2nameAndTypeIndex =
-            remapCpIndex(methodrefCpInfo.u2nameAndTypeIndex);
+        methodrefConstant.u2classIndex =
+            remapConstantIndex(methodrefConstant.u2classIndex);
+        methodrefConstant.u2nameAndTypeIndex =
+            remapConstantIndex(methodrefConstant.u2nameAndTypeIndex);
     }
 
 
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
     {
-        nameAndTypeCpInfo.u2nameIndex =
-            remapCpIndex(nameAndTypeCpInfo.u2nameIndex);
-        nameAndTypeCpInfo.u2descriptorIndex =
-            remapCpIndex(nameAndTypeCpInfo.u2descriptorIndex);
+        nameAndTypeConstant.u2nameIndex =
+            remapConstantIndex(nameAndTypeConstant.u2nameIndex);
+        nameAndTypeConstant.u2descriptorIndex =
+            remapConstantIndex(nameAndTypeConstant.u2descriptorIndex);
     }
 
 
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-        stringCpInfo.u2stringIndex =
-            remapCpIndex(stringCpInfo.u2stringIndex);
+        stringConstant.u2stringIndex =
+            remapConstantIndex(stringConstant.u2stringIndex);
     }
 
 
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
     {
         // Nothing to do.
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        visitMemberInfo(programClassFile, programFieldInfo);
+        visitMember(programClass, programField);
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        visitMemberInfo(programClassFile, programMethodInfo);
+        visitMember(programClass, programMethod);
     }
 
 
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    private void visitMember(ProgramClass programClass, ProgramMember programMember)
     {
         // Remap the local constant pool references.
-        programMemberInfo.u2nameIndex =
-            remapCpIndex(programMemberInfo.u2nameIndex);
-        programMemberInfo.u2descriptorIndex =
-            remapCpIndex(programMemberInfo.u2descriptorIndex);
+        programMember.u2nameIndex =
+            remapConstantIndex(programMember.u2nameIndex);
+        programMember.u2descriptorIndex =
+            remapConstantIndex(programMember.u2descriptorIndex);
 
         // Remap the constant pool references of the remaining attributes.
-        programMemberInfo.attributesAccept(programClassFile, this);
+        programMember.attributesAccept(programClass, this);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
-        // Library class files are left unchanged.
+        // Library classes are left unchanged.
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        // Library class files are left unchanged.
+        // Library classes are left unchanged.
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
     {
-        unknownAttrInfo.u2attrNameIndex =
-            remapCpIndex(unknownAttrInfo.u2attrNameIndex);
+        unknownAttribute.u2attributeNameIndex =
+            remapConstantIndex(unknownAttribute.u2attributeNameIndex);
 
         // There's not much else we can do with unknown attributes.
     }
 
 
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
     {
-        innerClassesAttrInfo.u2attrNameIndex =
-            remapCpIndex(innerClassesAttrInfo.u2attrNameIndex);
-
-        // Remap the constant pool references of the inner classes.
-        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+        sourceFileAttribute.u2attributeNameIndex =
+            remapConstantIndex(sourceFileAttribute.u2attributeNameIndex);
+        sourceFileAttribute.u2sourceFileIndex =
+            remapConstantIndex(sourceFileAttribute.u2sourceFileIndex);
     }
 
 
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
     {
-        enclosingMethodAttrInfo.u2attrNameIndex =
-            remapCpIndex(enclosingMethodAttrInfo.u2attrNameIndex);
-        enclosingMethodAttrInfo.u2classIndex =
-            remapCpIndex(enclosingMethodAttrInfo.u2classIndex);
-        enclosingMethodAttrInfo.u2nameAndTypeIndex =
-            remapCpIndex(enclosingMethodAttrInfo.u2nameAndTypeIndex);
+        sourceDirAttribute.u2attributeNameIndex =
+            remapConstantIndex(sourceDirAttribute.u2attributeNameIndex);
+        sourceDirAttribute.u2sourceDirIndex       =
+            remapConstantIndex(sourceDirAttribute.u2sourceDirIndex);
     }
 
 
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
     {
-        constantValueAttrInfo.u2attrNameIndex =
-            remapCpIndex(constantValueAttrInfo.u2attrNameIndex);
-        constantValueAttrInfo.u2constantValueIndex =
-            remapCpIndex(constantValueAttrInfo.u2constantValueIndex);
+        innerClassesAttribute.u2attributeNameIndex =
+            remapConstantIndex(innerClassesAttribute.u2attributeNameIndex);
+
+        // Remap the constant pool references of the inner classes.
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
     }
 
 
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
-        exceptionsAttrInfo.u2attrNameIndex =
-            remapCpIndex(exceptionsAttrInfo.u2attrNameIndex);
-
-        // Remap the constant pool references of the exceptions.
-        remapCpIndexArray(exceptionsAttrInfo.u2exceptionIndexTable,
-                          exceptionsAttrInfo.u2numberOfExceptions);
+        enclosingMethodAttribute.u2attributeNameIndex =
+            remapConstantIndex(enclosingMethodAttribute.u2attributeNameIndex);
+        enclosingMethodAttribute.u2classIndex =
+            remapConstantIndex(enclosingMethodAttribute.u2classIndex);
+        enclosingMethodAttribute.u2nameAndTypeIndex =
+            remapConstantIndex(enclosingMethodAttribute.u2nameAndTypeIndex);
     }
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
     {
-        codeAttrInfo.u2attrNameIndex =
-            remapCpIndex(codeAttrInfo.u2attrNameIndex);
-
-        // Initially, the code attribute editor doesn't contain any changes.
-        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
+        deprecatedAttribute.u2attributeNameIndex =
+            remapConstantIndex(deprecatedAttribute.u2attributeNameIndex);
+    }
 
-        // Remap the constant pool references of the instructions.
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
 
-        // Apply the code atribute editor. It will only contain any changes if
-        // the code length is changing at any point.
-        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
-
-        // Remap the constant pool references of the exceptions and attributes.
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        syntheticAttribute.u2attributeNameIndex =
+            remapConstantIndex(syntheticAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
     {
-        lineNumberTableAttrInfo.u2attrNameIndex =
-            remapCpIndex(lineNumberTableAttrInfo.u2attrNameIndex);
+        signatureAttribute.u2attributeNameIndex =
+            remapConstantIndex(signatureAttribute.u2attributeNameIndex);
+        signatureAttribute.u2signatureIndex       =
+            remapConstantIndex(signatureAttribute.u2signatureIndex);
     }
 
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
     {
-        localVariableTableAttrInfo.u2attrNameIndex =
-            remapCpIndex(localVariableTableAttrInfo.u2attrNameIndex);
-
-        // Remap the constant pool references of the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        constantValueAttribute.u2attributeNameIndex =
+            remapConstantIndex(constantValueAttribute.u2attributeNameIndex);
+        constantValueAttribute.u2constantValueIndex =
+            remapConstantIndex(constantValueAttribute.u2constantValueIndex);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
     {
-        localVariableTypeTableAttrInfo.u2attrNameIndex =
-            remapCpIndex(localVariableTypeTableAttrInfo.u2attrNameIndex);
+        exceptionsAttribute.u2attributeNameIndex =
+            remapConstantIndex(exceptionsAttribute.u2attributeNameIndex);
 
-        // Remap the constant pool references of the local variables.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        // Remap the constant pool references of the exceptions.
+        remapConstantIndexArray(exceptionsAttribute.u2exceptionIndexTable,
+                                exceptionsAttribute.u2exceptionIndexTableLength);
     }
 
 
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        sourceFileAttrInfo.u2attrNameIndex =
-            remapCpIndex(sourceFileAttrInfo.u2attrNameIndex);
-        sourceFileAttrInfo.u2sourceFileIndex =
-            remapCpIndex(sourceFileAttrInfo.u2sourceFileIndex);
-    }
+        codeAttribute.u2attributeNameIndex =
+            remapConstantIndex(codeAttribute.u2attributeNameIndex);
 
+        // Initially, the code attribute editor doesn't contain any changes.
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
 
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
-    {
-        sourceDirAttrInfo.u2attrNameIndex =
-            remapCpIndex(sourceDirAttrInfo.u2attrNameIndex);
-        sourceDirAttrInfo.u2sourceDirIndex =
-            remapCpIndex(sourceDirAttrInfo.u2sourceDirIndex);
+        // Remap the constant pool references of the instructions.
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        // Apply the code atribute editor. It will only contain any changes if
+        // the code length is changing at any point.
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Remap the constant pool references of the exceptions and attributes.
+        codeAttribute.exceptionsAccept(clazz, method, this);
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
     {
-        deprecatedAttrInfo.u2attrNameIndex =
-            remapCpIndex(deprecatedAttrInfo.u2attrNameIndex);
+        stackMapAttribute.u2attributeNameIndex =
+            remapConstantIndex(stackMapAttribute.u2attributeNameIndex);
+
+        // Remap the constant pool references of the stack map frames.
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
     {
-        syntheticAttrInfo.u2attrNameIndex =
-            remapCpIndex(syntheticAttrInfo.u2attrNameIndex);
+        stackMapTableAttribute.u2attributeNameIndex =
+            remapConstantIndex(stackMapTableAttribute.u2attributeNameIndex);
+
+        // Remap the constant pool references of the stack map frames.
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
-        signatureAttrInfo.u2attrNameIndex =
-            remapCpIndex(signatureAttrInfo.u2attrNameIndex);
-        signatureAttrInfo.u2signatureIndex =
-            remapCpIndex(signatureAttrInfo.u2signatureIndex);
+        lineNumberTableAttribute.u2attributeNameIndex =
+            remapConstantIndex(lineNumberTableAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-        runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex =
-            remapCpIndex(runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+        localVariableTableAttribute.u2attributeNameIndex =
+            remapConstantIndex(localVariableTableAttribute.u2attributeNameIndex);
 
-        // Remap the constant pool references of the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Remap the constant pool references of the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-        runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex =
-            remapCpIndex(runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+        localVariableTypeTableAttribute.u2attributeNameIndex =
+            remapConstantIndex(localVariableTypeTableAttribute.u2attributeNameIndex);
 
-        // Remap the constant pool references of the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Remap the constant pool references of the local variables.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
     {
-        runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex =
-            remapCpIndex(runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+        annotationsAttribute.u2attributeNameIndex =
+            remapConstantIndex(annotationsAttribute.u2attributeNameIndex);
 
         // Remap the constant pool references of the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        annotationsAttribute.annotationsAccept(clazz, this);
     }
 
 
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
     {
-        runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex =
-            remapCpIndex(runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+        parameterAnnotationsAttribute.u2attributeNameIndex =
+            remapConstantIndex(parameterAnnotationsAttribute.u2attributeNameIndex);
 
         // Remap the constant pool references of the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
     }
 
 
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
     {
-        annotationDefaultAttrInfo.u2attrNameIndex =
-            remapCpIndex(annotationDefaultAttrInfo.u2attrNameIndex);
+        annotationDefaultAttribute.u2attributeNameIndex =
+            remapConstantIndex(annotationDefaultAttribute.u2attributeNameIndex);
 
         // Remap the constant pool references of the annotations.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
     }
 
 
     // Implementations for InnerClassesInfoVisitor.
 
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
     {
-        if (innerClassesInfo.u2innerClassInfoIndex != 0)
+        if (innerClassesInfo.u2innerClassIndex != 0)
         {
-            innerClassesInfo.u2innerClassInfoIndex =
-                remapCpIndex(innerClassesInfo.u2innerClassInfoIndex);
+            innerClassesInfo.u2innerClassIndex =
+                remapConstantIndex(innerClassesInfo.u2innerClassIndex);
         }
 
-        if (innerClassesInfo.u2outerClassInfoIndex != 0)
+        if (innerClassesInfo.u2outerClassIndex != 0)
         {
-            innerClassesInfo.u2outerClassInfoIndex =
-                remapCpIndex(innerClassesInfo.u2outerClassInfoIndex);
+            innerClassesInfo.u2outerClassIndex =
+                remapConstantIndex(innerClassesInfo.u2outerClassIndex);
         }
 
         if (innerClassesInfo.u2innerNameIndex != 0)
         {
             innerClassesInfo.u2innerNameIndex =
-                remapCpIndex(innerClassesInfo.u2innerNameIndex);
+                remapConstantIndex(innerClassesInfo.u2innerNameIndex);
         }
     }
 
 
     // Implementations for ExceptionInfoVisitor.
 
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
         if (exceptionInfo.u2catchType != 0)
         {
             exceptionInfo.u2catchType =
-                remapCpIndex(exceptionInfo.u2catchType);
+                remapConstantIndex(exceptionInfo.u2catchType);
         }
     }
 
 
+    // 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)
+    {
+        // Is the new constant pool index different from the original one?
+        int newConstantIndex = remapConstantIndex(constantInstruction.constantIndex);
+        if (newConstantIndex != constantInstruction.constantIndex)
+        {
+            // Replace the instruction.
+            Instruction replacementInstruction =
+                new ConstantInstruction(constantInstruction.opcode,
+                                        newConstantIndex,
+                                        constantInstruction.constant).shrink();
+
+            codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
+        }
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) {}
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        // Remap the constant pool references of the verification types.
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        // Remap the constant pool references of the verification types.
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        // Remap the constant pool references of the verification types.
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
+
+
+    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
+    {
+        objectType.u2classIndex =
+            remapConstantIndex(objectType.u2classIndex);
+    }
+
+
     // Implementations for LocalVariableInfoVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
     {
         localVariableInfo.u2nameIndex =
-            remapCpIndex(localVariableInfo.u2nameIndex);
+            remapConstantIndex(localVariableInfo.u2nameIndex);
         localVariableInfo.u2descriptorIndex =
-            remapCpIndex(localVariableInfo.u2descriptorIndex);
+            remapConstantIndex(localVariableInfo.u2descriptorIndex);
     }
 
 
     // Implementations for LocalVariableTypeInfoVisitor.
 
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
     {
         localVariableTypeInfo.u2nameIndex =
-            remapCpIndex(localVariableTypeInfo.u2nameIndex);
-        localVariableTypeInfo.u2signatureIndex =
-            remapCpIndex(localVariableTypeInfo.u2signatureIndex);
+            remapConstantIndex(localVariableTypeInfo.u2nameIndex);
+        localVariableTypeInfo.u2signatureIndex       =
+            remapConstantIndex(localVariableTypeInfo.u2signatureIndex);
     }
 
 
     // Implementations for AnnotationVisitor.
 
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
     {
         annotation.u2typeIndex =
-            remapCpIndex(annotation.u2typeIndex);
+            remapConstantIndex(annotation.u2typeIndex);
 
         // Remap the constant pool references of the element values.
-        annotation.elementValuesAccept(classFile, this);
+        annotation.elementValuesAccept(clazz, this);
     }
 
 
     // Implementations for ElementValueVisitor.
 
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
     {
-        constantElementValue.u2elementName =
-            remapCpIndex(constantElementValue.u2elementName);
+        constantElementValue.u2elementNameIndex =
+            remapConstantIndex(constantElementValue.u2elementNameIndex);
         constantElementValue.u2constantValueIndex =
-            remapCpIndex(constantElementValue.u2constantValueIndex);
+            remapConstantIndex(constantElementValue.u2constantValueIndex);
     }
 
 
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
     {
-        enumConstantElementValue.u2elementName =
-            remapCpIndex(enumConstantElementValue.u2elementName);
+        enumConstantElementValue.u2elementNameIndex =
+            remapConstantIndex(enumConstantElementValue.u2elementNameIndex);
         enumConstantElementValue.u2typeNameIndex =
-            remapCpIndex(enumConstantElementValue.u2typeNameIndex);
+            remapConstantIndex(enumConstantElementValue.u2typeNameIndex);
         enumConstantElementValue.u2constantNameIndex =
-            remapCpIndex(enumConstantElementValue.u2constantNameIndex);
+            remapConstantIndex(enumConstantElementValue.u2constantNameIndex);
     }
 
 
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
     {
-        classElementValue.u2elementName =
-            remapCpIndex(classElementValue.u2elementName);
-        classElementValue.u2classInfoIndex =
-            remapCpIndex(classElementValue.u2classInfoIndex);
+        classElementValue.u2elementNameIndex =
+            remapConstantIndex(classElementValue.u2elementNameIndex);
+        classElementValue.u2classInfoIndex       =
+            remapConstantIndex(classElementValue.u2classInfoIndex);
     }
 
 
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
     {
-        annotationElementValue.u2elementName =
-            remapCpIndex(annotationElementValue.u2elementName);
+        annotationElementValue.u2elementNameIndex =
+            remapConstantIndex(annotationElementValue.u2elementNameIndex);
 
         // Remap the constant pool references of the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
+        annotationElementValue.annotationAccept(clazz, this);
     }
 
 
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
     {
-        arrayElementValue.u2elementName =
-            remapCpIndex(arrayElementValue.u2elementName);
+        arrayElementValue.u2elementNameIndex =
+            remapConstantIndex(arrayElementValue.u2elementNameIndex);
 
         // Remap the constant pool references of the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        // Is the new constant pool index different from the original one?
-        int newCpIndex = remapCpIndex(cpInstruction.cpIndex);
-        if (newCpIndex != cpInstruction.cpIndex)
-        {
-            // Replace the instruction.
-            cpInstruction = new CpInstruction().copy(cpInstruction);
-            cpInstruction.cpIndex = newCpIndex;
-            cpInstruction.shrink();
-
-            codeAttrInfoEditor.replaceInstruction(offset, cpInstruction);
-        }
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
     }
 
 
@@ -561,11 +597,11 @@ public class ConstantPoolRemapper
     /**
      * Remaps all constant pool indices in the given array.
      */
-    private void remapCpIndexArray(int[] array, int length)
+    private void remapConstantIndexArray(int[] array, int length)
     {
         for (int index = 0; index < length; index++)
         {
-            array[index] = remapCpIndex(array[index]);
+            array[index] = remapConstantIndex(array[index]);
         }
     }
 
@@ -574,8 +610,8 @@ public class ConstantPoolRemapper
      * Returns the new constant pool index of the entry at the
      * given index.
      */
-    private int remapCpIndex(int cpIndex)
+    private int remapConstantIndex(int constantIndex)
     {
-        return cpIndexMap[cpIndex];
+        return constantIndexMap[constantIndex];
     }
 }
diff --git a/src/proguard/classfile/editor/ConstantPoolSorter.java b/src/proguard/classfile/editor/ConstantPoolSorter.java
index f89c635..4ce4cb0 100644
--- a/src/proguard/classfile/editor/ConstantPoolSorter.java
+++ b/src/proguard/classfile/editor/ConstantPoolSorter.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantPoolSorter.java,v 1.6.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,54 +20,43 @@
  */
 package proguard.classfile.editor;
 
-import java.util.Arrays;
-
 import proguard.classfile.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.constant.Constant;
+import proguard.classfile.visitor.ClassVisitor;
 
+import java.util.Arrays;
 
 /**
- * This ClassFileVisitor sorts the constant pool entries of the classes that
- * it visits. The sorting is based on the types of the constant pool entries
- * in the first place, and on their contents in the second place.
+ * This ClassVisitor sorts the constant pool entries of the classes that
+ * it visits. The sorting order is based on the types of the constant pool
+ * entries in the first place, and on their contents in the second place.
  *
  * @author Eric Lafortune
  */
-public class ConstantPoolSorter implements ClassFileVisitor
+public class ConstantPoolSorter implements ClassVisitor
 {
-    private int[]                cpIndexMap;
-    private ComparableCpInfo[]   comparableConstantPool;
-    private ConstantPoolRemapper constantPoolRemapper;
-
+    private int[]                constantIndexMap       = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
+    private ComparableConstant[] comparableConstantPool = new ComparableConstant[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
 
-    /**
-     * Creates a new ConstantPoolSorter.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public ConstantPoolSorter(int codeLength)
-    {
-        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
-    }
+    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Sort the constant pool and set up an index map.
-        sortConstantPool(programClassFile,
-                         programClassFile.constantPool,
-                         programClassFile.u2constantPoolCount);
+        sortConstantPool(programClass,
+                         programClass.constantPool,
+                         programClass.u2constantPoolCount);
 
         // Remap all constant pool references.
-        constantPoolRemapper.setCpIndexMap(cpIndexMap);
-        constantPoolRemapper.visitProgramClassFile(programClassFile);
+        constantPoolRemapper.setConstantIndexMap(constantIndexMap);
+        constantPoolRemapper.visitProgramClass(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
     }
 
@@ -77,52 +66,51 @@ public class ConstantPoolSorter implements ClassFileVisitor
     /**
      * Sorts the given constant pool.
      */
-    private void sortConstantPool(ClassFile classFile, CpInfo[] constantPool, int length)
+    private void sortConstantPool(Clazz clazz, Constant[] constantPool, int length)
     {
-        if (cpIndexMap == null ||
-            cpIndexMap.length < length)
+        if (constantIndexMap.length < length)
         {
-            cpIndexMap             = new int[length];
-            comparableConstantPool = new ComparableCpInfo[length];
+            constantIndexMap       = new int[length];
+            comparableConstantPool = new ComparableConstant[length];
         }
 
         // Initialize an array whose elements can be compared.
         for (int oldIndex = 1; oldIndex < length; oldIndex++)
         {
-            CpInfo cpInfo = constantPool[oldIndex];
+            Constant constant = constantPool[oldIndex];
 
             // Long entries take up two entries, the second of which is null.
-            if (cpInfo == null)
+            if (constant == null)
             {
-                cpInfo = constantPool[oldIndex-1];
+                constant = constantPool[oldIndex-1];
             }
 
-            comparableConstantPool[oldIndex] = new ComparableCpInfo(classFile,
-                                                                    oldIndex,
-                                                                    cpInfo);
+            comparableConstantPool[oldIndex] = new ComparableConstant(clazz,
+                                                                      oldIndex,
+                                                                      constant);
         }
 
         // Sort the array.
         Arrays.sort(comparableConstantPool, 1, length);
 
         // Save the sorted elements.
-        CpInfo previousCpInfo = null;
+        Constant previousConstant = null;
         for (int newIndex = 1; newIndex < length; newIndex++)
         {
-            ComparableCpInfo comparableCpInfo = comparableConstantPool[newIndex];
+            ComparableConstant comparableConstant = comparableConstantPool[newIndex];
 
             // Fill out the map array.
-            int oldIndex = comparableCpInfo.getIndex();
-            cpIndexMap[oldIndex] = newIndex;
+            int oldIndex = comparableConstant.getIndex();
+            constantIndexMap[oldIndex] = newIndex;
 
             // Copy the sorted constant pool entry over to the constant pool.
             // Long entries take up two entries, the second of which is null.
-            CpInfo cpInfo = comparableCpInfo.getCpInfo();
-            constantPool[newIndex] = cpInfo != previousCpInfo ?
-                cpInfo :
+            Constant constant = comparableConstant.getConstant();
+            constantPool[newIndex] = constant != previousConstant ?
+                constant :
                 null;
 
-            previousCpInfo = cpInfo;
+            previousConstant = constant;
         }
     }
 }
diff --git a/src/proguard/classfile/editor/ExceptionAdder.java b/src/proguard/classfile/editor/ExceptionAdder.java
new file mode 100644
index 0000000..91e9a22
--- /dev/null
+++ b/src/proguard/classfile/editor/ExceptionAdder.java
@@ -0,0 +1,67 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.ExceptionsAttribute;
+import proguard.classfile.constant.ClassConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This ConstantVisitor adds all class constants that it visits to the given
+ * target exceptions attribute.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionAdder
+extends      SimplifiedVisitor
+implements   ConstantVisitor
+{
+    private final ExceptionsAttribute targetExceptionsAttribute;
+
+    private final ConstantAdder    constantAdder    = new ConstantAdder();
+    private final ExceptionsEditor exceptionsEditor = new ExceptionsEditor();
+
+
+    /**
+     * Creates a new MemberAdder that will copy methods into the given target
+     * class.
+     */
+  public ExceptionAdder(ProgramClass        targetClass,
+                        ExceptionsAttribute targetExceptionsAttribute)
+    {
+        this.targetExceptionsAttribute = targetExceptionsAttribute;
+
+        constantAdder.setTargetClass(targetClass);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        constantAdder.visitClassConstant(clazz, classConstant);
+
+        exceptionsEditor.addException(targetExceptionsAttribute,
+                                      constantAdder.getConstantIndex());
+    }
+}
diff --git a/src/proguard/classfile/editor/ExceptionsEditor.java b/src/proguard/classfile/editor/ExceptionsEditor.java
new file mode 100644
index 0000000..f84020d
--- /dev/null
+++ b/src/proguard/classfile/editor/ExceptionsEditor.java
@@ -0,0 +1,55 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.attribute.ExceptionsAttribute;
+
+/**
+ * This class can add exceptions to exceptions attributes. Exceptions to be
+ * added must have been added to the constant pool filled out beforehand.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionsEditor
+{
+    /**
+     * Adds a given exception to the given exceptions attribute.
+     */
+  public void addException(ExceptionsAttribute exceptionsAttribute,
+                           int                 exceptionIndex)
+    {
+        int   exceptionIndexTableLength = exceptionsAttribute.u2exceptionIndexTableLength;
+        int[] exceptionIndexTable       = exceptionsAttribute.u2exceptionIndexTable;
+
+        // Make sure there is enough space for the new exception.
+        if (exceptionIndexTable.length <= exceptionIndexTableLength)
+        {
+            exceptionsAttribute.u2exceptionIndexTable = new int[exceptionIndexTableLength+1];
+            System.arraycopy(exceptionIndexTable, 0,
+                             exceptionsAttribute.u2exceptionIndexTable, 0,
+                             exceptionIndexTableLength);
+            exceptionIndexTable = exceptionsAttribute.u2exceptionIndexTable;
+        }
+
+        // Add the exception.
+        exceptionIndexTable[exceptionsAttribute.u2exceptionIndexTableLength++] = exceptionIndex;
+    }
+}
diff --git a/src/proguard/classfile/editor/InstructionWriter.java b/src/proguard/classfile/editor/InstructionWriter.java
index f73ba94..c82fa76 100644
--- a/src/proguard/classfile/editor/InstructionWriter.java
+++ b/src/proguard/classfile/editor/InstructionWriter.java
@@ -1,45 +1,48 @@
-/* $Id: InstructionWriter.java,v 1.1.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
- * This program is free software; you can redistribute it and/or modify it
+ * This library is 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
+ * This library 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.
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 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
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
 import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
  * This InstructionVisitor writes out the instructions that it visits,
- * collecting instructions that have to be widened. As an AttrInfoVisitor,
+ * collecting instructions that have to be widened. As an AttributeVisitor,
  * it then applies the collected changes. The process will be repeated
  * recursively, if necessary.
  *
  * @author Eric Lafortune
  */
 public class InstructionWriter
-  implements InstructionVisitor,
-             AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             AttributeVisitor
 {
     private int codeLength;
 
-    private CodeAttrInfoEditor codeAttrInfoEditor;
+    private CodeAttributeEditor codeAttributeEditor;
 
 
     /**
@@ -51,52 +54,54 @@ public class InstructionWriter
         this.codeLength = codeLength;
 
         // The code attribute editor has to be created lazily.
-        if (codeAttrInfoEditor != null)
+        if (codeAttributeEditor != null)
         {
-            codeAttrInfoEditor.reset(codeLength);
+            codeAttributeEditor.reset(codeLength);
         }
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         // Try to write out the instruction.
         // Simple instructions should always fit.
-        simpleInstruction.write(codeAttrInfo, offset);
+        simpleInstruction.write(codeAttribute, offset);
     }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
         try
         {
             // Try to write out the instruction.
-            cpInstruction.write(codeAttrInfo, offset);
+            constantInstruction.write(codeAttribute, offset);
         }
         catch (IllegalArgumentException exception)
         {
             // Create a new constant instruction that will fit.
             Instruction replacementInstruction =
-                new CpInstruction().copy(cpInstruction).shrink();
+                new ConstantInstruction(constantInstruction.opcode,
+                                        constantInstruction.constantIndex,
+                                        constantInstruction.constant).shrink();
 
             replaceInstruction(offset, replacementInstruction);
 
             // Write out a dummy constant instruction for now.
-            cpInstruction.cpIndex  = 0;
-            cpInstruction.constant = 0;
-            cpInstruction.write(codeAttrInfo, offset);
+            constantInstruction.constantIndex = 0;
+            constantInstruction.constant      = 0;
+            constantInstruction.write(codeAttribute, offset);
         }
     }
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         try
         {
             // Try to write out the instruction.
-            variableInstruction.write(codeAttrInfo, offset);
+            variableInstruction.write(codeAttribute, offset);
         }
         catch (IllegalArgumentException exception)
         {
@@ -104,24 +109,24 @@ public class InstructionWriter
             Instruction replacementInstruction =
                 new VariableInstruction(variableInstruction.opcode,
                                         variableInstruction.variableIndex,
-                                        variableInstruction.constant);
+                                        variableInstruction.constant).shrink();
 
             replaceInstruction(offset, replacementInstruction);
 
             // Write out a dummy variable instruction for now.
             variableInstruction.variableIndex = 0;
             variableInstruction.constant      = 0;
-            variableInstruction.write(codeAttrInfo, offset);
+            variableInstruction.write(codeAttribute, offset);
         }
     }
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         try
         {
             // Try to write out the instruction.
-            branchInstruction.write(codeAttrInfo, offset);
+            branchInstruction.write(codeAttribute, offset);
         }
         catch (IllegalArgumentException exception)
         {
@@ -137,7 +142,8 @@ public class InstructionWriter
                 {
                     // Create a new branch instruction that will fit.
                     replacementInstruction =
-                        new BranchInstruction().copy(branchInstruction).shrink();
+                        new BranchInstruction(branchInstruction.opcode,
+                                              branchInstruction.branchOffset).shrink();
 
                     break;
                 }
@@ -188,59 +194,31 @@ public class InstructionWriter
 
             // Write out a dummy branch instruction for now.
             branchInstruction.branchOffset = 0;
-            branchInstruction.write(codeAttrInfo, offset);
+            branchInstruction.write(codeAttribute, offset);
         }
     }
 
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
     {
         // Try to write out the instruction.
-        // Table switch instructions should always fit.
-        tableSwitchInstruction.write(codeAttrInfo, offset);
+        // Switch instructions should always fit.
+        switchInstruction.write(codeAttribute, offset);
     }
 
 
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
-    {
-        // Try to write out the instruction.
-        // Table switch instructions should always fit.
-        lookUpSwitchInstruction.write(codeAttrInfo, offset);
-    }
-
+    // Implementations for AttributeVisitor.
 
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         // Avoid doing any work if nothing is changing anyway.
-        if (codeAttrInfoEditor != null)
+        if (codeAttributeEditor != null)
         {
             // Apply the collected expansions.
-            codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+            codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
 
             // Clear the modifications for the next run.
-            codeAttrInfoEditor = null;
+            codeAttributeEditor = null;
         }
     }
 
@@ -253,10 +231,10 @@ public class InstructionWriter
      */
     private void insertBeforeInstruction(int instructionOffset, Instruction instruction)
     {
-        ensureCodeAttrInfoEditor();
+        ensureCodeAttributeEditor();
 
         // Replace the instruction.
-        codeAttrInfoEditor.insertBeforeInstruction(instructionOffset, instruction);
+        codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction);
     }
 
 
@@ -266,10 +244,10 @@ public class InstructionWriter
      */
     private void replaceInstruction(int instructionOffset, Instruction instruction)
     {
-        ensureCodeAttrInfoEditor();
+        ensureCodeAttributeEditor();
 
         // Replace the instruction.
-        codeAttrInfoEditor.replaceInstruction(instructionOffset, instruction);
+        codeAttributeEditor.replaceInstruction(instructionOffset, instruction);
     }
 
 
@@ -279,21 +257,22 @@ public class InstructionWriter
      */
     private void insertAfterInstruction(int instructionOffset, Instruction instruction)
     {
-        ensureCodeAttrInfoEditor();
+        ensureCodeAttributeEditor();
 
         // Replace the instruction.
-        codeAttrInfoEditor.insertAfterInstruction(instructionOffset, instruction);
+        codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction);
     }
 
 
     /**
      * Makes sure there is a code attribute editor for the given code attribute.
      */
-    private void ensureCodeAttrInfoEditor()
+    private void ensureCodeAttributeEditor()
     {
-        if (codeAttrInfoEditor == null)
+        if (codeAttributeEditor == null)
         {
-            codeAttrInfoEditor = new CodeAttrInfoEditor(codeLength);
+            codeAttributeEditor = new CodeAttributeEditor();
+            codeAttributeEditor.reset(codeLength);
         }
     }
 }
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/editor/InterfaceSorter.java
similarity index 55%
copy from src/proguard/classfile/FieldInfo.java
copy to src/proguard/classfile/editor/InterfaceSorter.java
index d9a4a34..80af353 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/editor/InterfaceSorter.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,30 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
+package proguard.classfile.editor;
 
+import proguard.classfile.*;
+import proguard.classfile.visitor.ClassVisitor;
 
+import java.util.Arrays;
 
 /**
- * Representation of a field from a class file.
+ * This ClassVisitor sorts the interfaces of the classes that it visits.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class InterfaceSorter implements ClassVisitor
 {
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Sort the interfaces.
+        Arrays.sort(programClass.u2interfaces, 0, programClass.u2interfacesCount);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+    }
 }
diff --git a/src/proguard/classfile/editor/MemberAdder.java b/src/proguard/classfile/editor/MemberAdder.java
new file mode 100644
index 0000000..5150059
--- /dev/null
+++ b/src/proguard/classfile/editor/MemberAdder.java
@@ -0,0 +1,141 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This ConstantVisitor adds all class members that it visits to the given
+ * target class.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberAdder
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0];
+
+
+    private final ProgramClass targetClass;
+    private final boolean      copyAttributes;
+
+    private final ConstantAdder constantAdder = new ConstantAdder();
+    private final MembersEditor membersEditor = new MembersEditor();
+
+
+    /**
+     * Creates a new MemberAdder that will copy methods into the given target
+     * class.
+     */
+    public MemberAdder(ProgramClass targetClass, boolean copyAttributes)
+    {
+        this.targetClass    = targetClass;
+        this.copyAttributes = copyAttributes;
+
+        constantAdder.setTargetClass(targetClass);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        String name       = programField.getName(programClass);
+        String descriptor = programField.getDescriptor(programClass);
+
+        if (targetClass.findMethod(name, descriptor) == null)
+        {
+            ProgramField newProgramField = new ProgramField();
+
+            // Copy the access flags.
+            newProgramField.u2accessFlags = programField.u2accessFlags;
+
+            // Make sure the name is set in the constant pool.
+            programClass.constantPoolEntryAccept(programField.u2nameIndex,
+                                                 constantAdder);
+
+            newProgramField.u2nameIndex = constantAdder.getConstantIndex();
+
+            // Make sure the descriptor is set in the constant pool.
+            programClass.constantPoolEntryAccept(programField.u2descriptorIndex,
+                                                 constantAdder);
+
+            newProgramField.u2descriptorIndex = constantAdder.getConstantIndex();
+
+            // Copy the attributes if requested.
+            if (copyAttributes)
+            {
+                programField.attributesAccept(programClass,
+                                              new AttributeAdder(targetClass,
+                                                                 newProgramField));
+            }
+
+            // Actually add the completed field.
+            membersEditor.addField(targetClass, newProgramField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        String name       = programMethod.getName(programClass);
+        String descriptor = programMethod.getDescriptor(programClass);
+
+        if (targetClass.findMethod(name, descriptor) == null)
+        {
+            ProgramMethod newProgramMethod = new ProgramMethod();
+
+            // Copy the access flags.
+            newProgramMethod.u2accessFlags = programMethod.u2accessFlags;
+
+            // Make sure the name is set in the constant pool.
+            programClass.constantPoolEntryAccept(programMethod.u2nameIndex,
+                                                 constantAdder);
+
+            newProgramMethod.u2nameIndex = constantAdder.getConstantIndex();
+
+            // Make sure the descriptor is set in the constant pool.
+            programClass.constantPoolEntryAccept(programMethod.u2descriptorIndex,
+                                                 constantAdder);
+
+            newProgramMethod.u2descriptorIndex = constantAdder.getConstantIndex();
+
+            // Start with an empty list of attributes.
+            newProgramMethod.u2attributesCount = 0;
+            newProgramMethod.attributes        = EMPTY_ATTRIBUTES;
+
+            // Copy the attributes if requested.
+            if (copyAttributes)
+            {
+                programMethod.attributesAccept(programClass,
+                                               new AttributeAdder(targetClass,
+                                                                  newProgramMethod));
+            }
+
+            // Actually add the completed method.
+            membersEditor.addMethod(targetClass, newProgramMethod);
+        }
+    }
+}
diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/src/proguard/classfile/editor/MemberReferenceFixer.java
index e31afd5..cee33af 100644
--- a/src/proguard/classfile/editor/MemberReferenceFixer.java
+++ b/src/proguard/classfile/editor/MemberReferenceFixer.java
@@ -1,6 +1,6 @@
-/* $Id: MemberReferenceFixer.java,v 1.4.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,147 +23,158 @@ package proguard.classfile.editor;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.util.ClassUtil;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
 
 /**
- * This ClassFileVisitor fixes constant pool field and method references to
+ * This ClassVisitor fixes constant pool field and method references to
  * fields and methods whose names or descriptors have changed.
  *
  * @author Eric Lafortune
  */
 public class MemberReferenceFixer
-implements   ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
              AnnotationVisitor,
              ElementValueVisitor
 {
     private static final boolean DEBUG = false;
 
 
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
-    private StackSizeUpdater   stackSizeUpdater;
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private final StackSizeUpdater   stackSizeUpdater   = new StackSizeUpdater();
 
     // Parameter for the visitor methods.
-    private int cpIndex;
+    private int constantIndex;
 
     // Return values for the visitor methods.
     private boolean isInterfaceMethod;
     private boolean stackSizesMayHaveChanged;
 
 
-    /**
-     * Creates a new MemberReferenceFixer.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public MemberReferenceFixer(int codeLength)
-    {
-        stackSizeUpdater = new StackSizeUpdater(codeLength);
-    }
-
+    // Implementations for ClassVisitor.
 
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         stackSizesMayHaveChanged = false;
 
         // Fix the constant pool entries.
-        for (int index = 1; index < programClassFile.u2constantPoolCount; index++)
+        for (int index = 1; index < programClass.u2constantPoolCount; index++)
         {
-            CpInfo cpInfo = programClassFile.constantPool[index];
-            if (cpInfo != null)
+            Constant constant = programClass.constantPool[index];
+            if (constant != null)
             {
                 // Fix the entry, replacing it entirely if needed.
-                this.cpIndex = index;
+                this.constantIndex = index;
 
-                cpInfo.accept(programClassFile, this);
+                constant.accept(programClass, this);
             }
         }
 
         // Fix class members.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
 
         // Fix the attributes.
-        programClassFile.attributesAccept(this);
+        programClass.attributesAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-    }
+        // Does the string refer to a class member, due to a
+        // Class.get[Declared]{Field,Method} construct?
+        Member referencedMember = stringConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            Clazz referencedClass = stringConstant.referencedClass;
 
+            // Does it have a new name?
+            String newName = referencedMember.getName(referencedClass);
 
-    // Implementations for CpInfoVisitor.
+            if (!stringConstant.getString(clazz).equals(newName))
+            {
+                if (DEBUG)
+                {
+                    debug(clazz, stringConstant, referencedClass, referencedMember);
+                }
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
+                // Update the name.
+                stringConstant.u2stringIndex =
+                    constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                       newName);
+            }
+        }
+    }
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
         // Do we know the referenced field?
-        MemberInfo referencedMemberInfo = fieldrefCpInfo.referencedMemberInfo;
-        if (referencedMemberInfo != null)
+        Member referencedMember = fieldrefConstant.referencedMember;
+        if (referencedMember != null)
         {
-            ClassFile referencedClassFile = fieldrefCpInfo.referencedClassFile;
+            Clazz referencedClass = fieldrefConstant.referencedClass;
 
             // Does it have a new name or type?
-            String newName = referencedMemberInfo.getName(referencedClassFile);
-            String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
+            String newName = referencedMember.getName(referencedClass);
+            String newType = referencedMember.getDescriptor(referencedClass);
 
-            if (!fieldrefCpInfo.getName(classFile).equals(newName) ||
-                !fieldrefCpInfo.getType(classFile).equals(newType))
+            if (!fieldrefConstant.getName(clazz).equals(newName) ||
+                !fieldrefConstant.getType(clazz).equals(newType))
             {
                 if (DEBUG)
                 {
-                    debug(classFile, fieldrefCpInfo, referencedClassFile, referencedMemberInfo);
+                    debug(clazz, fieldrefConstant, referencedClass, referencedMember);
                 }
 
                 // Update the name and type index.
-                fieldrefCpInfo.u2nameAndTypeIndex =
-                    constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
-                                                            newName,
-                                                            newType);
+                fieldrefConstant.u2nameAndTypeIndex =
+                    constantPoolEditor.addNameAndTypeConstant((ProgramClass)clazz,
+                                                              newName,
+                                                              newType);
             }
         }
     }
 
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
         // Do we know the referenced interface method?
-        MemberInfo referencedMemberInfo = interfaceMethodrefCpInfo.referencedMemberInfo;
-        if (referencedMemberInfo != null)
+        Member referencedMember = interfaceMethodrefConstant.referencedMember;
+        if (referencedMember != null)
         {
-            ClassFile referencedClassFile = interfaceMethodrefCpInfo.referencedClassFile;
+            Clazz referencedClass = interfaceMethodrefConstant.referencedClass;
 
             // Does it have a new name or type?
-            String newName = referencedMemberInfo.getName(referencedClassFile);
-            String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
+            String newName = referencedMember.getName(referencedClass);
+            String newType = referencedMember.getDescriptor(referencedClass);
 
-            if (!interfaceMethodrefCpInfo.getName(classFile).equals(newName) ||
-                !interfaceMethodrefCpInfo.getType(classFile).equals(newType))
+            if (!interfaceMethodrefConstant.getName(clazz).equals(newName) ||
+                !interfaceMethodrefConstant.getType(clazz).equals(newType))
             {
                 if (DEBUG)
                 {
-                    debug(classFile, interfaceMethodrefCpInfo, referencedClassFile, referencedMemberInfo);
+                    debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember);
                 }
 
                 // Update the name and type index.
-                interfaceMethodrefCpInfo.u2nameAndTypeIndex =
-                    constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
-                                                            newName,
-                                                            newType);
+                interfaceMethodrefConstant.u2nameAndTypeIndex =
+                    constantPoolEditor.addNameAndTypeConstant((ProgramClass)clazz,
+                                                              newName,
+                                                              newType);
 
                 // Remember that the stack sizes of the methods in this class
                 // may have changed.
@@ -172,7 +183,7 @@ implements   ClassFileVisitor,
 
             // Check if this is an interface method.
             isInterfaceMethod = true;
-            classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.u2classIndex, this);
+            clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this);
 
             // Has the method become a non-interface method?
             if (!isInterfaceMethod)
@@ -180,48 +191,48 @@ implements   ClassFileVisitor,
                 if (DEBUG)
                 {
                     System.out.println("MemberReferenceFixer:");
-                    System.out.println("  Class file     = "+classFile.getName());
-                    System.out.println("  Ref class file = "+referencedClassFile.getName());
-                    System.out.println("  Ref method     = "+interfaceMethodrefCpInfo.getName(classFile)+interfaceMethodrefCpInfo.getType(classFile));
+                    System.out.println("  Class file     = "+clazz.getName());
+                    System.out.println("  Ref class      = "+referencedClass.getName());
+                    System.out.println("  Ref method     = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz));
                     System.out.println("    -> ordinary method");
                 }
 
                 // Replace the interface method reference by a method reference.
-                ((ProgramClassFile)classFile).constantPool[this.cpIndex] =
-                    new MethodrefCpInfo(interfaceMethodrefCpInfo.u2classIndex,
-                                        interfaceMethodrefCpInfo.u2nameAndTypeIndex,
-                                        referencedClassFile,
-                                        referencedMemberInfo);
+                ((ProgramClass)clazz).constantPool[this.constantIndex] =
+                    new MethodrefConstant(interfaceMethodrefConstant.u2classIndex,
+                                          interfaceMethodrefConstant.u2nameAndTypeIndex,
+                                          referencedClass,
+                                          referencedMember);
             }
         }
     }
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
         // Do we know the referenced method?
-        MemberInfo referencedMemberInfo = methodrefCpInfo.referencedMemberInfo;
-        if (referencedMemberInfo != null)
+        Member referencedMember = methodrefConstant.referencedMember;
+        if (referencedMember != null)
         {
-            ClassFile referencedClassFile = methodrefCpInfo.referencedClassFile;
+            Clazz referencedClass = methodrefConstant.referencedClass;
 
             // Does it have a new name or type?
-            String newName = referencedMemberInfo.getName(referencedClassFile);
-            String newType = referencedMemberInfo.getDescriptor(referencedClassFile);
+            String newName = referencedMember.getName(referencedClass);
+            String newType = referencedMember.getDescriptor(referencedClass);
 
-            if (!methodrefCpInfo.getName(classFile).equals(newName) ||
-                !methodrefCpInfo.getType(classFile).equals(newType))
+            if (!methodrefConstant.getName(clazz).equals(newName) ||
+                !methodrefConstant.getType(clazz).equals(newType))
             {
                 if (DEBUG)
                 {
-                    debug(classFile, methodrefCpInfo, referencedClassFile, referencedMemberInfo);
+                    debug(clazz, methodrefConstant, referencedClass, referencedMember);
                 }
 
                 // Update the name and type index.
-                methodrefCpInfo.u2nameAndTypeIndex =
-                    constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
-                                                            newName,
-                                                            newType);
+                methodrefConstant.u2nameAndTypeIndex =
+                    constantPoolEditor.addNameAndTypeConstant((ProgramClass)clazz,
+                                                              newName,
+                                                              newType);
 
                 // Remember that the stack sizes of the methods in this class
                 // may have changed.
@@ -230,7 +241,7 @@ implements   ClassFileVisitor,
 
             // Check if this is an interface method.
             isInterfaceMethod = false;
-            classFile.constantPoolEntryAccept(methodrefCpInfo.u2classIndex, this);
+            clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this);
 
             // Has the method become an interface method?
             if (isInterfaceMethod)
@@ -238,241 +249,219 @@ implements   ClassFileVisitor,
                 if (DEBUG)
                 {
                     System.out.println("MemberReferenceFixer:");
-                    System.out.println("  Class file     = "+classFile.getName());
-                    System.out.println("  Ref class file = "+referencedClassFile.getName());
-                    System.out.println("  Ref method     = "+methodrefCpInfo.getName(classFile)+methodrefCpInfo.getType(classFile));
+                    System.out.println("  Class file     = "+clazz.getName());
+                    System.out.println("  Ref class      = "+referencedClass.getName());
+                    System.out.println("  Ref method     = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz));
                     System.out.println("    -> interface method");
                 }
 
                 // Replace the method reference by an interface method reference.
-                ((ProgramClassFile)classFile).constantPool[this.cpIndex] =
-                    new InterfaceMethodrefCpInfo(methodrefCpInfo.u2classIndex,
-                                                 methodrefCpInfo.u2nameAndTypeIndex,
-                                                 referencedClassFile,
-                                                 referencedMemberInfo);
+                ((ProgramClass)clazz).constantPool[this.constantIndex] =
+                    new InterfaceMethodrefConstant(methodrefConstant.u2classIndex,
+                                                   methodrefConstant.u2nameAndTypeIndex,
+                                                   referencedClass,
+                                                   referencedMember);
             }
         }
     }
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
         // Check if this class entry is an array type.
-        if (ClassUtil.isInternalArrayType(classCpInfo.getName(classFile)))
+        if (ClassUtil.isInternalArrayType(classConstant.getName(clazz)))
         {
             isInterfaceMethod = false;
         }
         else
         {
             // Check if this class entry refers to an interface class.
-            ClassFile referencedClassFile = classCpInfo.referencedClassFile;
-            if (referencedClassFile != null)
+            Clazz referencedClass = classConstant.referencedClass;
+            if (referencedClass != null)
             {
-                isInterfaceMethod = (referencedClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
+                isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
             }
         }
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        // Fix the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
-    }
-
+    // Implementations for MemberVisitor.
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
     {
         // Fix the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
+        programMember.attributesAccept(programClass, this);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+    // Implementations for AttributeVisitor.
 
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
-    // Implementations for AttrInfoVisitor.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
-        MemberInfo referencedMemberInfo = enclosingMethodAttrInfo.referencedMethodInfo;
-        if (referencedMemberInfo != null)
+        Member referencedMember = enclosingMethodAttribute.referencedMethod;
+        if (referencedMember != null)
         {
-            ClassFile referencedClassFile = enclosingMethodAttrInfo.referencedClassFile;
+            Clazz referencedClass = enclosingMethodAttribute.referencedClass;
 
             // Does it have a new class?
-            if (!enclosingMethodAttrInfo.getClassName(classFile).equals(referencedClassFile.getName()))
+            if (!enclosingMethodAttribute.getClassName(clazz).equals(referencedClass.getName()))
             {
                 // Update the class index.
-                enclosingMethodAttrInfo.u2classIndex =
-                    constantPoolEditor.addClassCpInfo((ProgramClassFile)classFile,
-                                                      referencedClassFile);
+                enclosingMethodAttribute.u2classIndex =
+                    constantPoolEditor.addClassConstant((ProgramClass)clazz,
+                                                        referencedClass);
             }
 
             // Does it have a new name or type?
-            if (!enclosingMethodAttrInfo.getName(classFile).equals(referencedMemberInfo.getName(referencedClassFile)) ||
-                !enclosingMethodAttrInfo.getType(classFile).equals(referencedMemberInfo.getDescriptor(referencedClassFile)))
+            if (!enclosingMethodAttribute.getName(clazz).equals(referencedMember.getName(referencedClass)) ||
+                !enclosingMethodAttribute.getType(clazz).equals(referencedMember.getDescriptor(referencedClass)))
             {
                 // Update the name and type index.
-                enclosingMethodAttrInfo.u2nameAndTypeIndex =
-                    constantPoolEditor.addNameAndTypeCpInfo((ProgramClassFile)classFile,
-                                                            referencedMemberInfo.getName(referencedClassFile),
-                                                            referencedMemberInfo.getDescriptor(referencedClassFile));
+                enclosingMethodAttribute.u2nameAndTypeIndex =
+                    constantPoolEditor.addNameAndTypeConstant((ProgramClass)clazz,
+                                                              referencedMember.getName(referencedClass),
+                                                              referencedMember.getDescriptor(referencedClass));
             }
         }
     }
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         // Recompute the maximum stack size if necessary.
         if (stackSizesMayHaveChanged)
         {
-            stackSizeUpdater.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+            stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
         }
 
         // Fix the nested attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
     {
         // Fix the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        annotationsAttribute.annotationsAccept(clazz, this);
     }
 
 
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
     {
         // Fix the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
     }
 
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        // Fix the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
     {
         // Fix the annotation.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
     }
 
 
     // Implementations for AnnotationVisitor.
 
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
     {
         // Fix the element values.
-        annotation.elementValuesAccept(classFile, this);
+        annotation.elementValuesAccept(clazz, this);
     }
 
 
     // Implementations for ElementValueVisitor.
 
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
     {
-        fixElementValue(classFile, annotation, constantElementValue);
+        fixElementValue(clazz, annotation, constantElementValue);
     }
 
 
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
     {
-        fixElementValue(classFile, annotation, enumConstantElementValue);
+        fixElementValue(clazz, annotation, enumConstantElementValue);
     }
 
 
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
     {
-        fixElementValue(classFile, annotation, classElementValue);
+        fixElementValue(clazz, annotation, classElementValue);
     }
 
 
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
     {
-        fixElementValue(classFile, annotation, annotationElementValue);
+        fixElementValue(clazz, annotation, annotationElementValue);
 
         // Fix the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
+        annotationElementValue.annotationAccept(clazz, this);
     }
 
 
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
     {
-        fixElementValue(classFile, annotation, arrayElementValue);
+        fixElementValue(clazz, annotation, arrayElementValue);
 
         // Fix the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
     }
 
 
     // Small utility methods.
 
     /**
-     * Fixs the method reference of the element value, if any.
+     * Fixes the method reference of the element value, if any.
      */
-    private void fixElementValue(ClassFile    classFile,
+    private void fixElementValue(Clazz        clazz,
                                  Annotation   annotation,
                                  ElementValue elementValue)
     {
         // Do we know the referenced method?
-        MemberInfo referencedMemberInfo = elementValue.referencedMethodInfo;
-        if (referencedMemberInfo != null)
+        Member referencedMember = elementValue.referencedMethod;
+        if (referencedMember != null)
         {
             // Does it have a new name or type?
-            String methodName    = elementValue.getMethodName(classFile);
-            String newMethodName = referencedMemberInfo.getName(elementValue.referencedClassFile);
+            String methodName    = elementValue.getMethodName(clazz);
+            String newMethodName = referencedMember.getName(elementValue.referencedClass);
+
             if (!methodName.equals(newMethodName))
             {
                 // Update the element name index.
-                elementValue.u2elementName =
-                    constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                     newMethodName);
+                elementValue.u2elementNameIndex =
+                    constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                       newMethodName);
             }
         }
     }
 
 
-    private void debug(ClassFile  classFile,
-                       RefCpInfo  refCpInfo,
-                       ClassFile  referencedClassFile,
-                       MemberInfo referencedMemberInfo)
+    private void debug(Clazz          clazz,
+                       StringConstant stringConstant,
+                       Clazz          referencedClass,
+                       Member         referencedMember)
+    {
+        System.out.println("MemberReferenceFixer:");
+        System.out.println("  Class file      = "+clazz.getName());
+        System.out.println("  Ref class       = "+referencedClass.getName());
+        System.out.println("  Ref member name = "+stringConstant.getString(clazz));
+        System.out.println("                 -> "+referencedMember.getName(referencedClass));
+    }
+
+
+    private void debug(Clazz       clazz,
+                       RefConstant refConstant,
+                       Clazz       referencedClass,
+                       Member      referencedMember)
     {
         System.out.println("MemberReferenceFixer:");
-        System.out.println("  Class file      = "+classFile.getName());
-        System.out.println("  Ref class file  = "+referencedClassFile.getName());
-        System.out.println("  Ref member name = "+refCpInfo.getName(classFile));
-        System.out.println("                 -> "+referencedMemberInfo.getName(referencedClassFile));
-        System.out.println("  Ref descriptor  = "+refCpInfo.getType(classFile));
-        System.out.println("                 -> "+referencedMemberInfo.getDescriptor(referencedClassFile));
+        System.out.println("  Class file      = "+clazz.getName());
+        System.out.println("  Ref class       = "+referencedClass.getName());
+        System.out.println("  Ref member name = "+refConstant.getName(clazz));
+        System.out.println("                 -> "+referencedMember.getName(referencedClass));
+        System.out.println("  Ref descriptor  = "+refConstant.getType(clazz));
+        System.out.println("                 -> "+referencedMember.getDescriptor(referencedClass));
     }
 }
diff --git a/src/proguard/classfile/editor/MembersEditor.java b/src/proguard/classfile/editor/MembersEditor.java
new file mode 100644
index 0000000..fb47b93
--- /dev/null
+++ b/src/proguard/classfile/editor/MembersEditor.java
@@ -0,0 +1,92 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.editor;
+
+import proguard.classfile.*;
+
+/**
+ * This class can add class members to classes. Class members to be added must
+ * be filled out beforehand, including their references to the constant pool.
+ *
+ * @author Eric Lafortune
+ */
+public class MembersEditor
+{
+    private static final boolean DEBUG = false;
+
+
+    /**
+     * Adds a given field to the given class.
+     */
+    public void addField(ProgramClass programClass,
+                         Field        field)
+    {
+        int     fieldsCount = programClass.u2fieldsCount;
+        Field[] fields      = programClass.fields;
+
+        // Make sure there is enough space for the new field.
+        if (fields.length <= fieldsCount)
+        {
+            programClass.fields = new ProgramField[fieldsCount+1];
+            System.arraycopy(fields, 0,
+                             programClass.fields, 0,
+                             fieldsCount);
+            fields = programClass.fields;
+        }
+
+        if (DEBUG)
+        {
+            System.out.println(programClass.getName()+": adding ["+field.getName(programClass)+field.getDescriptor(programClass)+"]");
+        }
+
+        // Add the field.
+        fields[programClass.u2fieldsCount++] = field;
+    }
+
+
+    /**
+     * Adds a given method to the given class.
+     */
+    public void addMethod(ProgramClass programClass,
+                          Method       method)
+    {
+        int      methodsCount = programClass.u2methodsCount;
+        Method[] methods      = programClass.methods;
+
+        // Make sure there is enough space for the new method.
+        if (methods.length <= methodsCount)
+        {
+            programClass.methods = new ProgramMethod[methodsCount+1];
+            System.arraycopy(methods, 0,
+                             programClass.methods, 0,
+                             methodsCount);
+            methods = programClass.methods;
+        }
+
+        if (DEBUG)
+        {
+            System.out.println(programClass.getName()+": adding ["+method.getName(programClass)+method.getDescriptor(programClass)+"]");
+        }
+
+        // Add the method.
+        methods[programClass.u2methodsCount++] = method;
+    }
+}
diff --git a/src/proguard/classfile/editor/MethodInvocationFixer.java b/src/proguard/classfile/editor/MethodInvocationFixer.java
index 30ab7ff..3def03e 100644
--- a/src/proguard/classfile/editor/MethodInvocationFixer.java
+++ b/src/proguard/classfile/editor/MethodInvocationFixer.java
@@ -1,6 +1,6 @@
-/* $Id: MethodInvocationFixer.java,v 1.4.2.4 2007/04/05 21:40:54 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,133 +21,141 @@
 package proguard.classfile.editor;
 
 import proguard.classfile.*;
-import proguard.classfile.util.ClassUtil;
-import proguard.classfile.attribute.CodeAttrInfo;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
 
 /**
- * This InstructionVisitor fixes all inappropriate special/virtual/static/interface
- * invocations.
+ * This AttributeVisitor fixes all inappropriate special/virtual/static/interface
+ * invocations of the code attributes that it visits.
  *
  * @author Eric Lafortune
  */
 public class MethodInvocationFixer
-implements   InstructionVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ConstantVisitor,
+             ClassVisitor,
+             MemberVisitor
 {
     private static final boolean DEBUG = false;
 
-    private CodeAttrInfoEditor codeAttrInfoEditor;
+
+    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
 
     // Return values for the visitor methods.
-    private boolean isMethodInvocation;
-    private int     accessFlags;
-    private boolean isInitializer;
-    private boolean isInterfaceMethod;
-    private int     parameterSize;
-
-
-    /**
-     * Creates a new MethodInvocationFixer.
-     * @param codeAttrInfoEditor a code editor that can be used for
-     *                           accumulating changes to the code.
-     */
-    public MethodInvocationFixer(CodeAttrInfoEditor codeAttrInfoEditor)
+    private Clazz  referencedClass;
+    private Clazz  referencedMethodClass;
+    private Member referencedMethod;
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        this.codeAttrInfoEditor = codeAttrInfoEditor;
+        // Reset the code attribute editor.
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
+
+        // Remap the variables of the instructions.
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        // Apply the code atribute editor.
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
-        int cpIndex  = cpInstruction.cpIndex;
-        int constant = cpInstruction.constant;
+        int constantIndex = constantInstruction.constantIndex;
 
-        // Get the constant pool entry's information.
-        isMethodInvocation = false;
-        isInterfaceMethod  = false;
-        accessFlags        = 0;
-        parameterSize      = constant;
+        // Get information on the called class and method, if present.
+        referencedMethod = null;
 
-        classFile.constantPoolEntryAccept(cpIndex, this);
+        clazz.constantPoolEntryAccept(constantIndex, this);
 
-        // Is it a method invocation?
-        if (isMethodInvocation)
+        // Did we find the called class and method?
+        if (referencedMethod != null)
         {
             // Do we need to update the opcode?
-            byte opcode = cpInstruction.opcode;
+            byte opcode = constantInstruction.opcode;
 
             // Is the method static?
-            if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
+            if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0)
             {
                 // But is it not a static invocation?
                 if (opcode != InstructionConstants.OP_INVOKESTATIC)
                 {
                     // Replace the invocation by an invokestatic instruction.
                     Instruction replacementInstruction =
-                        new CpInstruction(InstructionConstants.OP_INVOKESTATIC,
-                                          cpIndex).shrink();
+                        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC,
+                                                constantIndex).shrink();
 
-                    codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
                     if (DEBUG)
                     {
-                        debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
+                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
                     }
                 }
             }
 
             // Is the method private, or an instance initializer?
-            else if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 ||
-                     isInitializer)
+            else if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0 ||
+                     referencedMethod.getName(referencedMethodClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
             {
                 // But is it not a special invocation?
                 if (opcode != InstructionConstants.OP_INVOKESPECIAL)
                 {
                     // Replace the invocation by an invokespecial instruction.
                     Instruction replacementInstruction =
-                        new CpInstruction(InstructionConstants.OP_INVOKESPECIAL,
-                                          cpIndex).shrink();
+                        new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL,
+                                                constantIndex).shrink();
 
-                    codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
                     if (DEBUG)
                     {
-                        debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
+                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
                     }
                 }
             }
 
             // Is the method an interface method?
-            else if (isInterfaceMethod)
+            else if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
             {
+                int invokeinterfaceConstant =
+                    (ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass)) + 1) << 8;
+
                 // But is it not an interface invocation, or is the parameter
                 // size incorrect?
                 if (opcode != InstructionConstants.OP_INVOKEINTERFACE ||
-                    parameterSize != constant)
+                    constantInstruction.constant != invokeinterfaceConstant)
                 {
                     // Fix the parameter size of the interface invocation.
                     Instruction replacementInstruction =
-                        new CpInstruction(InstructionConstants.OP_INVOKEINTERFACE,
-                                          cpIndex,
-                                          parameterSize).shrink();
+                        new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE,
+                                                constantIndex,
+                                                invokeinterfaceConstant).shrink();
 
-                    codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
                     if (DEBUG)
                     {
-                        debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
+                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
                     }
                 }
             }
@@ -157,20 +165,21 @@ implements   InstructionVisitor,
             else
             {
                 // But is it not a virtual invocation (or a special invocation,
-                // which is allowed for super calls)?
+                // but not a super call)?
                 if (opcode != InstructionConstants.OP_INVOKEVIRTUAL &&
-                    opcode != InstructionConstants.OP_INVOKESPECIAL)
+                    (opcode != InstructionConstants.OP_INVOKESPECIAL ||
+                     !clazz.extends_(referencedClass)))
                 {
                     // Replace the invocation by an invokevirtual instruction.
                     Instruction replacementInstruction =
-                        new CpInstruction(InstructionConstants.OP_INVOKEVIRTUAL,
-                                          cpIndex).shrink();
+                        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL,
+                                                constantIndex).shrink();
 
-                    codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+                    codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
                     if (DEBUG)
                     {
-                        debug(classFile, methodInfo, offset, cpInstruction, replacementInstruction);
+                        debug(clazz, method, offset, constantInstruction, replacementInstruction);
                     }
                 }
             }
@@ -178,110 +187,67 @@ implements   InstructionVisitor,
     }
 
 
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-
+    // Implementations for ConstantVisitor.
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        // Check if this is an interface method.
-        classFile.constantPoolEntryAccept(interfaceMethodrefCpInfo.u2classIndex, this);
-
-        // Get the referenced method's access flags.
-        interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
-    }
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
     {
-        // Check if this is an interface method.
-        classFile.constantPoolEntryAccept(methodrefCpInfo.u2classIndex, this);
-
-        // Get the referenced method's access flags.
-        methodrefCpInfo.referencedMemberInfoAccept(this);
+        // Check if this is an interface method. Note that we're interested in
+        // the class of the method reference, not in the class in which the
+        // method was actually found.
+        //refConstant.referencedClassAccept(this);
+        clazz.constantPoolEntryAccept(refConstant.u2classIndex, this);
+
+        // Get the referenced access flags and names.
+        refConstant.referencedMemberAccept(this);
     }
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        // Check if this class entry is an array type.
-        if (ClassUtil.isInternalArrayType(classCpInfo.getName(classFile)))
-        {
-            isInterfaceMethod = false;
-        }
-        else
-        {
-            // Check if this class entry refers to an interface class.
-            ClassFile referencedClassFile = classCpInfo.referencedClassFile;
-            if (referencedClassFile != null)
-            {
-                isInterfaceMethod = (referencedClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0;
-            }
-        }
+        // Check if this is an interface class.
+       classConstant.referencedClassAccept(this);
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitAnyClass(Clazz clazz)
     {
-        visitMethodInfo(programClassFile, programMethodInfo);
+        // Remember the referenced class.
+        referencedClass = clazz;
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        visitMethodInfo(libraryClassFile, libraryMethodInfo);
-    }
+    // Implementations for MemberVisitor.
 
-
-    private void visitMethodInfo(ClassFile classFile, MethodInfo methodInfo)
+    public void visitAnyMember(Clazz clazz, Member member)
     {
-        // We've found a method definition.
-        isMethodInvocation = true;
-
-        // Get the method's access flags.
-        accessFlags = methodInfo.getAccessFlags();
-
-        // Check if this is an instance initializer.
-        isInitializer = methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
-
-        // Remember the parameter size of interface methods.
-        if (isInterfaceMethod)
-        {
-            parameterSize = (ClassUtil.internalMethodParameterSize(methodInfo.getDescriptor(classFile)) + 1) << 8;
-        }
+        // Remember the referenced method.
+        referencedMethodClass = clazz;
+        referencedMethod      = member;
     }
 
 
     // Small utility methods.
 
-    private void debug(ClassFile     classFile,
-                       MethodInfo    methodInfo,
-                       int           offset,
-                       CpInstruction cpInstruction,
-                       Instruction   replacementInstruction)
+    private void debug(Clazz               clazz,
+                       Method              method,
+                       int                 offset,
+                       ConstantInstruction constantInstruction,
+                       Instruction         replacementInstruction)
     {
         System.out.println("MethodInvocationFixer:");
-        System.out.println("  Class file       = "+classFile.getName());
-        System.out.println("  Method           = "+methodInfo.getName(classFile)+methodInfo.getDescriptor(classFile));
-        System.out.println("  Instruction      = "+cpInstruction.toString(offset));
-        System.out.println("  Interface method = "+isInterfaceMethod);
-        if (isInterfaceMethod)
+        System.out.println("  Class       = "+clazz.getName());
+        System.out.println("  Method      = "+method.getName(clazz)+method.getDescriptor(clazz));
+        System.out.println("  Instruction = "+constantInstruction.toString(offset));
+        System.out.println("  -> Class    = "+referencedClass);
+        System.out.println("     Method   = "+referencedMethod);
+        if ((referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
         {
-            System.out.println("  Parameter size   = "+parameterSize);
+            System.out.println("     Parameter size   = "+(ClassUtil.internalMethodParameterSize(referencedMethod.getDescriptor(referencedMethodClass)+1)));
         }
         System.out.println("  Replacement instruction = "+replacementInstruction.toString(offset));
     }
diff --git a/src/proguard/classfile/editor/StackSizeUpdater.java b/src/proguard/classfile/editor/StackSizeUpdater.java
index 5160777..5ab4dd7 100644
--- a/src/proguard/classfile/editor/StackSizeUpdater.java
+++ b/src/proguard/classfile/editor/StackSizeUpdater.java
@@ -1,6 +1,6 @@
-/* $Id: StackSizeUpdater.java,v 1.14.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,347 +21,34 @@
 package proguard.classfile.editor;
 
 import proguard.classfile.*;
-import proguard.classfile.util.ClassUtil;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttrInfoVisitor computes and updates the maximum stack size of the
+ * This AttributeVisitor computes and updates the maximum stack size of the
  * code attributes that it visits.
  *
  * @author Eric Lafortune
  */
-class      StackSizeUpdater
-implements AttrInfoVisitor,
-           InstructionVisitor,
-           ExceptionInfoVisitor
+public class StackSizeUpdater
+extends      SimplifiedVisitor
+implements   AttributeVisitor
 {
-    //*
-    private static final boolean DEBUG = false;
-    /*/
-    private static boolean DEBUG       = true;
-    //*/
-
-
-    private boolean[] evaluated;
-
-    private boolean exitInstructionBlock;
-
-    private int stackSize;
-    private int maxStackSize;
+    private final StackSizeComputer stackSizeComputer = new StackSizeComputer();
 
 
-    /**
-     * Creates a new StackSizeUpdater.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be visited.
-     */
-    public StackSizeUpdater(int codeLength)
-    {
-        evaluated = new boolean[codeLength];
-    }
-
+    // Implementations for AttributeVisitor.
 
-    // Implementations for AttrInfoVisitor.
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
 
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-//        DEBUG =
-//            classFile.getName().equals("abc/Def") &&
-//            methodInfo.getName(classFile).equals("abc");
-
-        if (DEBUG)
-        {
-            System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
-            System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
-                                                                                 0,
-                                                                                 methodInfo.getName(classFile),
-                                                                                 methodInfo.getDescriptor(classFile)));
-        }
-
-        // Try to reuse the previous array.
-        int codeLength = codeAttrInfo.u4codeLength;
-        if (evaluated.length < codeLength)
-        {
-            evaluated = new boolean[codeLength];
-        }
-        else
-        {
-            for (int index = 0; index < codeLength; index++)
-            {
-                evaluated[index] = false;
-            }
-        }
-
-        // The initial stack is always empty.
-        stackSize    = 0;
-        maxStackSize = 0;
-
-        // Evaluate the instruction block starting at the entry point of the method.
-        evaluateInstructionBlock(classFile, methodInfo, codeAttrInfo, 0);
-
-        // Evaluate the exception handlers.
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        // Compute the stack sizes.
+        stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute);
 
         // Update the maximum stack size.
-        codeAttrInfo.u2maxStack = maxStackSize;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
-    {
-        byte opcode = simpleInstruction.opcode;
-
-        // Some simple instructions exit from the current instruction block.
-        exitInstructionBlock =
-            opcode == InstructionConstants.OP_IRETURN ||
-            opcode == InstructionConstants.OP_LRETURN ||
-            opcode == InstructionConstants.OP_FRETURN ||
-            opcode == InstructionConstants.OP_DRETURN ||
-            opcode == InstructionConstants.OP_ARETURN ||
-            opcode == InstructionConstants.OP_RETURN  ||
-            opcode == InstructionConstants.OP_ATHROW;
-    }
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        // Constant pool instructions never end the current instruction block.
-        exitInstructionBlock = false;
-    }
-
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-        byte opcode = variableInstruction.opcode;
-
-        // The ret instruction end the current instruction block.
-        exitInstructionBlock =
-            opcode == InstructionConstants.OP_RET;
-    }
-
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
-    {
-        byte opcode = branchInstruction.opcode;
-
-        // Evaluate the target instruction blocks.
-        evaluateInstructionBlock(classFile,
-                                 methodInfo,
-                                 codeAttrInfo,
-                                 offset +
-                                 branchInstruction.branchOffset);
-
-        // Evaluate the instructions after a subroutine branch.
-        if (opcode == InstructionConstants.OP_JSR ||
-            opcode == InstructionConstants.OP_JSR_W)
-        {
-            // We assume subroutine calls (jsr and jsr_w instructions) don't
-            // change the stack, other than popping the return value.
-            stackSize -= 1;
-
-            evaluateInstructionBlock(classFile,
-                                     methodInfo,
-                                     codeAttrInfo,
-                                     offset + branchInstruction.length(offset));
-        }
-
-        // Some branch instructions always end the current instruction block.
-        exitInstructionBlock =
-            opcode == InstructionConstants.OP_GOTO   ||
-            opcode == InstructionConstants.OP_GOTO_W ||
-            opcode == InstructionConstants.OP_JSR    ||
-            opcode == InstructionConstants.OP_JSR_W;
-    }
-
-
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
-    {
-        // Evaluate the target instruction blocks.
-        evaluateInstructionBlocks(classFile,
-                                  methodInfo,
-                                  codeAttrInfo,
-                                  offset,
-                                  tableSwitchInstruction.jumpOffsets,
-                                  tableSwitchInstruction.jumpOffsetCount,
-                                  tableSwitchInstruction.defaultOffset);
-
-        // The switch instruction always ends the current instruction block.
-        exitInstructionBlock = true;
-    }
-
-
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
-    {
-        // Evaluate the target instruction blocks.
-        evaluateInstructionBlocks(classFile,
-                                  methodInfo,
-                                  codeAttrInfo,
-                                  offset,
-                                  lookUpSwitchInstruction.jumpOffsets,
-                                  lookUpSwitchInstruction.jumpOffsetCount,
-                                  lookUpSwitchInstruction.defaultOffset);
-
-        // The switch instruction always ends the current instruction block.
-        exitInstructionBlock = true;
-    }
-
-
-    // Implementations for ExceptionInfoVisitor.
-
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
-    {
-        // The stack size when entering the exception handler is always 1.
-        stackSize = 1;
-
-        // Evaluate the instruction block starting at the entry point of the
-        // exception handler.
-        evaluateInstructionBlock(classFile,
-                                 methodInfo,
-                                 codeAttrInfo,
-                                 exceptionInfo.u2handlerpc);
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Evaluates the blocks of instructions starting at the given jump offsets.
-     */
-    private void evaluateInstructionBlocks(ClassFile    classFile,
-                                           MethodInfo   methodInfo,
-                                           CodeAttrInfo codeAttrInfo,
-                                           int          instructionOffset,
-                                           int[]        jumpOffsets,
-                                           int          jumpOffsetCount,
-                                           int          defaultOffset)
-    {
-        // The jump offset arrays may be reused, so we have to make a local copy.
-        int[] jumpOffsetsCopy = new int[jumpOffsetCount];
-        System.arraycopy(jumpOffsets, 0, jumpOffsetsCopy, 0,  jumpOffsetCount);
-        jumpOffsets = jumpOffsetsCopy;
-
-        // Loop over all jump offsets.
-        for (int index = 0; index < jumpOffsetCount; index++)
-        {
-            // Evaluate the jump instruction block.
-            evaluateInstructionBlock(classFile,
-                                     methodInfo,
-                                     codeAttrInfo,
-                                     instructionOffset + jumpOffsets[index]);
-        }
-
-        // Also evaluate the default instruction block.
-        evaluateInstructionBlock(classFile,
-                                 methodInfo,
-                                 codeAttrInfo,
-                                 instructionOffset + defaultOffset);
-    }
-
-
-    /**
-     * Evaluates a block of instructions that hasn't been handled before,
-     * starting at the given offset and ending a branch instruction, a return
-     * instruction, or a throw instruction. Branch instructions are handled
-     * recursively.
-     */
-    private void evaluateInstructionBlock(ClassFile    classFile,
-                                          MethodInfo   methodInfo,
-                                          CodeAttrInfo codeAttrInfo,
-                                          int          instructionOffset)
-    {
-        if (DEBUG)
-        {
-            System.out.println("--");
-        }
-
-        // Remember the initial stack size.
-        int initialStackSize = stackSize;
-
-        // Remember the maximum stack size.
-        if (maxStackSize < stackSize)
-        {
-            maxStackSize = stackSize;
-        }
-
-        // Evaluate any instructions that haven't been evaluated before.
-        while (!evaluated[instructionOffset])
-        {
-            // Mark the instruction as evaluated.
-            evaluated[instructionOffset] = true;
-
-            Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
-                                                                instructionOffset);
-
-            if (DEBUG)
-            {
-                int stackPushCount = instruction.stackPushCount(classFile);
-                int stackPopCount  = instruction.stackPopCount(classFile);
-                System.out.println("["+instructionOffset+"]: "+
-                                   stackSize+" - "+
-                                   stackPopCount+" + "+
-                                   stackPushCount+" = "+
-                                   (stackSize+stackPushCount-stackPopCount)+": "+
-                                   instruction.toString());
-            }
-
-            // Compute the instruction's effect on the stack size.
-            stackSize += instruction.stackPushCount(classFile) -
-                         instruction.stackPopCount(classFile);
-
-            // Remember the maximum stack size.
-            if (maxStackSize < stackSize)
-            {
-                maxStackSize = stackSize;
-            }
-
-            // Remember the next instruction offset.
-            int nextInstructionOffset = instructionOffset +
-                                        instruction.length(instructionOffset);
-
-            // Visit the instruction, in order to handle branches.
-            instruction.accept(classFile, methodInfo, codeAttrInfo, instructionOffset, this);
-
-            // Stop evaluating after a branch.
-            if (exitInstructionBlock)
-            {
-                break;
-            }
-
-            // Continue with the next instruction.
-            instructionOffset = nextInstructionOffset;
-
-            if (DEBUG)
-            {
-                if (evaluated[instructionOffset])
-                {
-                    System.out.println("-- ("+instructionOffset+" already evaluated)");
-                }
-            }
-        }
-
-        // Restore the stack size for possible subsequent instruction blocks.
-        this.stackSize = initialStackSize;
+        codeAttribute.u2maxStack = stackSizeComputer.getMaxStackSize();
     }
 }
diff --git a/src/proguard/classfile/editor/VariableEditor.java b/src/proguard/classfile/editor/VariableEditor.java
index fe2122a..06f9cb6 100644
--- a/src/proguard/classfile/editor/VariableEditor.java
+++ b/src/proguard/classfile/editor/VariableEditor.java
@@ -1,6 +1,6 @@
-/* $Id: VariableEditor.java,v 1.5.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,41 +22,25 @@ package proguard.classfile.editor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttrInfoVisitor accumulates specified changes to local variables, and
+ * This AttributeVisitor accumulates specified changes to local variables, and
  * then applies these accumulated changes to the code attributes that it visits.
  *
- * @author Eric Lafortune
+ * @author Eric  Lafortune
  */
 public class VariableEditor
-  implements AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor
 {
-    private VariableRemapper variableRemapper;
-
-    private boolean          modified;
-
-    private boolean[]        deleted;
-
-    private int[]            variableMap;
+    private boolean   modified;
 
+    private boolean[] deleted     = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE];
+    private int[]     variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE];
 
-    /**
-     * Creates a new VariableEditor.
-     * @param codeLength an estimate of the maximum length of all the code
-     *                   that will be edited.
-     * @param maxLocals  an estimate of the maximum length of all the local
-     *                   variable frames that will be edited.
-     */
-    public VariableEditor(int codeLength, int maxLocals)
-    {
-        variableRemapper = new VariableRemapper(codeLength);
-        deleted          = new boolean[maxLocals];
-        variableMap      = new int[maxLocals];
-    }
+    private final VariableRemapper variableRemapper = new VariableRemapper();
 
 
     /**
@@ -83,8 +67,6 @@ public class VariableEditor
     }
 
 
-
-
     /**
      * Remembers to delete the given variable.
      * @param variableIndex the index of the variable to be deleted.
@@ -106,38 +88,12 @@ public class VariableEditor
     }
 
 
-    /**
-     * Returns whether any oarameter has been modified.
-     */
-    public boolean isModified()
-    {
-        return modified;
-    }
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         // Avoid doing any work if nothing is changing anyway.
         if (!modified)
@@ -145,7 +101,7 @@ public class VariableEditor
             return;
         }
 
-        int oldMaxLocals = codeAttrInfo.u2maxLocals;
+        int oldMaxLocals = codeAttribute.u2maxLocals;
 
         // Make sure there is a sufficiently large variable map.
         if (variableMap.length < oldMaxLocals)
@@ -172,9 +128,9 @@ public class VariableEditor
         variableRemapper.setVariableMap(variableMap);
 
         // Remap the variables.
-        variableRemapper.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+        variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
 
         // Update the length of local variable frame.
-        codeAttrInfo.u2maxLocals = newVariableIndex;
+        codeAttribute.u2maxLocals = newVariableIndex;
     }
 }
diff --git a/src/proguard/classfile/editor/VariableRemapper.java b/src/proguard/classfile/editor/VariableRemapper.java
index a26e058..4877979 100644
--- a/src/proguard/classfile/editor/VariableRemapper.java
+++ b/src/proguard/classfile/editor/VariableRemapper.java
@@ -1,6 +1,6 @@
-/* $Id: VariableRemapper.java,v 1.4.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,38 +22,30 @@ package proguard.classfile.editor;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.*;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttrInfoVisitor remaps variable indexes in all attributes that it
+ * This AttributeVisitor remaps variable indexes in all attributes that it
  * visits, based on a given index map.
  *
  * @author Eric Lafortune
  */
 public class VariableRemapper
-  implements AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
              InstructionVisitor,
              LocalVariableInfoVisitor,
              LocalVariableTypeInfoVisitor
 {
-    private CodeAttrInfoEditor codeAttrInfoEditor;
+    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
 
     private int[] variableMap;
 
 
     /**
-     * Creates a new VariableRemapper.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public VariableRemapper(int codeLength)
-    {
-        codeAttrInfoEditor = new CodeAttrInfoEditor(codeLength);
-    }
-
-
-    /**
      * Sets the given mapping of old variable indexes to their new indexes.
      * Variables that should disappear can be mapped to -1.
      */
@@ -63,69 +55,54 @@ public class VariableRemapper
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         // Initially, the code attribute editor doesn't contain any changes.
-        codeAttrInfoEditor.reset(codeAttrInfo.u4codeLength);
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
 
         // Remap the variables of the instructions.
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+        codeAttribute.instructionsAccept(clazz, method, this);
 
         // Apply the code atribute editor.
-        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
 
         // Remap the variables of the attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
         // Remap the variable references of the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
         // Remove local variables that haven't been mapped.
-        localVariableTableAttrInfo.u2localVariableTableLength =
-            removeEmptyLocalVariables(localVariableTableAttrInfo.localVariableTable,
-                                      localVariableTableAttrInfo.u2localVariableTableLength);
+        localVariableTableAttribute.u2localVariableTableLength =
+            removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
+                                      localVariableTableAttribute.u2localVariableTableLength);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
         // Remap the variable references of the local variables.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
 
         // Remove local variables that haven't been mapped.
-        localVariableTypeTableAttrInfo.u2localVariableTypeTableLength =
-            removeEmptyLocalVariableTypes(localVariableTypeTableAttrInfo.localVariableTypeTable,
-                                          localVariableTypeTableAttrInfo.u2localVariableTypeTableLength);
+        localVariableTypeTableAttribute.u2localVariableTypeTableLength =
+            removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
+                                          localVariableTypeTableAttribute.u2localVariableTypeTableLength);
     }
 
 
     // Implementations for LocalVariableInfoVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
     {
         localVariableInfo.u2index =
             remapVariable(localVariableInfo.u2index);
@@ -134,7 +111,7 @@ public class VariableRemapper
 
     // Implementations for LocalVariableTypeInfoVisitor.
 
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
     {
         localVariableTypeInfo.u2index =
             remapVariable(localVariableTypeInfo.u2index);
@@ -143,14 +120,10 @@ public class VariableRemapper
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         // Is the new variable index different from the original one?
         int oldVariableIndex = variableInstruction.variableIndex;
@@ -158,11 +131,12 @@ public class VariableRemapper
         if (newVariableIndex != oldVariableIndex)
         {
             // Replace the instruction.
-            variableInstruction = new VariableInstruction().copy(variableInstruction);
-            variableInstruction.variableIndex = newVariableIndex;
-            variableInstruction.shrink();
+            Instruction replacementInstruction =
+                new VariableInstruction(variableInstruction.opcode,
+                                        newVariableIndex,
+                                        variableInstruction.constant).shrink();
 
-            codeAttrInfoEditor.replaceInstruction(offset, variableInstruction);
+            codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
         }
     }
 
diff --git a/src/proguard/classfile/editor/VariableSizeUpdater.java b/src/proguard/classfile/editor/VariableSizeUpdater.java
new file mode 100644
index 0000000..7ca609e
--- /dev/null
+++ b/src/proguard/classfile/editor/VariableSizeUpdater.java
@@ -0,0 +1,102 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.AttributeVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+
+/**
+ * This AttributeVisitor computes and updates the maximum local variable frame
+ * size of the code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableSizeUpdater
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        // The minimum variable size is determined by the arguments.
+        codeAttribute.u2maxLocals = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
+
+        // Non-static methods also have the 'this' variable.
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
+        {
+            codeAttribute.u2maxLocals++;
+        }
+
+        if (DEBUG)
+        {
+            System.out.println("VariableSizeUpdater: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+            System.out.println("Max locals: "+codeAttribute.u2maxLocals+" <- parameters");
+        }
+
+        // Go over all instructions.
+        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)
+    {
+        int variableSize = variableInstruction.variableIndex + 1;
+        if (variableInstruction.isCategory2())
+        {
+            variableSize++;
+        }
+
+        if (codeAttribute.u2maxLocals < variableSize)
+        {
+            codeAttribute.u2maxLocals = variableSize;
+
+            if (DEBUG)
+            {
+                System.out.println("Max locals: "+codeAttribute.u2maxLocals+" <- "+variableInstruction.toString(offset));
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/instruction/AllInstructionVisitor.java b/src/proguard/classfile/instruction/AllInstructionVisitor.java
deleted file mode 100644
index 2673a6b..0000000
--- a/src/proguard/classfile/instruction/AllInstructionVisitor.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/* $Id: AllInstructionVisitor.java,v 1.5 2004/10/10 20:56:58 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.instruction;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.attribute.AttrInfoVisitor;
-
-/**
- * This AttrInfoVisitor lets a given InstructionVisitor visit all Instruction
- * objects of the CodeAttrInfo objects it visits.
- *
- * @author Eric Lafortune
- */
-public class AllInstructionVisitor implements AttrInfoVisitor
-{
-    private InstructionVisitor instructionVisitor;
-
-
-    public AllInstructionVisitor(InstructionVisitor instructionVisitor)
-    {
-        this.instructionVisitor = instructionVisitor;
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, instructionVisitor);
-    }
-}
diff --git a/src/proguard/classfile/instruction/BranchInstruction.java b/src/proguard/classfile/instruction/BranchInstruction.java
index 88e2697..a803840 100644
--- a/src/proguard/classfile/instruction/BranchInstruction.java
+++ b/src/proguard/classfile/instruction/BranchInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: BranchInstruction.java,v 1.15.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * This interface describes an instruction that branches to a given offset in
@@ -63,6 +64,19 @@ public class BranchInstruction extends Instruction
 
     // Implementations for Instruction.
 
+    public byte canonicalOpcode()
+    {
+        // Remove the _w extension, if any.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_GOTO_W: return InstructionConstants.OP_GOTO;
+
+            case InstructionConstants.OP_JSR_W: return InstructionConstants.OP_JSR;
+
+            default: return opcode;
+        }
+    }
+
     public Instruction shrink()
     {
         // Do we need an ordinary branch or a wide branch?
@@ -80,7 +94,7 @@ public class BranchInstruction extends Instruction
         }
         else
         {
-            // Can we replace the ordinary branch by a wide branch?
+            // Should we replace the ordinary branch by a wide branch?
             if      (opcode == InstructionConstants.OP_GOTO)
             {
                 opcode = InstructionConstants.OP_GOTO_W;
@@ -111,7 +125,7 @@ public class BranchInstruction extends Instruction
             throw new IllegalArgumentException("Instruction has invalid branch offset size ("+this.toString(offset)+")");
         }
 
-        writeValue(code, offset, branchOffset, branchOffsetSize());
+        writeSignedValue(code, offset, branchOffset, branchOffsetSize());
     }
 
 
@@ -121,15 +135,15 @@ public class BranchInstruction extends Instruction
     }
 
 
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        instructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+        instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, this);
     }
 
 
     public String toString(int offset)
     {
-        return "["+offset+"] "+getName()+" (offset="+branchOffset+", target="+(offset+branchOffset)+")";
+        return "["+offset+"] "+toString()+" (target="+(offset+branchOffset)+")";
     }
 
 
@@ -137,7 +151,7 @@ public class BranchInstruction extends Instruction
 
     public String toString()
     {
-        return getName()+" (offset="+branchOffset+")";
+        return getName()+" "+(branchOffset >= 0 ? "+" : "")+branchOffset;
     }
 
 
diff --git a/src/proguard/classfile/instruction/CpInstruction.java b/src/proguard/classfile/instruction/ConstantInstruction.java
similarity index 51%
rename from src/proguard/classfile/instruction/CpInstruction.java
rename to src/proguard/classfile/instruction/ConstantInstruction.java
index 1604da5..69942de 100644
--- a/src/proguard/classfile/instruction/CpInstruction.java
+++ b/src/proguard/classfile/instruction/ConstantInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: CpInstruction.java,v 1.20.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,11 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 import proguard.classfile.util.ClassUtil;
-import proguard.classfile.visitor.CpInfoVisitor;
 
 /**
  * This Instruction represents an instruction that refers to an entry in the
@@ -31,55 +33,56 @@ import proguard.classfile.visitor.CpInfoVisitor;
  *
  * @author Eric Lafortune
  */
-public class CpInstruction extends Instruction
-implements   CpInfoVisitor
+public class ConstantInstruction extends Instruction
+implements   ConstantVisitor
 {
-    public int cpIndex;
+    public int constantIndex;
     public int constant;
 
 
-    // Fields acting as return parameters for the CpInfoVisitor methods.
+    // Fields acting as return parameters for the ConstantVisitor methods.
     private int parameterStackDelta;
     private int typeStackDelta;
 
 
     /**
-     * Creates an uninitialized CpInstruction.
+     * Creates an uninitialized ConstantInstruction.
      */
-    public CpInstruction() {}
+    public ConstantInstruction() {}
 
 
     /**
-     * Creates a new CpInstruction with the given opcode and constant pool index.
+     * Creates a new ConstantInstruction with the given opcode and constant pool
+     * index.
      */
-    public CpInstruction(byte opcode, int cpIndex)
+    public ConstantInstruction(byte opcode, int constantIndex)
     {
-        this(opcode, cpIndex, 0);
+        this(opcode, constantIndex, 0);
     }
 
 
     /**
-     * Creates a new CpInstruction with the given opcode, constant pool index,
-     * and constant.
+     * Creates a new ConstantInstruction with the given opcode, constant pool
+     * index, and constant.
      */
-    public CpInstruction(byte opcode, int cpIndex, int constant)
+    public ConstantInstruction(byte opcode, int constantIndex, int constant)
     {
-        this.opcode   = opcode;
-        this.cpIndex  = cpIndex;
-        this.constant = constant;
+        this.opcode        = opcode;
+        this.constantIndex = constantIndex;
+        this.constant      = constant;
     }
 
 
     /**
      * Copies the given instruction into this instruction.
-     * @param cpInstruction the instruction to be copied.
+     * @param constantInstruction the instruction to be copied.
      * @return this instruction.
      */
-    public CpInstruction copy(CpInstruction cpInstruction)
+    public ConstantInstruction copy(ConstantInstruction constantInstruction)
     {
-        this.opcode   = cpInstruction.opcode;
-        this.cpIndex  = cpInstruction.cpIndex;
-        this.constant = cpInstruction.constant;
+        this.opcode        = constantInstruction.opcode;
+        this.constantIndex = constantInstruction.constantIndex;
+        this.constant      = constantInstruction.constant;
 
         return this;
     }
@@ -87,10 +90,22 @@ implements   CpInfoVisitor
 
     // Implementations for Instruction.
 
+    public byte canonicalOpcode()
+    {
+        // Remove the _w extension, if any.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_LDC_W:
+            case InstructionConstants.OP_LDC2_W: return InstructionConstants.OP_LDC;
+
+            default: return opcode;
+        }
+    }
+
     public Instruction shrink()
     {
         // Do we need a short index or a long index?
-        if (requiredCpIndexSize() == 1)
+        if (requiredConstantIndexSize() == 1)
         {
             // Can we replace the long instruction by a short instruction?
             if (opcode == InstructionConstants.OP_LDC_W)
@@ -112,50 +127,44 @@ implements   CpInfoVisitor
 
     protected void readInfo(byte[] code, int offset)
     {
-        int cpIndexSize  = cpIndexSize();
-        int constantSize = constantSize();
+        int constantIndexSize = constantIndexSize();
+        int constantSize      = constantSize();
 
-        cpIndex  = readValue(code, offset, cpIndexSize);  offset += cpIndexSize;
-        constant = readValue(code, offset, constantSize);
+        constantIndex = readValue(code, offset, constantIndexSize);  offset += constantIndexSize;
+        constant      = readValue(code, offset, constantSize);
     }
 
 
     protected void writeInfo(byte[] code, int offset)
     {
-        int cpIndexSize  = cpIndexSize();
-        int constantSize = constantSize();
+        int constantIndexSize = constantIndexSize();
+        int constantSize      = constantSize();
 
-        if (requiredCpIndexSize() > cpIndexSize)
+        if (requiredConstantIndexSize() > constantIndexSize)
         {
             throw new IllegalArgumentException("Instruction has invalid constant index size ("+this.toString(offset)+")");
         }
 
-        writeValue(code, offset, cpIndex,  cpIndexSize); offset += cpIndexSize;
-        writeValue(code, offset, constant, constantSize);
+        writeValue(code, offset, constantIndex, constantIndexSize); offset += constantIndexSize;
+        writeValue(code, offset, constant,      constantSize);
     }
 
 
     public int length(int offset)
     {
-        return 1 + cpIndexSize() + constantSize();
+        return 1 + constantIndexSize() + constantSize();
     }
 
 
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        instructionVisitor.visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+        instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, this);
     }
 
 
-    public String toString(int offset)
+    public int stackPopCount(Clazz clazz)
     {
-        return "["+offset+"] "+getName()+" (cpIndex="+cpIndex+")";
-    }
-
-
-    public int stackPopCount(ClassFile classFile)
-    {
-        int stackPopCount = super.stackPopCount(classFile);
+        int stackPopCount = super.stackPopCount(clazz);
 
         // Some special cases.
         switch (opcode)
@@ -168,7 +177,7 @@ implements   CpInfoVisitor
             case InstructionConstants.OP_PUTSTATIC:
             case InstructionConstants.OP_PUTFIELD:
                 // The field value is be popped from the stack.
-                classFile.constantPoolEntryAccept(cpIndex, this);
+                clazz.constantPoolEntryAccept(constantIndex, this);
                 stackPopCount += typeStackDelta;
                 break;
 
@@ -177,7 +186,7 @@ implements   CpInfoVisitor
             case InstructionConstants.OP_INVOKESTATIC:
             case InstructionConstants.OP_INVOKEINTERFACE:
                 // The some parameters may be popped from the stack.
-                classFile.constantPoolEntryAccept(cpIndex, this);
+                clazz.constantPoolEntryAccept(constantIndex, this);
                 stackPopCount += parameterStackDelta;
                 break;
         }
@@ -186,9 +195,9 @@ implements   CpInfoVisitor
     }
 
 
-    public int stackPushCount(ClassFile classFile)
+    public int stackPushCount(Clazz clazz)
     {
-        int stackPushCount = super.stackPushCount(classFile);
+        int stackPushCount = super.stackPushCount(clazz);
 
         // Some special cases.
         switch (opcode)
@@ -200,7 +209,7 @@ implements   CpInfoVisitor
             case InstructionConstants.OP_INVOKESTATIC:
             case InstructionConstants.OP_INVOKEINTERFACE:
                 // The field value or a return value may be pushed onto the stack.
-                classFile.constantPoolEntryAccept(cpIndex, this);
+                clazz.constantPoolEntryAccept(constantIndex, this);
                 stackPushCount += typeStackDelta;
                 break;
         }
@@ -209,38 +218,41 @@ implements   CpInfoVisitor
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) {}
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant) {}
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) {}
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) {}
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant) {}
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) {}
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant) {}
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) {}
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
-        String type = fieldrefCpInfo.getType(classFile);
+        String type = fieldrefConstant.getType(clazz);
 
         typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
     }
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
-        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+        visitRefConstant(clazz, interfaceMethodrefConstant);
     }
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
-        visitRefCpInfo(classFile, methodrefCpInfo);
+        visitRefConstant(clazz, methodrefConstant);
     }
 
-    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
+
+    private void visitRefConstant(Clazz clazz, RefConstant methodrefConstant)
     {
-        String type = methodrefCpInfo.getType(classFile);
+        String type = methodrefConstant.getType(clazz);
 
         parameterStackDelta = ClassUtil.internalMethodParameterSize(type);
         typeStackDelta      = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
@@ -251,7 +263,7 @@ implements   CpInfoVisitor
 
     public String toString()
     {
-        return getName()+" (cpIndex="+cpIndex+")";
+        return getName()+" #"+constantIndex;
     }
 
 
@@ -260,7 +272,7 @@ implements   CpInfoVisitor
     /**
      * Returns the constant pool index size for this instruction.
      */
-    private int cpIndexSize()
+    private int constantIndexSize()
     {
         return opcode == InstructionConstants.OP_LDC ? 1 :
                                                        2;
@@ -282,10 +294,10 @@ implements   CpInfoVisitor
      * Computes the required constant pool index size for this instruction's
      * constant pool index.
      */
-    private int requiredCpIndexSize()
+    private int requiredConstantIndexSize()
     {
-        return (cpIndex &   0xff) == cpIndex ? 1 :
-               (cpIndex & 0xffff) == cpIndex ? 2 :
-                                               4;
+        return (constantIndex &   0xff) == constantIndex ? 1 :
+               (constantIndex & 0xffff) == constantIndex ? 2 :
+                                                           4;
     }
 }
diff --git a/src/proguard/classfile/instruction/Instruction.java b/src/proguard/classfile/instruction/Instruction.java
index a461060..630ea8b 100644
--- a/src/proguard/classfile/instruction/Instruction.java
+++ b/src/proguard/classfile/instruction/Instruction.java
@@ -1,6 +1,6 @@
-/* $Id: Instruction.java,v 1.24.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * Base class for representing instructions.
@@ -660,25 +661,37 @@ public abstract class Instruction
 
 
     /**
+     * Returns the canonical opcode of this instruction, i.e. typically the
+     * opcode whose extension has been removed.
+     */
+    public byte canonicalOpcode()
+    {
+        return opcode;
+    }
+
+
+    /**
      * Shrinks this instruction to its shortest possible form.
      * @return this instruction.
-     * @throws IllegalArgumentException if the instruction can't be expanded
-     *                                  to the necessary size, e.g. if it
-     *                                  contains an offset that is too large.
      */
     public abstract Instruction shrink();
 
 
+
     /**
      * Writes the Instruction at the given offset in the given code attribute.
-     * @throws IllegalArgumentException if the instruction can't be written out
-     *                                  in its current state, e.g. if it
-     *                                  contains an offset that is too large.
      */
-    public final void write(CodeAttrInfo codeAttrInfo, int offset)
+    public final void write(CodeAttribute codeAttribute, int offset)
     {
-        byte[] code  = codeAttrInfo.code;
+        write(codeAttribute.code, offset);
+    }
+
 
+    /**
+     * Writes the Instruction at the given offset in the given code array.
+     */
+    public final void write(byte[] code, int offset)
+    {
         // Write the wide opcode, if necessary.
         if (isWide())
         {
@@ -694,7 +707,7 @@ public abstract class Instruction
 
 
     /**
-     * Returns whether the instruction is wide, i.e. preceded by a wide opcode.
+     * Returns whether the instruction is wide, index.e. preceded by a wide opcode.
      * With the current specifications, only variable instructions can be wide.
      */
     protected boolean isWide()
@@ -724,13 +737,16 @@ public abstract class Instruction
     /**
      * Accepts the given visitor.
      */
-    public abstract void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor);
+    public abstract void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor);
 
 
     /**
      * Returns a description of the instruction, at the given offset.
      */
-    public abstract String toString(int offset);
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+ this.toString();
+    }
 
 
     /**
@@ -756,7 +772,7 @@ public abstract class Instruction
      * Returns the number of entries popped from the stack during the execution
      * of the instruction.
      */
-    public int stackPopCount(ClassFile classFile)
+    public int stackPopCount(Clazz clazz)
     {
         return STACK_POP_COUNTS[opcode & 0xff];
     }
@@ -766,7 +782,7 @@ public abstract class Instruction
      * Returns the number of entries pushed onto the stack during the execution
      * of the instruction.
      */
-    public int stackPushCount(ClassFile classFile)
+    public int stackPushCount(Clazz clazz)
     {
         return STACK_PUSH_COUNTS[opcode & 0xff];
     }
@@ -832,7 +848,7 @@ public abstract class Instruction
     {
         if (value > 0xff)
         {
-            throw new IllegalArgumentException("Byte value larger than 0xff ["+value+"]");
+            throw new IllegalArgumentException("Unsigned byte value larger than 0xff ["+value+"]");
         }
 
         code[offset] = (byte)value;
@@ -842,7 +858,7 @@ public abstract class Instruction
     {
         if (value > 0xffff)
         {
-            throw new IllegalArgumentException("Short value larger than 0xffff ["+value+"]");
+            throw new IllegalArgumentException("Unsigned short value larger than 0xffff ["+value+"]");
         }
 
         code[offset++] = (byte)(value >> 8);
@@ -868,4 +884,37 @@ public abstract class Instruction
             default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]");
         }
     }
+
+    protected static void writeSignedByte(byte[] code, int offset, int value)
+    {
+        if (value << 24 >> 24 != value)
+        {
+            throw new IllegalArgumentException("Signed byte value out of range ["+value+"]");
+        }
+
+        code[offset] = (byte)value;
+    }
+
+    protected static void writeSignedShort(byte[] code, int offset, int value)
+    {
+        if (value << 16 >> 16 != value)
+        {
+            throw new IllegalArgumentException("Signed short value out of range ["+value+"]");
+        }
+
+        code[offset++] = (byte)(value >> 8);
+        code[offset  ] = (byte)(value     );
+    }
+
+    protected static void writeSignedValue(byte[] code, int offset, int value, int valueSize)
+    {
+        switch (valueSize)
+        {
+            case 0:                                        break;
+            case 1: writeSignedByte( code, offset, value); break;
+            case 2: writeSignedShort(code, offset, value); break;
+            case 4: writeInt(        code, offset, value); break;
+            default: throw new IllegalArgumentException("Unsupported value size ["+valueSize+"]");
+        }
+    }
 }
diff --git a/src/proguard/classfile/instruction/InstructionConstants.java b/src/proguard/classfile/instruction/InstructionConstants.java
index 60e9fec..4ee0c38 100644
--- a/src/proguard/classfile/instruction/InstructionConstants.java
+++ b/src/proguard/classfile/instruction/InstructionConstants.java
@@ -1,6 +1,6 @@
-/* $Id: InstructionConstants.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,208 +27,208 @@ package proguard.classfile.instruction;
  */
 public interface InstructionConstants
 {
-    public static final byte OP_NOP              = 0;
-    public static final byte OP_ACONST_NULL      = 1;
-    public static final byte OP_ICONST_M1        = 2;
-    public static final byte OP_ICONST_0         = 3;
-    public static final byte OP_ICONST_1         = 4;
-    public static final byte OP_ICONST_2         = 5;
-    public static final byte OP_ICONST_3         = 6;
-    public static final byte OP_ICONST_4         = 7;
-    public static final byte OP_ICONST_5         = 8;
-    public static final byte OP_LCONST_0         = 9;
-    public static final byte OP_LCONST_1         = 10;
-    public static final byte OP_FCONST_0         = 11;
-    public static final byte OP_FCONST_1         = 12;
-    public static final byte OP_FCONST_2         = 13;
-    public static final byte OP_DCONST_0         = 14;
-    public static final byte OP_DCONST_1         = 15;
-    public static final byte OP_BIPUSH           = 16;
-    public static final byte OP_SIPUSH           = 17;
-    public static final byte OP_LDC              = 18;
-    public static final byte OP_LDC_W            = 19;
-    public static final byte OP_LDC2_W           = 20;
-    public static final byte OP_ILOAD            = 21;
-    public static final byte OP_LLOAD            = 22;
-    public static final byte OP_FLOAD            = 23;
-    public static final byte OP_DLOAD            = 24;
-    public static final byte OP_ALOAD            = 25;
-    public static final byte OP_ILOAD_0          = 26;
-    public static final byte OP_ILOAD_1          = 27;
-    public static final byte OP_ILOAD_2          = 28;
-    public static final byte OP_ILOAD_3          = 29;
-    public static final byte OP_LLOAD_0          = 30;
-    public static final byte OP_LLOAD_1          = 31;
-    public static final byte OP_LLOAD_2          = 32;
-    public static final byte OP_LLOAD_3          = 33;
-    public static final byte OP_FLOAD_0          = 34;
-    public static final byte OP_FLOAD_1          = 35;
-    public static final byte OP_FLOAD_2          = 36;
-    public static final byte OP_FLOAD_3          = 37;
-    public static final byte OP_DLOAD_0          = 38;
-    public static final byte OP_DLOAD_1          = 39;
-    public static final byte OP_DLOAD_2          = 40;
-    public static final byte OP_DLOAD_3          = 41;
-    public static final byte OP_ALOAD_0          = 42;
-    public static final byte OP_ALOAD_1          = 43;
-    public static final byte OP_ALOAD_2          = 44;
-    public static final byte OP_ALOAD_3          = 45;
-    public static final byte OP_IALOAD           = 46;
-    public static final byte OP_LALOAD           = 47;
-    public static final byte OP_FALOAD           = 48;
-    public static final byte OP_DALOAD           = 49;
-    public static final byte OP_AALOAD           = 50;
-    public static final byte OP_BALOAD           = 51;
-    public static final byte OP_CALOAD           = 52;
-    public static final byte OP_SALOAD           = 53;
-    public static final byte OP_ISTORE           = 54;
-    public static final byte OP_LSTORE           = 55;
-    public static final byte OP_FSTORE           = 56;
-    public static final byte OP_DSTORE           = 57;
-    public static final byte OP_ASTORE           = 58;
-    public static final byte OP_ISTORE_0         = 59;
-    public static final byte OP_ISTORE_1         = 60;
-    public static final byte OP_ISTORE_2         = 61;
-    public static final byte OP_ISTORE_3         = 62;
-    public static final byte OP_LSTORE_0         = 63;
-    public static final byte OP_LSTORE_1         = 64;
-    public static final byte OP_LSTORE_2         = 65;
-    public static final byte OP_LSTORE_3         = 66;
-    public static final byte OP_FSTORE_0         = 67;
-    public static final byte OP_FSTORE_1         = 68;
-    public static final byte OP_FSTORE_2         = 69;
-    public static final byte OP_FSTORE_3         = 70;
-    public static final byte OP_DSTORE_0         = 71;
-    public static final byte OP_DSTORE_1         = 72;
-    public static final byte OP_DSTORE_2         = 73;
-    public static final byte OP_DSTORE_3         = 74;
-    public static final byte OP_ASTORE_0         = 75;
-    public static final byte OP_ASTORE_1         = 76;
-    public static final byte OP_ASTORE_2         = 77;
-    public static final byte OP_ASTORE_3         = 78;
-    public static final byte OP_IASTORE          = 79;
-    public static final byte OP_LASTORE          = 80;
-    public static final byte OP_FASTORE          = 81;
-    public static final byte OP_DASTORE          = 82;
-    public static final byte OP_AASTORE          = 83;
-    public static final byte OP_BASTORE          = 84;
-    public static final byte OP_CASTORE          = 85;
-    public static final byte OP_SASTORE          = 86;
-    public static final byte OP_POP              = 87;
-    public static final byte OP_POP2             = 88;
-    public static final byte OP_DUP              = 89;
-    public static final byte OP_DUP_X1           = 90;
-    public static final byte OP_DUP_X2           = 91;
-    public static final byte OP_DUP2             = 92;
-    public static final byte OP_DUP2_X1          = 93;
-    public static final byte OP_DUP2_X2          = 94;
-    public static final byte OP_SWAP             = 95;
-    public static final byte OP_IADD             = 96;
-    public static final byte OP_LADD             = 97;
-    public static final byte OP_FADD             = 98;
-    public static final byte OP_DADD             = 99;
-    public static final byte OP_ISUB             = 100;
-    public static final byte OP_LSUB             = 101;
-    public static final byte OP_FSUB             = 102;
-    public static final byte OP_DSUB             = 103;
-    public static final byte OP_IMUL             = 104;
-    public static final byte OP_LMUL             = 105;
-    public static final byte OP_FMUL             = 106;
-    public static final byte OP_DMUL             = 107;
-    public static final byte OP_IDIV             = 108;
-    public static final byte OP_LDIV             = 109;
-    public static final byte OP_FDIV             = 110;
-    public static final byte OP_DDIV             = 111;
-    public static final byte OP_IREM             = 112;
-    public static final byte OP_LREM             = 113;
-    public static final byte OP_FREM             = 114;
-    public static final byte OP_DREM             = 115;
-    public static final byte OP_INEG             = 116;
-    public static final byte OP_LNEG             = 117;
-    public static final byte OP_FNEG             = 118;
-    public static final byte OP_DNEG             = 119;
-    public static final byte OP_ISHL             = 120;
-    public static final byte OP_LSHL             = 121;
-    public static final byte OP_ISHR             = 122;
-    public static final byte OP_LSHR             = 123;
-    public static final byte OP_IUSHR            = 124;
-    public static final byte OP_LUSHR            = 125;
-    public static final byte OP_IAND             = 126;
-    public static final byte OP_LAND             = 127;
-    public static final byte OP_IOR              = -128;
-    public static final byte OP_LOR              = -127;
-    public static final byte OP_IXOR             = -126;
-    public static final byte OP_LXOR             = -125;
-    public static final byte OP_IINC             = -124;
-    public static final byte OP_I2L              = -123;
-    public static final byte OP_I2F              = -122;
-    public static final byte OP_I2D              = -121;
-    public static final byte OP_L2I              = -120;
-    public static final byte OP_L2F              = -119;
-    public static final byte OP_L2D              = -118;
-    public static final byte OP_F2I              = -117;
-    public static final byte OP_F2L              = -116;
-    public static final byte OP_F2D              = -115;
-    public static final byte OP_D2I              = -114;
-    public static final byte OP_D2L              = -113;
-    public static final byte OP_D2F              = -112;
-    public static final byte OP_I2B              = -111;
-    public static final byte OP_I2C              = -110;
-    public static final byte OP_I2S              = -109;
-    public static final byte OP_LCMP             = -108;
-    public static final byte OP_FCMPL            = -107;
-    public static final byte OP_FCMPG            = -106;
-    public static final byte OP_DCMPL            = -105;
-    public static final byte OP_DCMPG            = -104;
-    public static final byte OP_IFEQ             = -103;
-    public static final byte OP_IFNE             = -102;
-    public static final byte OP_IFLT             = -101;
-    public static final byte OP_IFGE             = -100;
-    public static final byte OP_IFGT             = -99;
-    public static final byte OP_IFLE             = -98;
-    public static final byte OP_IFICMPEQ         = -97;
-    public static final byte OP_IFICMPNE         = -96;
-    public static final byte OP_IFICMPLT         = -95;
-    public static final byte OP_IFICMPGE         = -94;
-    public static final byte OP_IFICMPGT         = -93;
-    public static final byte OP_IFICMPLE         = -92;
-    public static final byte OP_IFACMPEQ         = -91;
-    public static final byte OP_IFACMPNE         = -90;
-    public static final byte OP_GOTO             = -89;
-    public static final byte OP_JSR              = -88;
-    public static final byte OP_RET              = -87;
-    public static final byte OP_TABLESWITCH      = -86;
-    public static final byte OP_LOOKUPSWITCH     = -85;
-    public static final byte OP_IRETURN          = -84;
-    public static final byte OP_LRETURN          = -83;
-    public static final byte OP_FRETURN          = -82;
-    public static final byte OP_DRETURN          = -81;
-    public static final byte OP_ARETURN          = -80;
-    public static final byte OP_RETURN           = -79;
-    public static final byte OP_GETSTATIC        = -78;
-    public static final byte OP_PUTSTATIC        = -77;
-    public static final byte OP_GETFIELD         = -76;
-    public static final byte OP_PUTFIELD         = -75;
-    public static final byte OP_INVOKEVIRTUAL    = -74;
-    public static final byte OP_INVOKESPECIAL    = -73;
-    public static final byte OP_INVOKESTATIC     = -72;
-    public static final byte OP_INVOKEINTERFACE  = -71;
+    public static final byte OP_NOP             = 0;
+    public static final byte OP_ACONST_NULL     = 1;
+    public static final byte OP_ICONST_M1       = 2;
+    public static final byte OP_ICONST_0        = 3;
+    public static final byte OP_ICONST_1        = 4;
+    public static final byte OP_ICONST_2        = 5;
+    public static final byte OP_ICONST_3        = 6;
+    public static final byte OP_ICONST_4        = 7;
+    public static final byte OP_ICONST_5        = 8;
+    public static final byte OP_LCONST_0        = 9;
+    public static final byte OP_LCONST_1        = 10;
+    public static final byte OP_FCONST_0        = 11;
+    public static final byte OP_FCONST_1        = 12;
+    public static final byte OP_FCONST_2        = 13;
+    public static final byte OP_DCONST_0        = 14;
+    public static final byte OP_DCONST_1        = 15;
+    public static final byte OP_BIPUSH          = 16;
+    public static final byte OP_SIPUSH          = 17;
+    public static final byte OP_LDC             = 18;
+    public static final byte OP_LDC_W           = 19;
+    public static final byte OP_LDC2_W          = 20;
+    public static final byte OP_ILOAD           = 21;
+    public static final byte OP_LLOAD           = 22;
+    public static final byte OP_FLOAD           = 23;
+    public static final byte OP_DLOAD           = 24;
+    public static final byte OP_ALOAD           = 25;
+    public static final byte OP_ILOAD_0         = 26;
+    public static final byte OP_ILOAD_1         = 27;
+    public static final byte OP_ILOAD_2         = 28;
+    public static final byte OP_ILOAD_3         = 29;
+    public static final byte OP_LLOAD_0         = 30;
+    public static final byte OP_LLOAD_1         = 31;
+    public static final byte OP_LLOAD_2         = 32;
+    public static final byte OP_LLOAD_3         = 33;
+    public static final byte OP_FLOAD_0         = 34;
+    public static final byte OP_FLOAD_1         = 35;
+    public static final byte OP_FLOAD_2         = 36;
+    public static final byte OP_FLOAD_3         = 37;
+    public static final byte OP_DLOAD_0         = 38;
+    public static final byte OP_DLOAD_1         = 39;
+    public static final byte OP_DLOAD_2         = 40;
+    public static final byte OP_DLOAD_3         = 41;
+    public static final byte OP_ALOAD_0         = 42;
+    public static final byte OP_ALOAD_1         = 43;
+    public static final byte OP_ALOAD_2         = 44;
+    public static final byte OP_ALOAD_3         = 45;
+    public static final byte OP_IALOAD          = 46;
+    public static final byte OP_LALOAD          = 47;
+    public static final byte OP_FALOAD          = 48;
+    public static final byte OP_DALOAD          = 49;
+    public static final byte OP_AALOAD          = 50;
+    public static final byte OP_BALOAD          = 51;
+    public static final byte OP_CALOAD          = 52;
+    public static final byte OP_SALOAD          = 53;
+    public static final byte OP_ISTORE          = 54;
+    public static final byte OP_LSTORE          = 55;
+    public static final byte OP_FSTORE          = 56;
+    public static final byte OP_DSTORE          = 57;
+    public static final byte OP_ASTORE          = 58;
+    public static final byte OP_ISTORE_0        = 59;
+    public static final byte OP_ISTORE_1        = 60;
+    public static final byte OP_ISTORE_2        = 61;
+    public static final byte OP_ISTORE_3        = 62;
+    public static final byte OP_LSTORE_0        = 63;
+    public static final byte OP_LSTORE_1        = 64;
+    public static final byte OP_LSTORE_2        = 65;
+    public static final byte OP_LSTORE_3        = 66;
+    public static final byte OP_FSTORE_0        = 67;
+    public static final byte OP_FSTORE_1        = 68;
+    public static final byte OP_FSTORE_2        = 69;
+    public static final byte OP_FSTORE_3        = 70;
+    public static final byte OP_DSTORE_0        = 71;
+    public static final byte OP_DSTORE_1        = 72;
+    public static final byte OP_DSTORE_2        = 73;
+    public static final byte OP_DSTORE_3        = 74;
+    public static final byte OP_ASTORE_0        = 75;
+    public static final byte OP_ASTORE_1        = 76;
+    public static final byte OP_ASTORE_2        = 77;
+    public static final byte OP_ASTORE_3        = 78;
+    public static final byte OP_IASTORE         = 79;
+    public static final byte OP_LASTORE         = 80;
+    public static final byte OP_FASTORE         = 81;
+    public static final byte OP_DASTORE         = 82;
+    public static final byte OP_AASTORE         = 83;
+    public static final byte OP_BASTORE         = 84;
+    public static final byte OP_CASTORE         = 85;
+    public static final byte OP_SASTORE         = 86;
+    public static final byte OP_POP             = 87;
+    public static final byte OP_POP2            = 88;
+    public static final byte OP_DUP             = 89;
+    public static final byte OP_DUP_X1          = 90;
+    public static final byte OP_DUP_X2          = 91;
+    public static final byte OP_DUP2            = 92;
+    public static final byte OP_DUP2_X1         = 93;
+    public static final byte OP_DUP2_X2         = 94;
+    public static final byte OP_SWAP            = 95;
+    public static final byte OP_IADD            = 96;
+    public static final byte OP_LADD            = 97;
+    public static final byte OP_FADD            = 98;
+    public static final byte OP_DADD            = 99;
+    public static final byte OP_ISUB            = 100;
+    public static final byte OP_LSUB            = 101;
+    public static final byte OP_FSUB            = 102;
+    public static final byte OP_DSUB            = 103;
+    public static final byte OP_IMUL            = 104;
+    public static final byte OP_LMUL            = 105;
+    public static final byte OP_FMUL            = 106;
+    public static final byte OP_DMUL            = 107;
+    public static final byte OP_IDIV            = 108;
+    public static final byte OP_LDIV            = 109;
+    public static final byte OP_FDIV            = 110;
+    public static final byte OP_DDIV            = 111;
+    public static final byte OP_IREM            = 112;
+    public static final byte OP_LREM            = 113;
+    public static final byte OP_FREM            = 114;
+    public static final byte OP_DREM            = 115;
+    public static final byte OP_INEG            = 116;
+    public static final byte OP_LNEG            = 117;
+    public static final byte OP_FNEG            = 118;
+    public static final byte OP_DNEG            = 119;
+    public static final byte OP_ISHL            = 120;
+    public static final byte OP_LSHL            = 121;
+    public static final byte OP_ISHR            = 122;
+    public static final byte OP_LSHR            = 123;
+    public static final byte OP_IUSHR           = 124;
+    public static final byte OP_LUSHR           = 125;
+    public static final byte OP_IAND            = 126;
+    public static final byte OP_LAND            = 127;
+    public static final byte OP_IOR             = -128;
+    public static final byte OP_LOR             = -127;
+    public static final byte OP_IXOR            = -126;
+    public static final byte OP_LXOR            = -125;
+    public static final byte OP_IINC            = -124;
+    public static final byte OP_I2L             = -123;
+    public static final byte OP_I2F             = -122;
+    public static final byte OP_I2D             = -121;
+    public static final byte OP_L2I             = -120;
+    public static final byte OP_L2F             = -119;
+    public static final byte OP_L2D             = -118;
+    public static final byte OP_F2I             = -117;
+    public static final byte OP_F2L             = -116;
+    public static final byte OP_F2D             = -115;
+    public static final byte OP_D2I             = -114;
+    public static final byte OP_D2L             = -113;
+    public static final byte OP_D2F             = -112;
+    public static final byte OP_I2B             = -111;
+    public static final byte OP_I2C             = -110;
+    public static final byte OP_I2S             = -109;
+    public static final byte OP_LCMP            = -108;
+    public static final byte OP_FCMPL           = -107;
+    public static final byte OP_FCMPG           = -106;
+    public static final byte OP_DCMPL           = -105;
+    public static final byte OP_DCMPG           = -104;
+    public static final byte OP_IFEQ            = -103;
+    public static final byte OP_IFNE            = -102;
+    public static final byte OP_IFLT            = -101;
+    public static final byte OP_IFGE            = -100;
+    public static final byte OP_IFGT            = -99;
+    public static final byte OP_IFLE            = -98;
+    public static final byte OP_IFICMPEQ        = -97;
+    public static final byte OP_IFICMPNE        = -96;
+    public static final byte OP_IFICMPLT        = -95;
+    public static final byte OP_IFICMPGE        = -94;
+    public static final byte OP_IFICMPGT        = -93;
+    public static final byte OP_IFICMPLE        = -92;
+    public static final byte OP_IFACMPEQ        = -91;
+    public static final byte OP_IFACMPNE        = -90;
+    public static final byte OP_GOTO            = -89;
+    public static final byte OP_JSR             = -88;
+    public static final byte OP_RET             = -87;
+    public static final byte OP_TABLESWITCH     = -86;
+    public static final byte OP_LOOKUPSWITCH    = -85;
+    public static final byte OP_IRETURN         = -84;
+    public static final byte OP_LRETURN         = -83;
+    public static final byte OP_FRETURN         = -82;
+    public static final byte OP_DRETURN         = -81;
+    public static final byte OP_ARETURN         = -80;
+    public static final byte OP_RETURN          = -79;
+    public static final byte OP_GETSTATIC       = -78;
+    public static final byte OP_PUTSTATIC       = -77;
+    public static final byte OP_GETFIELD        = -76;
+    public static final byte OP_PUTFIELD        = -75;
+    public static final byte OP_INVOKEVIRTUAL   = -74;
+    public static final byte OP_INVOKESPECIAL   = -73;
+    public static final byte OP_INVOKESTATIC    = -72;
+    public static final byte OP_INVOKEINTERFACE = -71;
 //  public static final byte OP_UNUSED           = -70;
-    public static final byte OP_NEW              = -69;
-    public static final byte OP_NEWARRAY         = -68;
-    public static final byte OP_ANEWARRAY        = -67;
-    public static final byte OP_ARRAYLENGTH      = -66;
-    public static final byte OP_ATHROW           = -65;
-    public static final byte OP_CHECKCAST        = -64;
-    public static final byte OP_INSTANCEOF       = -63;
-    public static final byte OP_MONITORENTER     = -62;
-    public static final byte OP_MONITOREXIT      = -61;
-    public static final byte OP_WIDE             = -60;
-    public static final byte OP_MULTIANEWARRAY   = -59;
-    public static final byte OP_IFNULL           = -58;
-    public static final byte OP_IFNONNULL        = -57;
-    public static final byte OP_GOTO_W           = -56;
-    public static final byte OP_JSR_W            = -55;
+    public static final byte OP_NEW            = -69;
+    public static final byte OP_NEWARRAY       = -68;
+    public static final byte OP_ANEWARRAY      = -67;
+    public static final byte OP_ARRAYLENGTH    = -66;
+    public static final byte OP_ATHROW         = -65;
+    public static final byte OP_CHECKCAST      = -64;
+    public static final byte OP_INSTANCEOF     = -63;
+    public static final byte OP_MONITORENTER   = -62;
+    public static final byte OP_MONITOREXIT    = -61;
+    public static final byte OP_WIDE           = -60;
+    public static final byte OP_MULTIANEWARRAY = -59;
+    public static final byte OP_IFNULL         = -58;
+    public static final byte OP_IFNONNULL      = -57;
+    public static final byte OP_GOTO_W         = -56;
+    public static final byte OP_JSR_W          = -55;
 
 
     public static final String[] NAMES =
@@ -438,12 +438,12 @@ public interface InstructionConstants
     };
 
 
-    public static final byte ARRAY_T_BOOLEAN     = 4;
-    public static final byte ARRAY_T_CHAR        = 5;
-    public static final byte ARRAY_T_FLOAT       = 6;
-    public static final byte ARRAY_T_DOUBLE      = 7;
-    public static final byte ARRAY_T_BYTE        = 8;
-    public static final byte ARRAY_T_SHORT       = 9;
-    public static final byte ARRAY_T_INT         = 10;
-    public static final byte ARRAY_T_LONG        = 11;
+    public static final byte ARRAY_T_BOOLEAN = 4;
+    public static final byte ARRAY_T_CHAR    = 5;
+    public static final byte ARRAY_T_FLOAT   = 6;
+    public static final byte ARRAY_T_DOUBLE  = 7;
+    public static final byte ARRAY_T_BYTE    = 8;
+    public static final byte ARRAY_T_SHORT   = 9;
+    public static final byte ARRAY_T_INT     = 10;
+    public static final byte ARRAY_T_LONG    = 11;
 }
diff --git a/src/proguard/classfile/instruction/InstructionCounter.java b/src/proguard/classfile/instruction/InstructionCounter.java
deleted file mode 100644
index c95e1ae..0000000
--- a/src/proguard/classfile/instruction/InstructionCounter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/* $Id: InstructionCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.instruction;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.instruction.*;
-
-/**
- * This InstructionVisitor counts the number of instructions that has been visited.
- *
- * @author Eric Lafortune
- */
-public class InstructionCounter implements InstructionVisitor
-{
-    private int count;
-
-
-    /**
-     * Returns the number of instructions that has been visited so far.
-     */
-    public int getCount()
-    {
-        return count;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitBranchInstruction(ClassFile         classFile,
-                                       MethodInfo        methodInfo,
-                                       CodeAttrInfo      codeAttrInfo,
-                                       int               offset,
-                                       BranchInstruction branchInstruction)
-    {
-        count++;
-    }
-
-
-    public void visitCpInstruction(ClassFile     classFile,
-                                   MethodInfo    methodInfo,
-                                   CodeAttrInfo  codeAttrInfo,
-                                   int           offset,
-                                   CpInstruction cpInstruction)
-    {
-        count++;
-    }
-
-
-    public void visitLookUpSwitchInstruction(ClassFile               classFile,
-                                             MethodInfo              methodInfo,
-                                             CodeAttrInfo            codeAttrInfo,
-                                             int                     offset,
-                                             LookUpSwitchInstruction lookUpSwitchInstruction)
-    {
-        count++;
-    }
-
-
-    public void visitSimpleInstruction(ClassFile         classFile,
-                                       MethodInfo        methodInfo,
-                                       CodeAttrInfo      codeAttrInfo,
-                                       int               offset,
-                                       SimpleInstruction simpleInstruction)
-    {
-        count++;
-    }
-
-
-    public void visitTableSwitchInstruction(ClassFile              classFile,
-                                            MethodInfo             methodInfo,
-                                            CodeAttrInfo           codeAttrInfo,
-                                            int                    offset,
-                                            TableSwitchInstruction tableSwitchInstruction)
-    {
-        count++;
-    }
-
-
-    public void visitVariableInstruction(ClassFile           classFile,
-                                         MethodInfo          methodInfo,
-                                         CodeAttrInfo        codeAttrInfo,
-                                         int                 offset,
-                                         VariableInstruction variableInstruction)
-    {
-        count++;
-    }
-}
diff --git a/src/proguard/classfile/instruction/InstructionFactory.java b/src/proguard/classfile/instruction/InstructionFactory.java
index 25e9990..6b3b71a 100644
--- a/src/proguard/classfile/instruction/InstructionFactory.java
+++ b/src/proguard/classfile/instruction/InstructionFactory.java
@@ -1,6 +1,6 @@
-/* $Id: InstructionFactory.java,v 1.6.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,22 +27,12 @@ package proguard.classfile.instruction;
  */
 public class InstructionFactory
 {
-    // Shared copies of Instruction objects, to avoid creating a lot of objects.
-    private static final SimpleInstruction       simpleInstruction       = new SimpleInstruction();
-    private static final CpInstruction           cpInstruction           = new CpInstruction();
-    private static final VariableInstruction     variableInstruction     = new VariableInstruction();
-    private static final BranchInstruction       branchInstruction       = new BranchInstruction();
-    private static final TableSwitchInstruction  tableSwitchInstruction  = new TableSwitchInstruction();
-    private static final LookUpSwitchInstruction lookUpSwitchInstruction = new LookUpSwitchInstruction();
-
-
     /**
      * Creates a new Instruction from the data in the byte array, starting
      * at the given index.
      */
     public static Instruction create(byte[] code, int offset)
     {
-        // We'll re-use the static Instruction objects.
         Instruction instruction;
 
         int  index  = offset;
@@ -175,7 +165,7 @@ public class InstructionFactory
 
             case InstructionConstants.OP_MONITORENTER:
             case InstructionConstants.OP_MONITOREXIT:
-                instruction = simpleInstruction;
+                instruction = new SimpleInstruction();
                 break;
 
             // Instructions with a contant pool index.
@@ -198,7 +188,7 @@ public class InstructionFactory
             case InstructionConstants.OP_CHECKCAST:
             case InstructionConstants.OP_INSTANCEOF:
             case InstructionConstants.OP_MULTIANEWARRAY:
-                instruction = cpInstruction;
+                instruction = new ConstantInstruction();
                 break;
 
             // Instructions with a local variable index.
@@ -257,8 +247,7 @@ public class InstructionFactory
             case InstructionConstants.OP_IINC:
 
             case InstructionConstants.OP_RET:
-                variableInstruction.wide = wide;
-                instruction = variableInstruction;
+                instruction = new VariableInstruction(wide);
                 break;
 
             // Instructions with a branch offset operand.
@@ -284,21 +273,21 @@ public class InstructionFactory
 
             case InstructionConstants.OP_GOTO_W:
             case InstructionConstants.OP_JSR_W:
-                instruction = branchInstruction;
+                instruction = new BranchInstruction();
                 break;
 
             //  The tableswitch instruction.
             case InstructionConstants.OP_TABLESWITCH:
-                instruction = tableSwitchInstruction;
+                instruction = new TableSwitchInstruction();
                 break;
 
             //  The lookupswitch instruction.
             case InstructionConstants.OP_LOOKUPSWITCH:
-                instruction = lookUpSwitchInstruction;
+                instruction = new LookUpSwitchInstruction();
                 break;
 
             default:
-                throw new IllegalArgumentException("Unknown instruction ["+opcode+"] at offset "+offset);
+                throw new IllegalArgumentException("Unknown instruction opcode ["+opcode+"] at offset "+offset);
         }
 
         instruction.opcode = opcode;
diff --git a/src/proguard/classfile/instruction/InstructionUtil.java b/src/proguard/classfile/instruction/InstructionUtil.java
new file mode 100644
index 0000000..74ed3e3
--- /dev/null
+++ b/src/proguard/classfile/instruction/InstructionUtil.java
@@ -0,0 +1,67 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+import proguard.classfile.ClassConstants;
+
+/**
+ * Utility methods for converting between representations of names and
+ * descriptions.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionUtil
+{
+    /**
+     * Returns the internal type corresponding to the given 'newarray' type.
+     * @param arrayType <code>InstructionConstants.ARRAY_T_BOOLEAN</code>,
+     *                  <code>InstructionConstants.ARRAY_T_BYTE</code>,
+     *                  <code>InstructionConstants.ARRAY_T_CHAR</code>,
+     *                  <code>InstructionConstants.ARRAY_T_SHORT</code>,
+     *                  <code>InstructionConstants.ARRAY_T_INT</code>,
+     *                  <code>InstructionConstants.ARRAY_T_LONG</code>,
+     *                  <code>InstructionConstants.ARRAY_T_FLOAT</code>, or
+     *                  <code>InstructionConstants.ARRAY_T_DOUBLE</code>.
+     * @return <code>ClassConstants.INTERNAL_TYPE_BOOLEAN</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_BYTE</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_CHAR</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_SHORT</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_INT</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_LONG</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_FLOAT</code>, or
+     *         <code>ClassConstants.INTERNAL_TYPE_DOUBLE</code>.
+     */
+    public static char internalTypeFromArrayType(byte arrayType)
+    {
+        switch (arrayType)
+        {
+            case InstructionConstants.ARRAY_T_BOOLEAN: return ClassConstants.INTERNAL_TYPE_BOOLEAN;
+            case InstructionConstants.ARRAY_T_CHAR:    return ClassConstants.INTERNAL_TYPE_CHAR;
+            case InstructionConstants.ARRAY_T_FLOAT:   return ClassConstants.INTERNAL_TYPE_FLOAT;
+            case InstructionConstants.ARRAY_T_DOUBLE:  return ClassConstants.INTERNAL_TYPE_DOUBLE;
+            case InstructionConstants.ARRAY_T_BYTE:    return ClassConstants.INTERNAL_TYPE_BYTE;
+            case InstructionConstants.ARRAY_T_SHORT:   return ClassConstants.INTERNAL_TYPE_SHORT;
+            case InstructionConstants.ARRAY_T_INT:     return ClassConstants.INTERNAL_TYPE_INT;
+            case InstructionConstants.ARRAY_T_LONG:    return ClassConstants.INTERNAL_TYPE_LONG;
+            default: throw new IllegalArgumentException("Unknown array type ["+arrayType+"]");
+        }
+    }
+}
diff --git a/src/proguard/classfile/instruction/InstructionVisitor.java b/src/proguard/classfile/instruction/InstructionVisitor.java
deleted file mode 100644
index e424f43..0000000
--- a/src/proguard/classfile/instruction/InstructionVisitor.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/* $Id: InstructionVisitor.java,v 1.14.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.instruction;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-
-
-/**
- * This interface specifies the methods for a visitor of
- * <code>Instruction</code> objects.
- *
- * @author Eric Lafortune
- */
-public interface InstructionVisitor
-{
-    public void visitSimpleInstruction(      ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction       simpleInstruction);
-    public void visitVariableInstruction(    ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction     variableInstruction);
-    public void visitCpInstruction(          ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction           cpInstruction);
-    public void visitBranchInstruction(      ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction       branchInstruction);
-    public void visitTableSwitchInstruction( ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction  tableSwitchInstruction);
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction);
-}
diff --git a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
index ded09a5..adca0f1 100644
--- a/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
+++ b/src/proguard/classfile/instruction/LookUpSwitchInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: LookUpSwitchInstruction.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * This Instruction represents a simple instruction without variable arguments
@@ -29,12 +30,9 @@ import proguard.classfile.attribute.*;
  *
  * @author Eric Lafortune
  */
-public class LookUpSwitchInstruction extends Instruction
+public class LookUpSwitchInstruction extends SwitchInstruction
 {
-    public int   defaultOffset;
-    public int   jumpOffsetCount;
     public int[] cases;
-    public int[] jumpOffsets;
 
 
     /**
@@ -44,17 +42,31 @@ public class LookUpSwitchInstruction extends Instruction
 
 
     /**
+     * Creates a new LookUpSwitchInstruction with the given arguments.
+     */
+    public LookUpSwitchInstruction(byte  opcode,
+                                   int   defaultOffset,
+                                   int[] cases,
+                                   int[] jumpOffsets)
+    {
+        this.opcode        = opcode;
+        this.defaultOffset = defaultOffset;
+        this.cases         = cases;
+        this.jumpOffsets   = jumpOffsets;
+    }
+
+
+    /**
      * Copies the given instruction into this instruction.
      * @param lookUpSwitchInstruction the instruction to be copied.
      * @return this instruction.
      */
     public LookUpSwitchInstruction copy(LookUpSwitchInstruction lookUpSwitchInstruction)
     {
-        this.opcode          = lookUpSwitchInstruction.opcode;
-        this.defaultOffset   = lookUpSwitchInstruction.defaultOffset;
-        this.jumpOffsetCount = lookUpSwitchInstruction.jumpOffsetCount;
-        this.cases           = lookUpSwitchInstruction.cases;
-        this.jumpOffsets     = lookUpSwitchInstruction.jumpOffsets;
+        this.opcode        = lookUpSwitchInstruction.opcode;
+        this.defaultOffset = lookUpSwitchInstruction.defaultOffset;
+        this.cases         = lookUpSwitchInstruction.cases;
+        this.jumpOffsets   = lookUpSwitchInstruction.jumpOffsets;
 
         return this;
     }
@@ -74,18 +86,13 @@ public class LookUpSwitchInstruction extends Instruction
         offset += -offset & 3;
 
         // Read the two 32-bit arguments.
-        defaultOffset   = readInt(code, offset); offset += 4;
-        jumpOffsetCount = readInt(code, offset); offset += 4;
-
-        // Make sure there are sufficiently large match-offset tables.
-        if (jumpOffsets == null ||
-            jumpOffsets.length < jumpOffsetCount)
-        {
-            cases       = new int[jumpOffsetCount];
-            jumpOffsets = new int[jumpOffsetCount];
-        }
+        defaultOffset       = readInt(code, offset); offset += 4;
+        int jumpOffsetCount = readInt(code, offset); offset += 4;
 
         // Read the matches-offset pairs.
+        cases       = new int[jumpOffsetCount];
+        jumpOffsets = new int[jumpOffsetCount];
+
         for (int index = 0; index < jumpOffsetCount; index++)
         {
             cases[index]       = readInt(code, offset); offset += 4;
@@ -103,11 +110,11 @@ public class LookUpSwitchInstruction extends Instruction
         }
 
         // Write the two 32-bit arguments.
-        writeInt(code, offset, defaultOffset);   offset += 4;
-        writeInt(code, offset, jumpOffsetCount); offset += 4;
+        writeInt(code, offset, defaultOffset); offset += 4;
+        writeInt(code, offset, cases.length);  offset += 4;
 
         // Write the matches-offset pairs.
-        for (int index = 0; index < jumpOffsetCount; index++)
+        for (int index = 0; index < cases.length; index++)
         {
             writeInt(code, offset, cases[index]);       offset += 4;
             writeInt(code, offset, jumpOffsets[index]); offset += 4;
@@ -117,26 +124,12 @@ public class LookUpSwitchInstruction extends Instruction
 
     public int length(int offset)
     {
-        return 1 + (-(offset+1) & 3) + 8 + jumpOffsetCount * 8;
-    }
-
-
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
-    {
-        instructionVisitor.visitLookUpSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
+        return 1 + (-(offset+1) & 3) + 8 + cases.length * 8;
     }
 
 
-    public String toString(int offset)
-    {
-        return "["+offset+"] "+getName()+" (switchCaseCount="+jumpOffsetCount+")";
-    }
-
-
-    // Implementations for Object.
-
-    public String toString()
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        return getName()+" (switchCaseCount="+jumpOffsetCount+")";
+        instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, this);
     }
 }
diff --git a/src/proguard/classfile/instruction/SimpleInstruction.java b/src/proguard/classfile/instruction/SimpleInstruction.java
index da6484c..d9ada3e 100644
--- a/src/proguard/classfile/instruction/SimpleInstruction.java
+++ b/src/proguard/classfile/instruction/SimpleInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: SimpleInstruction.java,v 1.9.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * This Instruction represents a simple instruction without variable arguments
@@ -45,7 +46,7 @@ public class SimpleInstruction extends Instruction
      */
     public SimpleInstruction(byte opcode)
     {
-        this(opcode, 0);
+        this(opcode, embeddedConstant(opcode));
     }
 
 
@@ -73,76 +74,123 @@ public class SimpleInstruction extends Instruction
     }
 
 
+    /**
+     * Return the embedded constant of the given opcode, or 0 if the opcode
+     * doesn't have one.
+     */
+    private static int embeddedConstant(byte opcode)
+    {
+        switch (opcode)
+        {
+            case InstructionConstants.OP_ICONST_M1: return -1;
+
+            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_LCONST_1:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_DCONST_1: return 1;
+
+            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_FCONST_2: return 2;
+
+            case InstructionConstants.OP_ICONST_3: return 3;
+
+            case InstructionConstants.OP_ICONST_4: return 4;
+
+            case InstructionConstants.OP_ICONST_5: return 5;
+
+            default: return 0;
+        }
+    }
+
+
     // Implementations for Instruction.
 
-    public Instruction shrink()
+    public byte canonicalOpcode()
     {
-        int requiredConstantSize = requiredConstantSize();
-
-        // Is the (integer) constant size right?
-        if (opcode != InstructionConstants.OP_NEWARRAY &&
-            requiredConstantSize != constantSize())
+        // Replace any _1, _2, _3,... extension by _0.
+        switch (opcode)
         {
-            // Can we replace the integer push instruction?
-            switch (requiredConstantSize)
-            {
-                case 0:
-                    opcode = (byte)(InstructionConstants.OP_ICONST_0 + constant);
-                    break;
-
-                case 1:
-                    opcode = InstructionConstants.OP_BIPUSH;
-                    break;
-
-                case 2:
-                    opcode = InstructionConstants.OP_SIPUSH;
-                    break;
-
-                default:
-                    throw new IllegalArgumentException("Simple instruction can't be widened ("+this.toString()+")");
-            }
-        }
+            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_BIPUSH:
+            case InstructionConstants.OP_SIPUSH:   return InstructionConstants.OP_ICONST_0;
 
-        return this;
+            case InstructionConstants.OP_LCONST_0:
+            case InstructionConstants.OP_LCONST_1: return InstructionConstants.OP_LCONST_0;
+
+            case InstructionConstants.OP_FCONST_0:
+            case InstructionConstants.OP_FCONST_1:
+            case InstructionConstants.OP_FCONST_2: return InstructionConstants.OP_FCONST_0;
+
+            case InstructionConstants.OP_DCONST_0:
+            case InstructionConstants.OP_DCONST_1: return InstructionConstants.OP_DCONST_0;
+
+            default: return opcode;
+        }
     }
 
-    protected void readInfo(byte[] code, int offset)
+    public Instruction shrink()
     {
-        // Also initialize embedded constants that are different from 0.
+        // Reconstruct the opcode of the shortest instruction, if there are
+        // any alternatives.
         switch (opcode)
         {
             case InstructionConstants.OP_ICONST_M1:
-                constant = -1;
+            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_BIPUSH:
+            case InstructionConstants.OP_SIPUSH:
+                switch (requiredConstantSize())
+                {
+                    case 0:
+                        opcode = (byte)(InstructionConstants.OP_ICONST_0 + constant);
+                        break;
+                    case 1:
+                        opcode = InstructionConstants.OP_BIPUSH;
+                        break;
+                    case 2:
+                        opcode = InstructionConstants.OP_SIPUSH;
+                        break;
+                }
                 break;
 
-            case InstructionConstants.OP_ICONST_1:
+            case InstructionConstants.OP_LCONST_0:
             case InstructionConstants.OP_LCONST_1:
-            case InstructionConstants.OP_FCONST_1:
-            case InstructionConstants.OP_DCONST_1:
-                constant = 1;
+                opcode = (byte)(InstructionConstants.OP_LCONST_0 + constant);
                 break;
 
-            case InstructionConstants.OP_ICONST_2:
+            case InstructionConstants.OP_FCONST_0:
+            case InstructionConstants.OP_FCONST_1:
             case InstructionConstants.OP_FCONST_2:
-                constant = 2;
+                opcode = (byte)(InstructionConstants.OP_FCONST_0 + constant);
                 break;
 
-            case InstructionConstants.OP_ICONST_3:
-                constant = 3;
+            case InstructionConstants.OP_DCONST_0:
+            case InstructionConstants.OP_DCONST_1:
+                opcode = (byte)(InstructionConstants.OP_DCONST_0 + constant);
                 break;
+        }
 
-            case InstructionConstants.OP_ICONST_4:
-                constant = 4;
-                break;
+        return this;
+    }
 
-            case InstructionConstants.OP_ICONST_5:
-                constant = 5;
-                break;
+    protected void readInfo(byte[] code, int offset)
+    {
+        int constantSize = constantSize();
 
-            default:
-                constant = readSignedValue(code, offset, constantSize());
-                break;
-        }
+        // Also initialize embedded constants that are different from 0.
+        constant = constantSize == 0 ?
+            embeddedConstant(opcode) :
+            readSignedValue(code, offset, constantSize);
     }
 
 
@@ -155,7 +203,7 @@ public class SimpleInstruction extends Instruction
             throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")");
         }
 
-        writeValue(code, offset, constant, constantSize);
+        writeSignedValue(code, offset, constant, constantSize);
     }
 
 
@@ -165,15 +213,9 @@ public class SimpleInstruction extends Instruction
     }
 
 
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
-    {
-        instructionVisitor.visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
-    }
-
-
-    public String toString(int offset)
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        return "["+offset+"] "+getName()+" (constant="+constant+")";
+        instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, this);
     }
 
 
@@ -181,7 +223,8 @@ public class SimpleInstruction extends Instruction
 
     public String toString()
     {
-        return getName()+" (constant="+constant+")";
+        return getName() +
+               (constantSize() > 0 ? " "+constant : "");
     }
 
 
diff --git a/src/proguard/classfile/instruction/SwitchInstruction.java b/src/proguard/classfile/instruction/SwitchInstruction.java
new file mode 100644
index 0000000..96411c6
--- /dev/null
+++ b/src/proguard/classfile/instruction/SwitchInstruction.java
@@ -0,0 +1,83 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction;
+
+/**
+ * This Instruction represents a simple instruction without variable arguments
+ * or constant pool references.
+ *
+ * @author Eric Lafortune
+ */
+public abstract class SwitchInstruction extends Instruction
+{
+    public int   defaultOffset;
+    public int[] jumpOffsets;
+
+
+    /**
+     * Creates an uninitialized SwitchInstruction.
+     */
+    public SwitchInstruction() {}
+
+
+    /**
+     * Creates a new SwitchInstruction with the given arguments.
+     */
+    public SwitchInstruction(byte  opcode,
+                             int   defaultOffset,
+                             int[] jumpOffsets)
+    {
+        this.opcode        = opcode;
+        this.defaultOffset = defaultOffset;
+        this.jumpOffsets   = jumpOffsets;
+    }
+
+
+    /**
+     * Copies the given instruction into this instruction.
+     * @param switchInstruction the instruction to be copied.
+     * @return this instruction.
+     */
+    public SwitchInstruction copy(SwitchInstruction switchInstruction)
+    {
+        this.opcode        = switchInstruction.opcode;
+        this.defaultOffset = switchInstruction.defaultOffset;
+        this.jumpOffsets   = switchInstruction.jumpOffsets;
+
+        return this;
+    }
+
+
+    // Implementations for Instruction.
+
+    public String toString(int offset)
+    {
+        return "["+offset+"] "+toString()+" (target="+(offset+defaultOffset)+")";
+    }
+
+
+    // Implementations for Object.
+
+    public String toString()
+    {
+        return getName()+" ("+jumpOffsets.length+" offsets, default="+defaultOffset+")";
+    }
+}
diff --git a/src/proguard/classfile/instruction/TableSwitchInstruction.java b/src/proguard/classfile/instruction/TableSwitchInstruction.java
index 096238a..42e448c 100644
--- a/src/proguard/classfile/instruction/TableSwitchInstruction.java
+++ b/src/proguard/classfile/instruction/TableSwitchInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: TableSwitchInstruction.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * This Instruction represents a simple instruction without variable arguments
@@ -29,13 +30,10 @@ import proguard.classfile.attribute.*;
  *
  * @author Eric Lafortune
  */
-public class TableSwitchInstruction extends Instruction
+public class TableSwitchInstruction extends SwitchInstruction
 {
-    public int   defaultOffset;
-    public int   lowCase;
-    public int   highCase;
-    public int   jumpOffsetCount;
-    public int[] jumpOffsets;
+    public int lowCase;
+    public int highCase;
 
 
     /**
@@ -45,18 +43,34 @@ public class TableSwitchInstruction extends Instruction
 
 
     /**
+     * Creates a new TableSwitchInstruction with the given arguments.
+     */
+    public TableSwitchInstruction(byte  opcode,
+                                  int   defaultOffset,
+                                  int   lowCase,
+                                  int   highCase,
+                                  int[] jumpOffsets)
+    {
+        this.opcode        = opcode;
+        this.defaultOffset = defaultOffset;
+        this.lowCase       = lowCase;
+        this.highCase      = highCase;
+        this.jumpOffsets   = jumpOffsets;
+    }
+
+
+    /**
      * Copies the given instruction into this instruction.
      * @param tableSwitchInstruction the instruction to be copied.
      * @return this instruction.
      */
     public TableSwitchInstruction copy(TableSwitchInstruction tableSwitchInstruction)
     {
-        this.opcode          = tableSwitchInstruction.opcode;
-        this.defaultOffset   = tableSwitchInstruction.defaultOffset;
-        this.lowCase         = tableSwitchInstruction.lowCase;
-        this.highCase        = tableSwitchInstruction.highCase;
-        this.jumpOffsetCount = tableSwitchInstruction.jumpOffsetCount;
-        this.jumpOffsets     = tableSwitchInstruction.jumpOffsets;
+        this.opcode        = tableSwitchInstruction.opcode;
+        this.defaultOffset = tableSwitchInstruction.defaultOffset;
+        this.lowCase       = tableSwitchInstruction.lowCase;
+        this.highCase      = tableSwitchInstruction.highCase;
+        this.jumpOffsets   = tableSwitchInstruction.jumpOffsets;
 
         return this;
     }
@@ -80,16 +94,10 @@ public class TableSwitchInstruction extends Instruction
         lowCase       = readInt(code, offset); offset += 4;
         highCase      = readInt(code, offset); offset += 4;
 
-        // Make sure there is a sufficiently large jump offset table.
-        jumpOffsetCount = highCase - lowCase + 1;
-        if (jumpOffsets == null ||
-            jumpOffsets.length < jumpOffsetCount)
-        {
-            jumpOffsets = new int[jumpOffsetCount];
-        }
-
         // Read the jump offsets.
-        for (int index = 0; index < jumpOffsetCount; index++)
+        jumpOffsets = new int[highCase - lowCase + 1];
+
+        for (int index = 0; index < jumpOffsets.length; index++)
         {
             jumpOffsets[index] = readInt(code, offset); offset += 4;
         }
@@ -124,22 +132,8 @@ public class TableSwitchInstruction extends Instruction
     }
 
 
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
-    {
-        instructionVisitor.visitTableSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
-    }
-
-
-    public String toString(int offset)
-    {
-        return "["+offset+"] "+getName()+" (low="+lowCase+", high="+highCase+")";
-    }
-
-
-    // Implementations for Object.
-
-    public String toString()
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        return getName()+" (low="+lowCase+", high="+highCase+")";
+        instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, this);
     }
 }
diff --git a/src/proguard/classfile/instruction/VariableInstruction.java b/src/proguard/classfile/instruction/VariableInstruction.java
index f8d3148..eb025b7 100644
--- a/src/proguard/classfile/instruction/VariableInstruction.java
+++ b/src/proguard/classfile/instruction/VariableInstruction.java
@@ -1,6 +1,6 @@
-/* $Id: VariableInstruction.java,v 1.20.2.4 2007/05/22 21:39:12 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,8 @@
 package proguard.classfile.instruction;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 
 /**
  * This Instruction represents an instruction that refers to a variable on the
@@ -31,9 +32,9 @@ import proguard.classfile.attribute.*;
  */
 public class VariableInstruction extends Instruction
 {
+    public boolean wide;
     public int     variableIndex;
     public int     constant;
-    public boolean wide;
 
 
     /**
@@ -42,6 +43,18 @@ public class VariableInstruction extends Instruction
     public VariableInstruction() {}
 
 
+    public VariableInstruction(boolean wide)
+    {
+        this.wide = wide;
+    }
+
+
+    public VariableInstruction(byte opcode)
+    {
+        this(opcode, embeddedVariable(opcode), 0);
+    }
+
+
     public VariableInstruction(byte opcode,
                                int  variableIndex)
     {
@@ -78,14 +91,60 @@ public class VariableInstruction extends Instruction
 
 
     /**
-     * Returns whether this instruction loads the value of a variable onto the
-     * stack, or if it stores one.
+     * Return the embedded variable of the given opcode, or 0 if the opcode
+     * doesn't have one.
+     */
+    private static int embeddedVariable(byte opcode)
+    {
+        switch (opcode)
+        {
+            case InstructionConstants.OP_ILOAD_1:
+            case InstructionConstants.OP_LLOAD_1:
+            case InstructionConstants.OP_FLOAD_1:
+            case InstructionConstants.OP_DLOAD_1:
+            case InstructionConstants.OP_ALOAD_1:
+            case InstructionConstants.OP_ISTORE_1:
+            case InstructionConstants.OP_LSTORE_1:
+            case InstructionConstants.OP_FSTORE_1:
+            case InstructionConstants.OP_DSTORE_1:
+            case InstructionConstants.OP_ASTORE_1: return 1;
+
+            case InstructionConstants.OP_ILOAD_2:
+            case InstructionConstants.OP_LLOAD_2:
+            case InstructionConstants.OP_FLOAD_2:
+            case InstructionConstants.OP_DLOAD_2:
+            case InstructionConstants.OP_ALOAD_2:
+            case InstructionConstants.OP_ISTORE_2:
+            case InstructionConstants.OP_LSTORE_2:
+            case InstructionConstants.OP_FSTORE_2:
+            case InstructionConstants.OP_DSTORE_2:
+            case InstructionConstants.OP_ASTORE_2: return 2;
+
+            case InstructionConstants.OP_ILOAD_3:
+            case InstructionConstants.OP_LLOAD_3:
+            case InstructionConstants.OP_FLOAD_3:
+            case InstructionConstants.OP_DLOAD_3:
+            case InstructionConstants.OP_ALOAD_3:
+            case InstructionConstants.OP_ISTORE_3:
+            case InstructionConstants.OP_LSTORE_3:
+            case InstructionConstants.OP_FSTORE_3:
+            case InstructionConstants.OP_DSTORE_3:
+            case InstructionConstants.OP_ASTORE_3: return 3;
+
+            default: return 0;
+        }
+    }
+
+
+    /**
+     * Returns whether this instruction loads the value of a variable.
+     * The value is true for the ret instruction, but false 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.
-        // The iinc instruction is considered a store instruction.
+        // 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;
     }
@@ -93,53 +152,60 @@ public class VariableInstruction extends Instruction
 
     // Implementations for Instruction.
 
-    public Instruction shrink()
+    public byte canonicalOpcode()
     {
-        // First widen the instruction.
+        // Remove the _0, _1, _2, _3 extension, if any.
         switch (opcode)
         {
             case InstructionConstants.OP_ILOAD_0:
             case InstructionConstants.OP_ILOAD_1:
             case InstructionConstants.OP_ILOAD_2:
-            case InstructionConstants.OP_ILOAD_3: opcode = InstructionConstants.OP_ILOAD; break;
+            case InstructionConstants.OP_ILOAD_3: return InstructionConstants.OP_ILOAD;
             case InstructionConstants.OP_LLOAD_0:
             case InstructionConstants.OP_LLOAD_1:
             case InstructionConstants.OP_LLOAD_2:
-            case InstructionConstants.OP_LLOAD_3: opcode = InstructionConstants.OP_LLOAD; break;
+            case InstructionConstants.OP_LLOAD_3: return InstructionConstants.OP_LLOAD;
             case InstructionConstants.OP_FLOAD_0:
             case InstructionConstants.OP_FLOAD_1:
             case InstructionConstants.OP_FLOAD_2:
-            case InstructionConstants.OP_FLOAD_3: opcode = InstructionConstants.OP_FLOAD; break;
+            case InstructionConstants.OP_FLOAD_3: return InstructionConstants.OP_FLOAD;
             case InstructionConstants.OP_DLOAD_0:
             case InstructionConstants.OP_DLOAD_1:
             case InstructionConstants.OP_DLOAD_2:
-            case InstructionConstants.OP_DLOAD_3: opcode = InstructionConstants.OP_DLOAD; break;
+            case InstructionConstants.OP_DLOAD_3: return InstructionConstants.OP_DLOAD;
             case InstructionConstants.OP_ALOAD_0:
             case InstructionConstants.OP_ALOAD_1:
             case InstructionConstants.OP_ALOAD_2:
-            case InstructionConstants.OP_ALOAD_3: opcode = InstructionConstants.OP_ALOAD; break;
+            case InstructionConstants.OP_ALOAD_3: return InstructionConstants.OP_ALOAD;
 
             case InstructionConstants.OP_ISTORE_0:
             case InstructionConstants.OP_ISTORE_1:
             case InstructionConstants.OP_ISTORE_2:
-            case InstructionConstants.OP_ISTORE_3: opcode = InstructionConstants.OP_ISTORE; break;
+            case InstructionConstants.OP_ISTORE_3: return InstructionConstants.OP_ISTORE;
             case InstructionConstants.OP_LSTORE_0:
             case InstructionConstants.OP_LSTORE_1:
             case InstructionConstants.OP_LSTORE_2:
-            case InstructionConstants.OP_LSTORE_3: opcode = InstructionConstants.OP_LSTORE; break;
+            case InstructionConstants.OP_LSTORE_3: return InstructionConstants.OP_LSTORE;
             case InstructionConstants.OP_FSTORE_0:
             case InstructionConstants.OP_FSTORE_1:
             case InstructionConstants.OP_FSTORE_2:
-            case InstructionConstants.OP_FSTORE_3: opcode = InstructionConstants.OP_FSTORE; break;
+            case InstructionConstants.OP_FSTORE_3: return InstructionConstants.OP_FSTORE;
             case InstructionConstants.OP_DSTORE_0:
             case InstructionConstants.OP_DSTORE_1:
             case InstructionConstants.OP_DSTORE_2:
-            case InstructionConstants.OP_DSTORE_3: opcode = InstructionConstants.OP_DSTORE; break;
+            case InstructionConstants.OP_DSTORE_3: return InstructionConstants.OP_DSTORE;
             case InstructionConstants.OP_ASTORE_0:
             case InstructionConstants.OP_ASTORE_1:
             case InstructionConstants.OP_ASTORE_2:
-            case InstructionConstants.OP_ASTORE_3: opcode = InstructionConstants.OP_ASTORE; break;
+            case InstructionConstants.OP_ASTORE_3: return InstructionConstants.OP_ASTORE;
+
+            default: return opcode;
         }
+    }
+
+    public Instruction shrink()
+    {
+        opcode = canonicalOpcode();
 
         // Is this instruction pointing to a variable with index from 0 to 3?
         if (variableIndex <= 3)
@@ -212,7 +278,7 @@ public class VariableInstruction extends Instruction
         }
 
         writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize;
-        writeValue(code, offset, constant,      constantSize);
+        writeSignedValue(code, offset, constant, constantSize);
     }
 
 
@@ -222,15 +288,9 @@ public class VariableInstruction extends Instruction
     }
 
 
-    public void accept(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, InstructionVisitor instructionVisitor)
-    {
-        instructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, this);
-    }
-
-
-    public String toString(int offset)
+    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
     {
-        return "["+offset+"] "+getName()+(wide?"_w":"")+" (variableIndex="+variableIndex+", constant="+constant+")";
+        instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, this);
     }
 
 
@@ -238,7 +298,10 @@ public class VariableInstruction extends Instruction
 
     public String toString()
     {
-        return getName()+" (variableIndex="+variableIndex+", constant="+constant+")";
+        return getName() +
+               (wide ? "_w" : "") +
+               " v"+variableIndex +
+               (constantSize() > 0 ? ", "+constant : "");
     }
 
 
diff --git a/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
new file mode 100644
index 0000000..3ba60ea
--- /dev/null
+++ b/src/proguard/classfile/instruction/visitor/AllInstructionVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction.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
+ * objects of the CodeAttribute objects it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AllInstructionVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private final InstructionVisitor instructionVisitor;
+
+
+    public AllInstructionVisitor(InstructionVisitor instructionVisitor)
+    {
+        this.instructionVisitor = instructionVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        codeAttribute.instructionsAccept(clazz, method, instructionVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/instruction/visitor/InstructionCounter.java
similarity index 52%
copy from src/proguard/classfile/visitor/ClassCounter.java
copy to src/proguard/classfile/instruction/visitor/InstructionCounter.java
index 892bcbc..b472f67 100644
--- a/src/proguard/classfile/visitor/ClassCounter.java
+++ b/src/proguard/classfile/instruction/visitor/InstructionCounter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,23 +18,27 @@
  * 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.visitor;
+package proguard.classfile.instruction.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.Instruction;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This ClassFileVisitor counts the number of classes that has been visited.
+ * This InstructionVisitor counts the number of instructions that has been visited.
  *
  * @author Eric Lafortune
  */
-public class ClassCounter implements ClassFileVisitor
+public class InstructionCounter
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
     private int count;
 
 
     /**
-     * Returns the number of classes that has been visited so far.
+     * Returns the number of instructions that has been visited so far.
      */
     public int getCount()
     {
@@ -42,15 +46,13 @@ public class ClassCounter implements ClassFileVisitor
     }
 
 
-    // Implementations for ClassFileVisitor.
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        count++;
-    }
-
+    // Implementations for InstructionVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitAnyInstruction(Clazz         clazz,
+                                    Method        method,
+                                    CodeAttribute codeAttribute,
+                                    int           offset,
+                                    Instruction   instruction)
     {
         count++;
     }
diff --git a/src/proguard/classfile/instruction/visitor/InstructionVisitor.java b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java
new file mode 100644
index 0000000..0565802
--- /dev/null
+++ b/src/proguard/classfile/instruction/visitor/InstructionVisitor.java
@@ -0,0 +1,42 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.instruction.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.*;
+
+
+/**
+ * This interface specifies the methods for a visitor of
+ * <code>Instruction</code> objects.
+ *
+ * @author Eric Lafortune
+ */
+public interface InstructionVisitor
+{
+    public void visitSimpleInstruction(      Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction       simpleInstruction);
+    public void visitVariableInstruction(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction     variableInstruction);
+    public void visitConstantInstruction(    Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction     constantInstruction);
+    public void visitBranchInstruction(      Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction       branchInstruction);
+    public void visitTableSwitchInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction  tableSwitchInstruction);
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction);
+}
diff --git a/src/proguard/classfile/instruction/MultiInstructionVisitor.java b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
similarity index 54%
rename from src/proguard/classfile/instruction/MultiInstructionVisitor.java
rename to src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
index 54b99ad..3e144da 100644
--- a/src/proguard/classfile/instruction/MultiInstructionVisitor.java
+++ b/src/proguard/classfile/instruction/visitor/MultiInstructionVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: MultiInstructionVisitor.java,v 1.5.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,10 +18,11 @@
  * 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;
+package proguard.classfile.instruction.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.*;
 
 
 /**
@@ -34,14 +35,6 @@ public class MultiInstructionVisitor implements InstructionVisitor
 {
     private static final int ARRAY_SIZE_INCREMENT = 5;
 
-    // Shared copies of Instruction objects, to avoid creating a lot of objects.
-    private final SimpleInstruction       simpleInstruction       = new SimpleInstruction();
-    private final CpInstruction           cpInstruction           = new CpInstruction();
-    private final VariableInstruction     variableInstruction     = new VariableInstruction();
-    private final BranchInstruction       branchInstruction       = new BranchInstruction();
-    private final TableSwitchInstruction  tableSwitchInstruction  = new TableSwitchInstruction();
-    private final LookUpSwitchInstruction lookUpSwitchInstruction = new LookUpSwitchInstruction();
-
 
     private InstructionVisitor[] instructionVisitors;
     private int                  instructionVisitorCount;
@@ -88,63 +81,51 @@ public class MultiInstructionVisitor implements InstructionVisitor
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
-        simpleInstruction = this.simpleInstruction.copy(simpleInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, simpleInstruction);
+            instructionVisitors[index].visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
         }
     }
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
-        variableInstruction = this.variableInstruction.copy(variableInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+            instructionVisitors[index].visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
         }
     }
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
-        cpInstruction = this.cpInstruction.copy(cpInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, cpInstruction);
+            instructionVisitors[index].visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction);
         }
     }
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
-        branchInstruction = this.branchInstruction.copy(branchInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+            instructionVisitors[index].visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
         }
     }
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
     {
-        tableSwitchInstruction = this.tableSwitchInstruction.copy(tableSwitchInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitTableSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, tableSwitchInstruction);
+            instructionVisitors[index].visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction);
         }
     }
 
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
     {
-        lookUpSwitchInstruction = this.lookUpSwitchInstruction.copy(lookUpSwitchInstruction);
-
         for (int index = 0; index < instructionVisitorCount; index++)
         {
-            instructionVisitors[index].visitLookUpSwitchInstruction(classFile, methodInfo, codeAttrInfo, offset, lookUpSwitchInstruction);
+            instructionVisitors[index].visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction);
         }
     }
 }
diff --git a/src/proguard/classfile/instruction/visitor/package.html b/src/proguard/classfile/instruction/visitor/package.html
new file mode 100644
index 0000000..a31a408
--- /dev/null
+++ b/src/proguard/classfile/instruction/visitor/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains visitors for instructions.
+</body>
diff --git a/src/proguard/classfile/io/LibraryClassReader.java b/src/proguard/classfile/io/LibraryClassReader.java
new file mode 100644
index 0000000..b059812
--- /dev/null
+++ b/src/proguard/classfile/io/LibraryClassReader.java
@@ -0,0 +1,362 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.io;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.io.DataInput;
+
+/**
+ * This ClassVisitor fills out the LibraryClass objects that it visits with data
+ * from the given DataInput object.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryClassReader
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor
+{
+    private static final LibraryField[]  EMPTY_LIBRARY_FIELDS  = new LibraryField[0];
+    private static final LibraryMethod[] EMPTY_LIBRARY_METHODS = new LibraryMethod[0];
+
+
+    private final RuntimeDataInput dataInput;
+    private final boolean          skipNonPublicClasses;
+    private final boolean          skipNonPublicClassMembers;
+
+    // A global array that acts as a parameter for the visitor methods.
+    private Constant[]      constantPool;
+
+
+    /**
+     * Creates a new ProgramClassReader for reading from the given DataInput.
+     */
+    public LibraryClassReader(DataInput dataInput,
+                              boolean   skipNonPublicClasses,
+                              boolean   skipNonPublicClassMembers)
+    {
+        this.dataInput                 = new RuntimeDataInput(dataInput);
+        this.skipNonPublicClasses      = skipNonPublicClasses;
+        this.skipNonPublicClassMembers = skipNonPublicClassMembers;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass libraryClass)
+    {
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Read and check the magic number.
+        int u4magic = dataInput.readInt();
+
+        ClassUtil.checkMagicNumber(u4magic);
+
+        // Read and check the version numbers.
+        int u2minorVersion = dataInput.readUnsignedShort();
+        int u2majorVersion = dataInput.readUnsignedShort();
+
+        int u4version = ClassUtil.internalClassVersion(u2majorVersion,
+                                                       u2minorVersion);
+
+        ClassUtil.checkVersionNumbers(u4version);
+
+        // Read the constant pool. Note that the first entry is not used.
+        int u2constantPoolCount = dataInput.readUnsignedShort();
+
+        // Create the constant pool array.
+        constantPool = new Constant[u2constantPoolCount];
+
+        for (int index = 1; index < u2constantPoolCount; index++)
+        {
+            Constant constant = createConstant();
+            constant.accept(libraryClass, this);
+
+            int tag = constant.getTag();
+            if (tag == ClassConstants.CONSTANT_Class ||
+                tag == ClassConstants.CONSTANT_Utf8)
+            {
+                constantPool[index] = constant;
+            }
+
+            // Long constants and double constants take up two entries in the
+            // constant pool.
+            if (tag == ClassConstants.CONSTANT_Long ||
+                tag == ClassConstants.CONSTANT_Double)
+            {
+                index++;
+            }
+        }
+
+        // Read the general class information.
+        libraryClass.u2accessFlags = dataInput.readUnsignedShort();
+
+        // We may stop parsing this library class if it's not public anyway.
+        // E.g. only about 60% of all rt.jar classes need to be parsed.
+        if (skipNonPublicClasses &&
+            AccessUtil.accessLevel(libraryClass.getAccessFlags()) < AccessUtil.PUBLIC)
+        {
+            return;
+        }
+
+        // Read the class and super class indices.
+        int u2thisClass  = dataInput.readUnsignedShort();
+        int u2superClass = dataInput.readUnsignedShort();
+
+        // Store their actual names.
+        libraryClass.thisClassName  = getClassName(u2thisClass);
+        libraryClass.superClassName = (u2superClass == 0) ? null :
+                                      getClassName(u2superClass);
+
+        // Read the interfaces
+        int u2interfacesCount = dataInput.readUnsignedShort();
+
+        libraryClass.interfaceNames = new String[u2interfacesCount];
+        for (int index = 0; index < u2interfacesCount; index++)
+        {
+            // Store the actual interface name.
+            int u2interface = dataInput.readUnsignedShort();
+            libraryClass.interfaceNames[index] = getClassName(u2interface);
+        }
+
+        // Read the fields.
+        int u2fieldsCount = dataInput.readUnsignedShort();
+
+        // Create the fields array.
+        LibraryField[] reusableFields = new LibraryField[u2fieldsCount];
+
+        int visibleFieldsCount = 0;
+        for (int index = 0; index < u2fieldsCount; index++)
+        {
+            LibraryField field = new LibraryField();
+            this.visitLibraryMember(libraryClass, field);
+
+            // Only store fields that are visible.
+            if (AccessUtil.accessLevel(field.getAccessFlags()) >=
+                (skipNonPublicClassMembers ? AccessUtil.PROTECTED :
+                                             AccessUtil.PACKAGE_VISIBLE))
+            {
+                reusableFields[visibleFieldsCount++] = field;
+            }
+        }
+
+        // Copy the visible fields (if any) into a fields array of the right size.
+        if (visibleFieldsCount == 0)
+        {
+            libraryClass.fields = EMPTY_LIBRARY_FIELDS;
+        }
+        else
+        {
+            libraryClass.fields = new LibraryField[visibleFieldsCount];
+            System.arraycopy(reusableFields, 0, libraryClass.fields, 0, visibleFieldsCount);
+        }
+
+        // Read the methods.
+        int u2methodsCount = dataInput.readUnsignedShort();
+
+        // Create the methods array.
+        LibraryMethod[] reusableMethods = new LibraryMethod[u2methodsCount];
+
+        int visibleMethodsCount = 0;
+        for (int index = 0; index < u2methodsCount; index++)
+        {
+            LibraryMethod method = new LibraryMethod();
+            this.visitLibraryMember(libraryClass, method);
+
+            // Only store methods that are visible.
+            if (AccessUtil.accessLevel(method.getAccessFlags()) >=
+                (skipNonPublicClassMembers ? AccessUtil.PROTECTED :
+                                             AccessUtil.PACKAGE_VISIBLE))
+            {
+                reusableMethods[visibleMethodsCount++] = method;
+            }
+        }
+
+        // Copy the visible methods (if any) into a methods array of the right size.
+        if (visibleMethodsCount == 0)
+        {
+            libraryClass.methods = EMPTY_LIBRARY_METHODS;
+        }
+        else
+        {
+            libraryClass.methods = new LibraryMethod[visibleMethodsCount];
+            System.arraycopy(reusableMethods, 0, libraryClass.methods, 0, visibleMethodsCount);
+        }
+
+        // Skip the class attributes.
+        skipAttributes();
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember)
+    {
+    }
+
+
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+    {
+        // Read the general field information.
+        libraryMember.u2accessFlags = dataInput.readUnsignedShort();
+        libraryMember.name          = getString(dataInput.readUnsignedShort());
+        libraryMember.descriptor    = getString(dataInput.readUnsignedShort());
+
+        // Skip the field attributes.
+        skipAttributes();
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        dataInput.skipBytes(4);
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        dataInput.skipBytes(8);
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        dataInput.skipBytes(4);
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        dataInput.skipBytes(8);
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        dataInput.skipBytes(2);
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        int u2length = dataInput.readUnsignedShort();
+
+        // Read the UTF-8 bytes.
+        byte[] bytes = new byte[u2length];
+        dataInput.readFully(bytes);
+        utf8Constant.setBytes(bytes);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        dataInput.skipBytes(4);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        classConstant.u2nameIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        dataInput.skipBytes(4);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the class name of the ClassConstant at the specified index in the
+     * reusable constant pool.
+     */
+    private String getClassName(int constantIndex)
+    {
+        ClassConstant classEntry = (ClassConstant)constantPool[constantIndex];
+
+        return getString(classEntry.u2nameIndex);
+    }
+
+
+    /**
+     * Returns the string of the Utf8Constant at the specified index in the
+     * reusable constant pool.
+     */
+    private String getString(int constantIndex)
+    {
+        return ((Utf8Constant)constantPool[constantIndex]).getString();
+    }
+
+
+    private Constant createConstant()
+    {
+        int u1tag = dataInput.readUnsignedByte();
+
+        switch (u1tag)
+        {
+            case ClassConstants.CONSTANT_Utf8:               return new Utf8Constant();
+            case ClassConstants.CONSTANT_Integer:            return new IntegerConstant();
+            case ClassConstants.CONSTANT_Float:              return new FloatConstant();
+            case ClassConstants.CONSTANT_Long:               return new LongConstant();
+            case ClassConstants.CONSTANT_Double:             return new DoubleConstant();
+            case ClassConstants.CONSTANT_String:             return new StringConstant();
+            case ClassConstants.CONSTANT_Fieldref:           return new FieldrefConstant();
+            case ClassConstants.CONSTANT_Methodref:          return new MethodrefConstant();
+            case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant();
+            case ClassConstants.CONSTANT_Class:              return new ClassConstant();
+            case ClassConstants.CONSTANT_NameAndType:        return new NameAndTypeConstant();
+
+            default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool");
+        }
+    }
+
+
+    private void skipAttributes()
+    {
+        int u2attributesCount = dataInput.readUnsignedShort();
+
+        for (int index = 0; index < u2attributesCount; index++)
+        {
+            skipAttribute();
+        }
+    }
+
+
+    private void skipAttribute()
+    {
+        dataInput.skipBytes(2);
+        int u4attributeLength = dataInput.readInt();
+        dataInput.skipBytes(u4attributeLength);
+    }
+}
diff --git a/src/proguard/classfile/io/ProgramClassReader.java b/src/proguard/classfile/io/ProgramClassReader.java
new file mode 100644
index 0000000..493208f
--- /dev/null
+++ b/src/proguard/classfile/io/ProgramClassReader.java
@@ -0,0 +1,850 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.io;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.io.DataInput;
+
+/**
+ * This ClassVisitor fills out the ProgramClass objects that it visits with data
+ * from the given DataInput object.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramClassReader
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor,
+             AttributeVisitor,
+             InnerClassesInfoVisitor,
+             ExceptionInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
+             LineNumberInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private final RuntimeDataInput dataInput;
+
+
+    /**
+     * Creates a new ProgramClassReader for reading from the given DataInput.
+     */
+    public ProgramClassReader(DataInput dataInput)
+    {
+        this.dataInput = new RuntimeDataInput(dataInput);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Read and check the magic number.
+        programClass.u4magic = dataInput.readInt();
+
+        ClassUtil.checkMagicNumber(programClass.u4magic);
+
+        // Read and check the version numbers.
+        int u2minorVersion = dataInput.readUnsignedShort();
+        int u2majorVersion = dataInput.readUnsignedShort();
+
+        programClass.u4version = ClassUtil.internalClassVersion(u2majorVersion,
+                                                                u2minorVersion);
+
+        ClassUtil.checkVersionNumbers(programClass.u4version);
+
+        // Read the constant pool. Note that the first entry is not used.
+        programClass.u2constantPoolCount = dataInput.readUnsignedShort();
+
+        programClass.constantPool = new Constant[programClass.u2constantPoolCount];
+        for (int index = 1; index < programClass.u2constantPoolCount; index++)
+        {
+            Constant constant = createConstant();
+            constant.accept(programClass, this);
+            programClass.constantPool[index] = constant;
+
+            // Long constants and double constants take up two entries in the
+            // constant pool.
+            int tag = constant.getTag();
+            if (tag == ClassConstants.CONSTANT_Long ||
+                tag == ClassConstants.CONSTANT_Double)
+            {
+                programClass.constantPool[++index] = null;
+            }
+        }
+
+        // Read the general class information.
+        programClass.u2accessFlags = dataInput.readUnsignedShort();
+        programClass.u2thisClass   = dataInput.readUnsignedShort();
+        programClass.u2superClass  = dataInput.readUnsignedShort();
+
+        // Read the interfaces.
+        programClass.u2interfacesCount = dataInput.readUnsignedShort();
+
+        programClass.u2interfaces = new int[programClass.u2interfacesCount];
+        for (int index = 0; index < programClass.u2interfacesCount; index++)
+        {
+            programClass.u2interfaces[index] = dataInput.readUnsignedShort();
+        }
+
+        // Read the fields.
+        programClass.u2fieldsCount = dataInput.readUnsignedShort();
+
+        programClass.fields = new ProgramField[programClass.u2fieldsCount];
+        for (int index = 0; index < programClass.u2fieldsCount; index++)
+        {
+            ProgramField programField = new ProgramField();
+            this.visitProgramField(programClass, programField);
+            programClass.fields[index] = programField;
+        }
+
+        // Read the methods.
+        programClass.u2methodsCount = dataInput.readUnsignedShort();
+
+        programClass.methods = new ProgramMethod[programClass.u2methodsCount];
+        for (int index = 0; index < programClass.u2methodsCount; index++)
+        {
+            ProgramMethod programMethod = new ProgramMethod();
+            this.visitProgramMethod(programClass, programMethod);
+            programClass.methods[index] = programMethod;
+        }
+
+        // Read the class attributes.
+        programClass.u2attributesCount = dataInput.readUnsignedShort();
+
+        programClass.attributes = new Attribute[programClass.u2attributesCount];
+        for (int index = 0; index < programClass.u2attributesCount; index++)
+        {
+            Attribute attribute = createAttribute(programClass);
+            attribute.accept(programClass, this);
+            programClass.attributes[index] = attribute;
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Read the general field information.
+        programField.u2accessFlags     = dataInput.readUnsignedShort();
+        programField.u2nameIndex       = dataInput.readUnsignedShort();
+        programField.u2descriptorIndex = dataInput.readUnsignedShort();
+
+        // Read the field attributes.
+        programField.u2attributesCount = dataInput.readUnsignedShort();
+
+        programField.attributes = new Attribute[programField.u2attributesCount];
+        for (int index = 0; index < programField.u2attributesCount; index++)
+        {
+            Attribute attribute = createAttribute(programClass);
+            attribute.accept(programClass, programField, this);
+            programField.attributes[index] = attribute;
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Read the general method information.
+        programMethod.u2accessFlags     = dataInput.readUnsignedShort();
+        programMethod.u2nameIndex       = dataInput.readUnsignedShort();
+        programMethod.u2descriptorIndex = dataInput.readUnsignedShort();
+
+        // Read the method attributes.
+        programMethod.u2attributesCount = dataInput.readUnsignedShort();
+
+        programMethod.attributes = new Attribute[programMethod.u2attributesCount];
+        for (int index = 0; index < programMethod.u2attributesCount; index++)
+        {
+            Attribute attribute = createAttribute(programClass);
+            attribute.accept(programClass, programMethod, this);
+            programMethod.attributes[index] = attribute;
+        }
+    }
+
+
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+    {
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        integerConstant.u4value = dataInput.readInt();
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        longConstant.u8value = dataInput.readLong();
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        floatConstant.f4value = dataInput.readFloat();
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        doubleConstant.f8value = dataInput.readDouble();
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        stringConstant.u2stringIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        int u2length = dataInput.readUnsignedShort();
+
+        // Read the UTF-8 bytes.
+        byte[] bytes = new byte[u2length];
+        dataInput.readFully(bytes);
+        utf8Constant.setBytes(bytes);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        refConstant.u2classIndex       = dataInput.readUnsignedShort();
+        refConstant.u2nameAndTypeIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        classConstant.u2nameIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        nameAndTypeConstant.u2nameIndex       = dataInput.readUnsignedShort();
+        nameAndTypeConstant.u2descriptorIndex = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        // Read the unknown information.
+        byte[] info = new byte[unknownAttribute.u4attributeLength];
+        dataInput.readFully(info);
+        unknownAttribute.info = info;
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        sourceFileAttribute.u2sourceFileIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        sourceDirAttribute.u2sourceDirIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        // Read the inner classes.
+        innerClassesAttribute.u2classesCount = dataInput.readUnsignedShort();
+
+        innerClassesAttribute.classes = new InnerClassesInfo[innerClassesAttribute.u2classesCount];
+        for (int index = 0; index < innerClassesAttribute.u2classesCount; index++)
+        {
+            InnerClassesInfo innerClassesInfo = new InnerClassesInfo();
+            this.visitInnerClassesInfo(clazz, innerClassesInfo);
+            innerClassesAttribute.classes[index] = innerClassesInfo;
+        }
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        enclosingMethodAttribute.u2classIndex       = dataInput.readUnsignedShort();
+        enclosingMethodAttribute.u2nameAndTypeIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        // This attribute does not contain any additional information.
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        // This attribute does not contain any additional information.
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        signatureAttribute.u2signatureIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        constantValueAttribute.u2constantValueIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        // Read the exceptions.
+        exceptionsAttribute.u2exceptionIndexTableLength = dataInput.readUnsignedShort();
+
+        exceptionsAttribute.u2exceptionIndexTable = new int[exceptionsAttribute.u2exceptionIndexTableLength];
+        for (int index = 0; index < exceptionsAttribute.u2exceptionIndexTableLength; index++)
+        {
+            exceptionsAttribute.u2exceptionIndexTable[index] = dataInput.readUnsignedShort();
+        }
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Read the stack size and local variable frame size.
+        codeAttribute.u2maxStack   = dataInput.readUnsignedShort();
+        codeAttribute.u2maxLocals  = dataInput.readUnsignedShort();
+
+        // Read the byte code.
+        codeAttribute.u4codeLength = dataInput.readInt();
+
+        byte[] code = new byte[codeAttribute.u4codeLength];
+        dataInput.readFully(code);
+        codeAttribute.code = code;
+
+        // Read the exceptions.
+        codeAttribute.u2exceptionTableLength = dataInput.readUnsignedShort();
+
+        codeAttribute.exceptionTable = new ExceptionInfo[codeAttribute.u2exceptionTableLength];
+        for (int index = 0; index < codeAttribute.u2exceptionTableLength; index++)
+        {
+            ExceptionInfo exceptionInfo = new ExceptionInfo();
+            this.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
+            codeAttribute.exceptionTable[index] = exceptionInfo;
+        }
+
+        // Read the code attributes.
+        codeAttribute.u2attributesCount = dataInput.readUnsignedShort();
+
+        codeAttribute.attributes = new Attribute[codeAttribute.u2attributesCount];
+        for (int index = 0; index < codeAttribute.u2attributesCount; index++)
+        {
+            Attribute attribute = createAttribute(clazz);
+            attribute.accept(clazz, method, codeAttribute, this);
+            codeAttribute.attributes[index] = attribute;
+        }
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        // Read the stack map frames (only full frames, without tag).
+        stackMapAttribute.u2stackMapFramesCount = dataInput.readUnsignedShort();
+
+        stackMapAttribute.stackMapFrames = new FullFrame[stackMapAttribute.u2stackMapFramesCount];
+        for (int index = 0; index < stackMapAttribute.u2stackMapFramesCount; index++)
+        {
+            FullFrame stackMapFrame = new FullFrame();
+            this.visitFullFrame(clazz, method, codeAttribute, index, stackMapFrame);
+            stackMapAttribute.stackMapFrames[index] = stackMapFrame;
+        }
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        // Read the stack map frames.
+        stackMapTableAttribute.u2stackMapFramesCount = dataInput.readUnsignedShort();
+
+        stackMapTableAttribute.stackMapFrames = new StackMapFrame[stackMapTableAttribute.u2stackMapFramesCount];
+        for (int index = 0; index < stackMapTableAttribute.u2stackMapFramesCount; index++)
+        {
+            StackMapFrame stackMapFrame = createStackMapFrame();
+            stackMapFrame.accept(clazz, method, codeAttribute, 0, this);
+            stackMapTableAttribute.stackMapFrames[index] = stackMapFrame;
+        }
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        // Read the line numbers.
+        lineNumberTableAttribute.u2lineNumberTableLength = dataInput.readUnsignedShort();
+
+        lineNumberTableAttribute.lineNumberTable = new LineNumberInfo[lineNumberTableAttribute.u2lineNumberTableLength];
+        for (int index = 0; index < lineNumberTableAttribute.u2lineNumberTableLength; index++)
+        {
+            LineNumberInfo lineNumberInfo = new LineNumberInfo();
+            this.visitLineNumberInfo(clazz, method, codeAttribute, lineNumberInfo);
+            lineNumberTableAttribute.lineNumberTable[index] = lineNumberInfo;
+        }
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Read the local variables.
+        localVariableTableAttribute.u2localVariableTableLength = dataInput.readUnsignedShort();
+
+        localVariableTableAttribute.localVariableTable = new LocalVariableInfo[localVariableTableAttribute.u2localVariableTableLength];
+        for (int index = 0; index < localVariableTableAttribute.u2localVariableTableLength; index++)
+        {
+            LocalVariableInfo localVariableInfo = new LocalVariableInfo();
+            this.visitLocalVariableInfo(clazz, method, codeAttribute, localVariableInfo);
+            localVariableTableAttribute.localVariableTable[index] = localVariableInfo;
+        }
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Read the local variable types.
+        localVariableTypeTableAttribute.u2localVariableTypeTableLength = dataInput.readUnsignedShort();
+
+        localVariableTypeTableAttribute.localVariableTypeTable = new LocalVariableTypeInfo[localVariableTypeTableAttribute.u2localVariableTypeTableLength];
+        for (int index = 0; index < localVariableTypeTableAttribute.u2localVariableTypeTableLength; index++)
+        {
+            LocalVariableTypeInfo localVariableTypeInfo = new LocalVariableTypeInfo();
+            this.visitLocalVariableTypeInfo(clazz, method, codeAttribute, localVariableTypeInfo);
+            localVariableTypeTableAttribute.localVariableTypeTable[index] = localVariableTypeInfo;
+        }
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Read the annotations.
+        annotationsAttribute.u2annotationsCount = dataInput.readUnsignedShort();
+
+        annotationsAttribute.annotations = new Annotation[annotationsAttribute.u2annotationsCount];
+        for (int index = 0; index < annotationsAttribute.u2annotationsCount; index++)
+        {
+            Annotation annotation = new Annotation();
+            this.visitAnnotation(clazz, annotation);
+            annotationsAttribute.annotations[index] = annotation;
+        }
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Read the parameter annotations.
+        parameterAnnotationsAttribute.u2parametersCount           = dataInput.readUnsignedByte();
+        parameterAnnotationsAttribute.u2parameterAnnotationsCount = new int[parameterAnnotationsAttribute.u2parametersCount];
+        parameterAnnotationsAttribute.parameterAnnotations        = new Annotation[parameterAnnotationsAttribute.u2parametersCount][];
+
+        for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++)
+        {
+            // Read the parameter annotations of the given parameter.
+            int u2annotationsCount = dataInput.readUnsignedShort();
+
+            Annotation[] annotations      = new Annotation[u2annotationsCount];
+
+            for (int index = 0; index < u2annotationsCount; index++)
+            {
+                Annotation annotation = new Annotation();
+                this.visitAnnotation(clazz, annotation);
+                annotations[index] = annotation;
+            }
+
+            parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] = u2annotationsCount;
+            parameterAnnotationsAttribute.parameterAnnotations[parameterIndex]        = annotations;
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // Read the default element value.
+        ElementValue elementValue = createElementValue();
+        elementValue.accept(clazz, null, this);
+        annotationDefaultAttribute.defaultValue = elementValue;
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+    {
+        innerClassesInfo.u2innerClassIndex       = dataInput.readUnsignedShort();
+        innerClassesInfo.u2outerClassIndex       = dataInput.readUnsignedShort();
+        innerClassesInfo.u2innerNameIndex        = dataInput.readUnsignedShort();
+        innerClassesInfo.u2innerClassAccessFlags = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        exceptionInfo.u2startPC   = dataInput.readUnsignedShort();
+        exceptionInfo.u2endPC     = dataInput.readUnsignedShort();
+        exceptionInfo.u2handlerPC = dataInput.readUnsignedShort();
+        exceptionInfo.u2catchType = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame)
+    {
+        if (sameZeroFrame.getTag() == StackMapFrame.SAME_ZERO_FRAME_EXTENDED)
+        {
+            sameZeroFrame.u2offsetDelta = dataInput.readUnsignedShort();
+        }
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        if (sameOneFrame.getTag() == StackMapFrame.SAME_ONE_FRAME_EXTENDED)
+        {
+            sameOneFrame.u2offsetDelta = dataInput.readUnsignedShort();
+        }
+
+        // Read the verification type of the stack entry.
+        VerificationType verificationType = createVerificationType();
+        verificationType.accept(clazz, method, codeAttribute, offset, this);
+        sameOneFrame.stackItem = verificationType;
+    }
+
+
+    public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame)
+    {
+        lessZeroFrame.u2offsetDelta = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        moreZeroFrame.u2offsetDelta = dataInput.readUnsignedShort();
+
+        // Read the verification types of the additional local variables.
+        moreZeroFrame.additionalVariables = new VerificationType[moreZeroFrame.additionalVariablesCount];
+        for (int index = 0; index < moreZeroFrame.additionalVariablesCount; index++)
+        {
+            VerificationType verificationType = createVerificationType();
+            verificationType.accept(clazz, method, codeAttribute, offset, this);
+            moreZeroFrame.additionalVariables[index] = verificationType;
+        }
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        fullFrame.u2offsetDelta = dataInput.readUnsignedShort();
+
+        // Read the verification types of the local variables.
+        fullFrame.variablesCount = dataInput.readUnsignedShort();
+        fullFrame.variables = new VerificationType[fullFrame.variablesCount];
+        for (int index = 0; index < fullFrame.variablesCount; index++)
+        {
+            VerificationType verificationType = createVerificationType();
+            verificationType.variablesAccept(clazz, method, codeAttribute, offset, index, this);
+            fullFrame.variables[index] = verificationType;
+        }
+
+        // Read the verification types of the stack entries.
+        fullFrame.stackCount = dataInput.readUnsignedShort();
+        fullFrame.stack = new VerificationType[fullFrame.stackCount];
+        for (int index = 0; index < fullFrame.stackCount; index++)
+        {
+            VerificationType verificationType = createVerificationType();
+            verificationType.stackAccept(clazz, method, codeAttribute, offset, index, this);
+            fullFrame.stack[index] = verificationType;
+        }
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType)
+    {
+        // Most verification types don't contain any additional information.
+    }
+
+
+    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
+    {
+        objectType.u2classIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+    {
+        uninitializedType.u2newInstructionOffset = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
+    {
+        lineNumberInfo.u2startPC    = dataInput.readUnsignedShort();
+        lineNumberInfo.u2lineNumber = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        localVariableInfo.u2startPC         = dataInput.readUnsignedShort();
+        localVariableInfo.u2length          = dataInput.readUnsignedShort();
+        localVariableInfo.u2nameIndex       = dataInput.readUnsignedShort();
+        localVariableInfo.u2descriptorIndex = dataInput.readUnsignedShort();
+        localVariableInfo.u2index           = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        localVariableTypeInfo.u2startPC        = dataInput.readUnsignedShort();
+        localVariableTypeInfo.u2length         = dataInput.readUnsignedShort();
+        localVariableTypeInfo.u2nameIndex      = dataInput.readUnsignedShort();
+        localVariableTypeInfo.u2signatureIndex = dataInput.readUnsignedShort();
+        localVariableTypeInfo.u2index          = dataInput.readUnsignedShort();
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        // Read the annotation type.
+        annotation.u2typeIndex = dataInput.readUnsignedShort();
+
+        // Read the element value pairs.
+        annotation.u2elementValuesCount = dataInput.readUnsignedShort();
+
+        annotation.elementValues = new ElementValue[annotation.u2elementValuesCount];
+        for (int index = 0; index < annotation.u2elementValuesCount; index++)
+        {
+            int u2elementNameIndex = dataInput.readUnsignedShort();
+            ElementValue elementValue = createElementValue();
+            elementValue.u2elementNameIndex = u2elementNameIndex;
+            elementValue.accept(clazz, annotation, this);
+            annotation.elementValues[index] = elementValue;
+        }
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        constantElementValue.u2constantValueIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        enumConstantElementValue.u2typeNameIndex     = dataInput.readUnsignedShort();
+        enumConstantElementValue.u2constantNameIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        classElementValue.u2classInfoIndex = dataInput.readUnsignedShort();
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        // Read the annotation.
+        Annotation annotationValue = new Annotation();
+        this.visitAnnotation(clazz, annotationValue);
+        annotationElementValue.annotationValue = annotationValue;
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        // Read the element values.
+        arrayElementValue.u2elementValuesCount = dataInput.readUnsignedShort();
+
+        arrayElementValue.elementValues = new ElementValue[arrayElementValue.u2elementValuesCount];
+        for (int index = 0; index < arrayElementValue.u2elementValuesCount; index++)
+        {
+            ElementValue elementValue = createElementValue();
+            elementValue.accept(clazz, annotation, this);
+            arrayElementValue.elementValues[index] = elementValue;
+        }
+    }
+
+
+    // Small utility methods.
+
+    private Constant createConstant()
+    {
+        int u1tag = dataInput.readUnsignedByte();
+
+        switch (u1tag)
+        {
+            case ClassConstants.CONSTANT_Utf8:               return new Utf8Constant();
+            case ClassConstants.CONSTANT_Integer:            return new IntegerConstant();
+            case ClassConstants.CONSTANT_Float:              return new FloatConstant();
+            case ClassConstants.CONSTANT_Long:               return new LongConstant();
+            case ClassConstants.CONSTANT_Double:             return new DoubleConstant();
+            case ClassConstants.CONSTANT_String:             return new StringConstant();
+            case ClassConstants.CONSTANT_Fieldref:           return new FieldrefConstant();
+            case ClassConstants.CONSTANT_Methodref:          return new MethodrefConstant();
+            case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant();
+            case ClassConstants.CONSTANT_Class:              return new ClassConstant();
+            case ClassConstants.CONSTANT_NameAndType:        return new NameAndTypeConstant();
+
+            default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool");
+        }
+    }
+
+
+    private Attribute createAttribute(Clazz clazz)
+    {
+        int u2attributeNameIndex = dataInput.readUnsignedShort();
+        int u4attributeLength    = dataInput.readInt();
+        String attributeName     = clazz.getString(u2attributeNameIndex);
+        Attribute attribute =
+            attributeName.equals(ClassConstants.ATTR_SourceFile)                           ? (Attribute)new SourceFileAttribute():
+            attributeName.equals(ClassConstants.ATTR_SourceDir)                            ? (Attribute)new SourceDirAttribute():
+            attributeName.equals(ClassConstants.ATTR_InnerClasses)                         ? (Attribute)new InnerClassesAttribute():
+            attributeName.equals(ClassConstants.ATTR_EnclosingMethod)                      ? (Attribute)new EnclosingMethodAttribute():
+            attributeName.equals(ClassConstants.ATTR_Deprecated)                           ? (Attribute)new DeprecatedAttribute():
+            attributeName.equals(ClassConstants.ATTR_Synthetic)                            ? (Attribute)new SyntheticAttribute():
+            attributeName.equals(ClassConstants.ATTR_Signature)                            ? (Attribute)new SignatureAttribute():
+            attributeName.equals(ClassConstants.ATTR_ConstantValue)                        ? (Attribute)new ConstantValueAttribute():
+            attributeName.equals(ClassConstants.ATTR_Exceptions)                           ? (Attribute)new ExceptionsAttribute():
+            attributeName.equals(ClassConstants.ATTR_Code)                                 ? (Attribute)new CodeAttribute():
+            attributeName.equals(ClassConstants.ATTR_StackMap)                             ? (Attribute)new StackMapAttribute():
+            attributeName.equals(ClassConstants.ATTR_StackMapTable)                        ? (Attribute)new StackMapTableAttribute():
+            attributeName.equals(ClassConstants.ATTR_LineNumberTable)                      ? (Attribute)new LineNumberTableAttribute():
+            attributeName.equals(ClassConstants.ATTR_LocalVariableTable)                   ? (Attribute)new LocalVariableTableAttribute():
+            attributeName.equals(ClassConstants.ATTR_LocalVariableTypeTable)               ? (Attribute)new LocalVariableTypeTableAttribute():
+            attributeName.equals(ClassConstants.ATTR_RuntimeVisibleAnnotations)            ? (Attribute)new RuntimeVisibleAnnotationsAttribute():
+            attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleAnnotations)          ? (Attribute)new RuntimeInvisibleAnnotationsAttribute():
+            attributeName.equals(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations)   ? (Attribute)new RuntimeVisibleParameterAnnotationsAttribute():
+            attributeName.equals(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations) ? (Attribute)new RuntimeInvisibleParameterAnnotationsAttribute():
+            attributeName.equals(ClassConstants.ATTR_AnnotationDefault)                    ? (Attribute)new AnnotationDefaultAttribute():
+                                                                                             (Attribute)new UnknownAttribute(u4attributeLength);
+        attribute.u2attributeNameIndex = u2attributeNameIndex;
+
+        return attribute;
+    }
+
+
+    private StackMapFrame createStackMapFrame()
+    {
+        int u1tag = dataInput.readUnsignedByte();
+
+        return
+            u1tag < StackMapFrame.SAME_ONE_FRAME           ? (StackMapFrame)new SameZeroFrame(u1tag) :
+            u1tag < StackMapFrame.SAME_ONE_FRAME_EXTENDED  ? (StackMapFrame)new SameOneFrame(u1tag)  :
+            u1tag < StackMapFrame.LESS_ZERO_FRAME          ? (StackMapFrame)new SameOneFrame(u1tag)  :
+            u1tag < StackMapFrame.SAME_ZERO_FRAME_EXTENDED ? (StackMapFrame)new LessZeroFrame(u1tag) :
+            u1tag < StackMapFrame.MORE_ZERO_FRAME          ? (StackMapFrame)new SameZeroFrame(u1tag) :
+            u1tag < StackMapFrame.FULL_FRAME               ? (StackMapFrame)new MoreZeroFrame(u1tag) :
+                                                             (StackMapFrame)new FullFrame();
+    }
+
+
+    private VerificationType createVerificationType()
+    {
+        int u1tag = dataInput.readUnsignedByte();
+
+        switch (u1tag)
+        {
+            case VerificationType.INTEGER_TYPE:            return new IntegerType();
+            case VerificationType.FLOAT_TYPE:              return new FloatType();
+            case VerificationType.LONG_TYPE:               return new LongType();
+            case VerificationType.DOUBLE_TYPE:             return new DoubleType();
+            case VerificationType.TOP_TYPE:                return new TopType();
+            case VerificationType.OBJECT_TYPE:             return new ObjectType();
+            case VerificationType.NULL_TYPE:               return new NullType();
+            case VerificationType.UNINITIALIZED_TYPE:      return new UninitializedType();
+            case VerificationType.UNINITIALIZED_THIS_TYPE: return new UninitializedThisType();
+
+            default: throw new RuntimeException("Unknown verification type ["+u1tag+"] in stack map frame");
+        }
+    }
+
+
+    private ElementValue createElementValue()
+    {
+        int u1tag = dataInput.readUnsignedByte();
+
+        switch (u1tag)
+        {
+            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+            case ClassConstants.INTERNAL_TYPE_BYTE:
+            case ClassConstants.INTERNAL_TYPE_CHAR:
+            case ClassConstants.INTERNAL_TYPE_SHORT:
+            case ClassConstants.INTERNAL_TYPE_INT:
+            case ClassConstants.INTERNAL_TYPE_FLOAT:
+            case ClassConstants.INTERNAL_TYPE_LONG:
+            case ClassConstants.INTERNAL_TYPE_DOUBLE:
+            case ClassConstants.ELEMENT_VALUE_STRING_CONSTANT: return new ConstantElementValue(u1tag);
+
+            case ClassConstants.ELEMENT_VALUE_ENUM_CONSTANT:   return new EnumConstantElementValue();
+            case ClassConstants.ELEMENT_VALUE_CLASS:           return new ClassElementValue();
+            case ClassConstants.ELEMENT_VALUE_ANNOTATION:      return new AnnotationElementValue();
+            case ClassConstants.ELEMENT_VALUE_ARRAY:           return new ArrayElementValue();
+
+            default: throw new IllegalArgumentException("Unknown element value tag ["+u1tag+"]");
+        }
+    }
+}
diff --git a/src/proguard/classfile/io/ProgramClassWriter.java b/src/proguard/classfile/io/ProgramClassWriter.java
new file mode 100644
index 0000000..ac776c5
--- /dev/null
+++ b/src/proguard/classfile/io/ProgramClassWriter.java
@@ -0,0 +1,692 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.io;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+
+import java.io.*;
+
+/**
+ * This ClassVisitor writes out the ProgramClass objects that it visits to the
+ * given DataOutput object.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramClassWriter
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor,
+             AttributeVisitor
+{
+    private RuntimeDataOutput dataOutput;
+
+    private final ConstantBodyWriter         constantBodyWriter         = new ConstantBodyWriter();
+    private final AttributeBodyWriter        attributeBodyWriter        = new AttributeBodyWriter();
+    private final StackMapFrameBodyWriter    stackMapFrameBodyWriter    = new StackMapFrameBodyWriter();
+    private final VerificationTypeBodyWriter verificationTypeBodyWriter = new VerificationTypeBodyWriter();
+    private final ElementValueBodyWriter     elementValueBodyWriter     = new ElementValueBodyWriter();
+
+
+    /**
+     * Creates a new ProgramClassWriter for reading from the given DataOutput.
+     */
+    public ProgramClassWriter(DataOutput dataOutput)
+    {
+        this.dataOutput = new RuntimeDataOutput(dataOutput);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Write the magic number.
+        dataOutput.writeInt(programClass.u4magic);
+
+        // Write the version numbers.
+        dataOutput.writeShort(ClassUtil.internalMinorClassVersion(programClass.u4version));
+        dataOutput.writeShort(ClassUtil.internalMajorClassVersion(programClass.u4version));
+
+        // Write the constant pool.
+        dataOutput.writeShort(programClass.u2constantPoolCount);
+
+        programClass.constantPoolEntriesAccept(this);
+
+        // Write the general class information.
+        dataOutput.writeShort(programClass.u2accessFlags);
+        dataOutput.writeShort(programClass.u2thisClass);
+        dataOutput.writeShort(programClass.u2superClass);
+
+        // Write the interfaces.
+        dataOutput.writeShort(programClass.u2interfacesCount);
+
+        for (int index = 0; index < programClass.u2interfacesCount; index++)
+        {
+            dataOutput.writeShort(programClass.u2interfaces[index]);
+        }
+
+        // Write the fields.
+        dataOutput.writeShort(programClass.u2fieldsCount);
+
+        programClass.fieldsAccept(this);
+
+        // Write the methods.
+        dataOutput.writeShort(programClass.u2methodsCount);
+
+        programClass.methodsAccept(this);
+
+        // Write the class attributes.
+        dataOutput.writeShort(programClass.u2attributesCount);
+
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Write the general field information.
+        dataOutput.writeShort(programField.u2accessFlags);
+        dataOutput.writeShort(programField.u2nameIndex);
+        dataOutput.writeShort(programField.u2descriptorIndex);
+
+        // Write the field attributes.
+        dataOutput.writeShort(programField.u2attributesCount);
+
+        programField.attributesAccept(programClass, this);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Write the general method information.
+        dataOutput.writeShort(programMethod.u2accessFlags);
+        dataOutput.writeShort(programMethod.u2nameIndex);
+        dataOutput.writeShort(programMethod.u2descriptorIndex);
+
+        // Write the method attributes.
+        dataOutput.writeShort(programMethod.u2attributesCount);
+
+        programMethod.attributesAccept(programClass, this);
+    }
+
+
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+    {
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant)
+    {
+        // Write the tag.
+        dataOutput.writeByte(constant.getTag());
+
+        // Write the actual body.
+        constant.accept(clazz, constantBodyWriter);
+    }
+
+
+    private class ConstantBodyWriter
+    extends       SimplifiedVisitor
+    implements    ConstantVisitor
+    {
+        // Implementations for ConstantVisitor.
+
+        public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+        {
+            dataOutput.writeInt(integerConstant.u4value);
+        }
+
+
+        public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+        {
+            dataOutput.writeLong(longConstant.u8value);
+        }
+
+
+        public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+        {
+            dataOutput.writeFloat(floatConstant.f4value);
+        }
+
+
+        public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+        {
+            dataOutput.writeDouble(doubleConstant.f8value);
+        }
+
+
+        public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+        {
+            dataOutput.writeShort(stringConstant.u2stringIndex);
+        }
+
+
+        public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+        {
+            byte[] bytes = utf8Constant.getBytes();
+
+            dataOutput.writeShort(bytes.length);
+            dataOutput.write(bytes);
+        }
+
+
+        public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+        {
+            dataOutput.writeShort(refConstant.u2classIndex);
+            dataOutput.writeShort(refConstant.u2nameAndTypeIndex);
+        }
+
+
+        public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+        {
+            dataOutput.writeShort(classConstant.u2nameIndex);
+        }
+
+
+        public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+        {
+            dataOutput.writeShort(nameAndTypeConstant.u2nameIndex);
+            dataOutput.writeShort(nameAndTypeConstant.u2descriptorIndex);
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
+    {
+        // Write the attribute name index.
+        dataOutput.writeShort(attribute.u2attributeNameIndex);
+
+        // We'll write the attribute body into an array first, so we can
+        // automatically figure out its length.
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+        // Temporarily replace the current data output.
+        RuntimeDataOutput oldDataOutput = dataOutput;
+        dataOutput = new RuntimeDataOutput(new DataOutputStream(byteArrayOutputStream));
+
+        // Write the attribute body into the array. Note that the
+        // accept method with two dummy null arguments never throws
+        // an UnsupportedOperationException.
+        attribute.accept(clazz, null, null, attributeBodyWriter);
+
+        // Restore the original data output.
+        dataOutput = oldDataOutput;
+
+        // Write the attribute length and body.
+        byte[] info = byteArrayOutputStream.toByteArray();
+
+        dataOutput.writeInt(info.length);
+        dataOutput.write(info);
+    }
+
+
+    private class AttributeBodyWriter
+    extends       SimplifiedVisitor
+    implements    AttributeVisitor,
+                  InnerClassesInfoVisitor,
+                  ExceptionInfoVisitor,
+                  StackMapFrameVisitor,
+                  VerificationTypeVisitor,
+                  LineNumberInfoVisitor,
+                  LocalVariableInfoVisitor,
+                  LocalVariableTypeInfoVisitor,
+                  AnnotationVisitor,
+                  ElementValueVisitor
+    {
+        // Implementations for AttributeVisitor.
+
+        public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+        {
+            // Write the unknown information.
+            byte[] info = new byte[unknownAttribute.u4attributeLength];
+            dataOutput.write(info);
+            unknownAttribute.info = info;
+        }
+
+
+        public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+        {
+            dataOutput.writeShort(sourceFileAttribute.u2sourceFileIndex);
+        }
+
+
+        public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+        {
+            dataOutput.writeShort(sourceDirAttribute.u2sourceDirIndex);
+        }
+
+
+        public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+        {
+            // Write the inner classes.
+            dataOutput.writeShort(innerClassesAttribute.u2classesCount);
+
+            innerClassesAttribute.innerClassEntriesAccept(clazz, this);
+        }
+
+
+        public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+        {
+            dataOutput.writeShort(enclosingMethodAttribute.u2classIndex);
+            dataOutput.writeShort(enclosingMethodAttribute.u2nameAndTypeIndex);
+        }
+
+
+        public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+        {
+            // This attribute does not contain any additional information.
+        }
+
+
+        public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+        {
+            // This attribute does not contain any additional information.
+        }
+
+
+        public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+        {
+            dataOutput.writeShort(signatureAttribute.u2signatureIndex);
+        }
+
+
+        public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+        {
+            dataOutput.writeShort(constantValueAttribute.u2constantValueIndex);
+        }
+
+
+        public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+        {
+            // Write the exceptions.
+            dataOutput.writeShort(exceptionsAttribute.u2exceptionIndexTableLength);
+
+            for (int index = 0; index < exceptionsAttribute.u2exceptionIndexTableLength; index++)
+            {
+                dataOutput.writeShort(exceptionsAttribute.u2exceptionIndexTable[index]);
+            }
+        }
+
+
+        public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+        {
+            // Write the stack size and local variable frame size.
+            dataOutput.writeShort(codeAttribute.u2maxStack);
+            dataOutput.writeShort(codeAttribute.u2maxLocals);
+
+            // Write the byte code.
+            dataOutput.writeInt(codeAttribute.u4codeLength);
+
+            dataOutput.write(codeAttribute.code, 0, codeAttribute.u4codeLength);
+
+            // Write the exceptions.
+            dataOutput.writeShort(codeAttribute.u2exceptionTableLength);
+
+            codeAttribute.exceptionsAccept(clazz, method, this);
+
+            // Write the code attributes.
+            dataOutput.writeShort(codeAttribute.u2attributesCount);
+
+            codeAttribute.attributesAccept(clazz, method, ProgramClassWriter.this);
+        }
+
+
+        public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+        {
+            // Write the stack map frames (only full frames, without tag).
+            dataOutput.writeShort(stackMapAttribute.u2stackMapFramesCount);
+
+            stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, stackMapFrameBodyWriter);
+        }
+
+
+        public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+        {
+            // Write the stack map frames.
+            dataOutput.writeShort(stackMapTableAttribute.u2stackMapFramesCount);
+
+            stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+        }
+
+
+        public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+        {
+            // Write the line numbers.
+            dataOutput.writeShort(lineNumberTableAttribute.u2lineNumberTableLength);
+
+            lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
+        }
+
+
+        public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+        {
+            // Write the local variables.
+            dataOutput.writeShort(localVariableTableAttribute.u2localVariableTableLength);
+
+            localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+        }
+
+
+        public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+        {
+            // Write the local variable types.
+            dataOutput.writeShort(localVariableTypeTableAttribute.u2localVariableTypeTableLength);
+
+            localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+        }
+
+
+        public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+        {
+            // Write the annotations.
+            dataOutput.writeShort(annotationsAttribute.u2annotationsCount);
+
+            annotationsAttribute.annotationsAccept(clazz, this);
+        }
+
+
+        public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+        {
+            // Write the parameter annotations.
+            dataOutput.writeByte(parameterAnnotationsAttribute.u2parametersCount);
+
+            for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++)
+            {
+                // Write the parameter annotations of the given parameter.
+                int          u2annotationsCount = parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex];
+                Annotation[] annotations        = parameterAnnotationsAttribute.parameterAnnotations[parameterIndex];
+
+                dataOutput.writeShort(u2annotationsCount);
+
+                for (int index = 0; index < u2annotationsCount; index++)
+                {
+                    Annotation annotation = annotations[index];
+                    this.visitAnnotation(clazz, annotation);
+                }
+
+            }
+        }
+
+
+        public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+        {
+            // Write the default element value.
+            annotationDefaultAttribute.defaultValue.accept(clazz, null, this);
+        }
+
+
+        // Implementations for InnerClassesInfoVisitor.
+
+        public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+        {
+            dataOutput.writeShort(innerClassesInfo.u2innerClassIndex);
+            dataOutput.writeShort(innerClassesInfo.u2outerClassIndex);
+            dataOutput.writeShort(innerClassesInfo.u2innerNameIndex);
+            dataOutput.writeShort(innerClassesInfo.u2innerClassAccessFlags);
+        }
+
+
+        // Implementations for ExceptionInfoVisitor.
+
+        public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+        {
+            dataOutput.writeShort(exceptionInfo.u2startPC);
+            dataOutput.writeShort(exceptionInfo.u2endPC);
+            dataOutput.writeShort(exceptionInfo.u2handlerPC);
+            dataOutput.writeShort(exceptionInfo.u2catchType);
+        }
+
+
+        // Implementations for StackMapFrameVisitor.
+
+        public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
+        {
+            // Write the stack map frame tag.
+            dataOutput.writeByte(stackMapFrame.getTag());
+
+            // Write the actual body.
+            stackMapFrame.accept(clazz, method, codeAttribute, offset, stackMapFrameBodyWriter);
+        }
+
+
+        // Implementations for LineNumberInfoVisitor.
+
+        public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
+        {
+            dataOutput.writeShort(lineNumberInfo.u2startPC);
+            dataOutput.writeShort(lineNumberInfo.u2lineNumber);
+        }
+
+
+        // Implementations for LocalVariableInfoVisitor.
+
+        public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+        {
+            dataOutput.writeShort(localVariableInfo.u2startPC);
+            dataOutput.writeShort(localVariableInfo.u2length);
+            dataOutput.writeShort(localVariableInfo.u2nameIndex);
+            dataOutput.writeShort(localVariableInfo.u2descriptorIndex);
+            dataOutput.writeShort(localVariableInfo.u2index);
+        }
+
+
+        // Implementations for LocalVariableTypeInfoVisitor.
+
+        public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+        {
+            dataOutput.writeShort(localVariableTypeInfo.u2startPC);
+            dataOutput.writeShort(localVariableTypeInfo.u2length);
+            dataOutput.writeShort(localVariableTypeInfo.u2nameIndex);
+            dataOutput.writeShort(localVariableTypeInfo.u2signatureIndex);
+            dataOutput.writeShort(localVariableTypeInfo.u2index);
+        }
+
+
+        // Implementations for AnnotationVisitor.
+
+        public void visitAnnotation(Clazz clazz, Annotation annotation)
+        {
+            // Write the annotation type.
+            dataOutput.writeShort(annotation.u2typeIndex);
+
+            // Write the element value pairs.
+            dataOutput.writeShort(annotation.u2elementValuesCount);
+
+            annotation.elementValuesAccept(clazz, this);
+        }
+
+
+        // Implementations for ElementValueVisitor.
+
+        public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+        {
+            // Write the element name index, if applicable.
+            int u2elementNameIndex = elementValue.u2elementNameIndex;
+            if (u2elementNameIndex != 0)
+            {
+                dataOutput.writeShort(u2elementNameIndex);
+            }
+
+            // Write the tag.
+            dataOutput.writeByte(elementValue.getTag());
+
+            // Write the actual body.
+            elementValue.accept(clazz, annotation, elementValueBodyWriter);
+        }
+    }
+
+
+    private class StackMapFrameBodyWriter
+    extends       SimplifiedVisitor
+    implements    StackMapFrameVisitor,
+                  VerificationTypeVisitor
+    {
+        public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame)
+        {
+            if (sameZeroFrame.getTag() == StackMapFrame.SAME_ZERO_FRAME_EXTENDED)
+            {
+                dataOutput.writeShort(sameZeroFrame.u2offsetDelta);
+            }
+        }
+
+
+        public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+        {
+            if (sameOneFrame.getTag() == StackMapFrame.SAME_ONE_FRAME_EXTENDED)
+            {
+                dataOutput.writeShort(sameOneFrame.u2offsetDelta);
+            }
+
+            // Write the verification type of the stack entry.
+            sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+        }
+
+
+        public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame)
+        {
+            dataOutput.writeShort(lessZeroFrame.u2offsetDelta);
+        }
+
+
+        public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+        {
+            dataOutput.writeShort(moreZeroFrame.u2offsetDelta);
+
+            // Write the verification types of the additional local variables.
+            moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+        }
+
+
+        public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+        {
+            dataOutput.writeShort(fullFrame.u2offsetDelta);
+
+            // Write the verification types of the local variables.
+            dataOutput.writeShort(fullFrame.variablesCount);
+            fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+
+            // Write the verification types of the stack entries.
+            dataOutput.writeShort(fullFrame.stackCount);
+            fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+        }
+
+
+        // Implementations for VerificationTypeVisitor.
+
+        public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType)
+        {
+            // Write the verification type tag.
+            dataOutput.writeByte(verificationType.getTag());
+
+            // Write the actual body.
+            verificationType.accept(clazz, method, codeAttribute, offset, verificationTypeBodyWriter);
+        }
+    }
+
+
+    private class VerificationTypeBodyWriter
+    extends       SimplifiedVisitor
+    implements    VerificationTypeVisitor
+    {
+        // Implementations for VerificationTypeVisitor.
+
+        public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType)
+        {
+            // Most verification types don't contain any additional information.
+        }
+
+
+        public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
+        {
+            dataOutput.writeShort(objectType.u2classIndex);
+        }
+
+
+        public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+        {
+            dataOutput.writeShort(uninitializedType.u2newInstructionOffset);
+        }
+    }
+
+
+    private class ElementValueBodyWriter
+    extends       SimplifiedVisitor
+    implements    ElementValueVisitor
+    {
+        // Implementations for ElementValueVisitor.
+
+        public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+        {
+            dataOutput.writeShort(constantElementValue.u2constantValueIndex);
+        }
+
+
+        public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+        {
+            dataOutput.writeShort(enumConstantElementValue.u2typeNameIndex);
+            dataOutput.writeShort(enumConstantElementValue.u2constantNameIndex);
+        }
+
+
+        public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+        {
+            dataOutput.writeShort(classElementValue.u2classInfoIndex);
+        }
+
+
+        public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+        {
+            // Write the annotation.
+            attributeBodyWriter.visitAnnotation(clazz, annotationElementValue.annotationValue);
+        }
+
+
+        public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+        {
+            // Write the element values.
+            dataOutput.writeShort(arrayElementValue.u2elementValuesCount);
+
+            arrayElementValue.elementValuesAccept(clazz, annotation, attributeBodyWriter);
+        }
+    }
+}
diff --git a/src/proguard/classfile/io/RuntimeDataInput.java b/src/proguard/classfile/io/RuntimeDataInput.java
new file mode 100644
index 0000000..94d11a3
--- /dev/null
+++ b/src/proguard/classfile/io/RuntimeDataInput.java
@@ -0,0 +1,223 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.io;
+
+import java.io.*;
+
+/**
+ * This class delegates its method calls to the corresponding DataInput methods,
+ * converting its IOExceptions to RuntimeExceptions.
+ *
+ * @author Eric Lafortune
+ */
+final class RuntimeDataInput
+{
+    private final DataInput dataInput;
+
+
+    public RuntimeDataInput(DataInput dataInput)
+    {
+        this.dataInput = dataInput;
+    }
+
+
+    // Methods delegating to DataInput.
+
+    public boolean readBoolean()
+    {
+        try
+        {
+            return dataInput.readBoolean();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public byte readByte()
+    {
+        try
+        {
+            return dataInput.readByte();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public char readChar()
+    {
+        try
+        {
+            return dataInput.readChar();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public double readDouble()
+    {
+        try
+        {
+            return dataInput.readDouble();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public float readFloat()
+    {
+        try
+        {
+            return dataInput.readFloat();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public void readFully(byte[] b)
+    {
+        try
+        {
+            dataInput.readFully(b);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public void readFully(byte[] b, int off, int len)
+    {
+        try
+        {
+            dataInput.readFully(b, off, len);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public int readInt()
+    {
+        try
+        {
+            return dataInput.readInt();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public String readLine()
+    {
+        try
+        {
+            return dataInput.readLine();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public long readLong()
+    {
+        try
+        {
+            return dataInput.readLong();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public short readShort()
+    {
+        try
+        {
+            return dataInput.readShort();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public int readUnsignedByte()
+    {
+        try
+        {
+            return dataInput.readUnsignedByte();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public int readUnsignedShort()
+    {
+        try
+        {
+            return dataInput.readUnsignedShort();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public String readUTF()
+    {
+        try
+        {
+            return dataInput.readUTF();
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    public int skipBytes(int n)
+    {
+        try
+        {
+            return dataInput.skipBytes(n);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+}
diff --git a/src/proguard/classfile/io/RuntimeDataOutput.java b/src/proguard/classfile/io/RuntimeDataOutput.java
new file mode 100644
index 0000000..51684b7
--- /dev/null
+++ b/src/proguard/classfile/io/RuntimeDataOutput.java
@@ -0,0 +1,224 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.io;
+
+import java.io.*;
+
+/**
+ * This class delegates its method calls to the corresponding DataOutput methods,
+ * converting its IOExceptions to RuntimeExceptions.
+ *
+ * @author Eric Lafortune
+ */
+final class RuntimeDataOutput
+{
+    private final DataOutput dataOutput;
+
+
+    public RuntimeDataOutput(DataOutput dataOutput)
+    {
+        this.dataOutput = dataOutput;
+    }
+
+
+    // Methods delegating to DataOutput.
+
+    public void write(byte[] b)
+    {
+        try
+        {
+            dataOutput.write(b);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void write(byte[] b, int off, int len)
+    {
+        try
+        {
+            dataOutput.write(b, off, len);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void write(int b)
+    {
+        try
+        {
+            dataOutput.write(b);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeBoolean(boolean v)
+    {
+        try
+        {
+            dataOutput.writeBoolean(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeByte(int v)
+    {
+        try
+        {
+            dataOutput.writeByte(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeBytes(String s)
+    {
+        try
+        {
+            dataOutput.writeBytes(s);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeChar(int v)
+    {
+        try
+        {
+            dataOutput.writeChar(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeChars(String s)
+    {
+        try
+        {
+            dataOutput.writeChars(s);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeDouble(double v)
+    {
+        try
+        {
+            dataOutput.writeDouble(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeFloat(float v)
+    {
+        try
+        {
+            dataOutput.writeFloat(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeInt(int v)
+    {
+        try
+        {
+            dataOutput.writeInt(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeLong(long v)
+    {
+        try
+        {
+            dataOutput.writeLong(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeShort(int v)
+    {
+        try
+        {
+            dataOutput.writeShort(v);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+
+    public void writeUTF(String str)
+    {
+        try
+        {
+            dataOutput.writeUTF(str);
+        }
+        catch (IOException ex)
+        {
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+}
diff --git a/src/proguard/classfile/io/package.html b/src/proguard/classfile/io/package.html
new file mode 100644
index 0000000..780b917
--- /dev/null
+++ b/src/proguard/classfile/io/package.html
@@ -0,0 +1,3 @@
+<body>
+This package contains classes for reading and writing class files.
+</body>
diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java
index 353bfed..621ecc2 100644
--- a/src/proguard/classfile/util/AccessUtil.java
+++ b/src/proguard/classfile/util/AccessUtil.java
@@ -1,6 +1,6 @@
-/* $Id: AccessUtil.java,v 1.6.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 
 /**
@@ -78,10 +78,10 @@ public class AccessUtil
     {
         switch (accessLevel)
         {
-            case PRIVATE:         return ClassConstants.INTERNAL_ACC_PRIVATE;
-            default:              return 0;
-            case PROTECTED:       return ClassConstants.INTERNAL_ACC_PROTECTED;
-            case PUBLIC:          return ClassConstants.INTERNAL_ACC_PUBLIC;
+            case PRIVATE:   return ClassConstants.INTERNAL_ACC_PRIVATE;
+            default:        return 0;
+            case PROTECTED: return ClassConstants.INTERNAL_ACC_PROTECTED;
+            case PUBLIC:    return ClassConstants.INTERNAL_ACC_PUBLIC;
         }
     }
 
@@ -93,6 +93,7 @@ public class AccessUtil
      */
     public static int replaceAccessFlags(int accessFlags, int newAccessFlags)
     {
+        // A private class member should not be explicitly final.
         if (newAccessFlags == ClassConstants.INTERNAL_ACC_PRIVATE)
         {
             accessFlags &= ~ClassConstants.INTERNAL_ACC_FINAL;
diff --git a/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java b/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
deleted file mode 100644
index 12a047c..0000000
--- a/src/proguard/classfile/util/ClassFileClassForNameReferenceInitializer.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/* $Id: ClassFileClassForNameReferenceInitializer.java,v 1.17.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.CpInfoVisitor;
-import proguard.util.ClassNameListMatcher;
-
-
-/**
- * This InstructionVisitor initializes any special <code>Class.forName</code> or
- * <code>.class</code> references of all class files it visits. More specifically,
- * it fills out the references of String constant pool entries that refer to a
- * class file in the program class pool.
- * <p>
- * It optionally prints notes if on usage of
- * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
- * <p>
- * The class file hierarchy must be initialized before using this visitor.
- *
- * @see ClassFileReferenceInitializer
- *
- * @author Eric Lafortune
- */
-public class ClassFileClassForNameReferenceInitializer
-  implements InstructionVisitor,
-             CpInfoVisitor
-{
-    private ClassPool            programClassPool;
-    private ClassPool            libraryClassPool;
-    private WarningPrinter       notePrinter;
-    private ClassNameListMatcher noteExceptionMatcher;
-
-    // Fields to remember the previous StringCpInfo and MethodRefCpInfo objects
-    // while visiting all instructions (to find Class.forName, class$, and
-    // Class.newInstance invocations, and possible class casts afterwards).
-    private int ldcStringCpIndex              = -1;
-    private int invokestaticMethodRefCpIndex  = -1;
-    private int invokevirtualMethodRefCpIndex = -1;
-
-    private ClassForNameChecker     classForNameChecker     = new ClassForNameChecker();
-    private ClassNewInstanceChecker classNewInstanceChecker = new ClassNewInstanceChecker();
-
-
-    /**
-     * Creates a new ClassFileClassForNameReferenceInitializer that optionally
-     * prints notes, with optional class specifications for which never to
-     * print notes.
-     */
-    public ClassFileClassForNameReferenceInitializer(ClassPool            programClassPool,
-                                                     ClassPool            libraryClassPool,
-                                                     WarningPrinter       notePrinter,
-                                                     ClassNameListMatcher noteExceptionMatcher)
-    {
-        this.programClassPool     = programClassPool;
-        this.libraryClassPool     = libraryClassPool;
-        this.notePrinter          = notePrinter;
-        this.noteExceptionMatcher = noteExceptionMatcher;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
-    {
-        // Nothing interesting; just forget any stored indices.
-        clearConstantPoolIndices();
-    }
-
-
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
-    {
-        // Nothing interesting; just forget any stored indices.
-        clearConstantPoolIndices();
-    }
-
-
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
-    {
-        // Nothing interesting; just forget any stored indices.
-        clearConstantPoolIndices();
-    }
-
-
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
-    {
-        // Nothing interesting; just forget any stored indices.
-        clearConstantPoolIndices();
-    }
-
-
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-
-        // Just ignore generic instructions and reset the constant pool indices.
-        switch (variableInstruction.opcode)
-        {
-            case InstructionConstants.OP_ICONST_0:
-            case InstructionConstants.OP_ICONST_1:
-                // Still remember any loaded string; this instruction may be
-                // setting up the second argument for class$(String, boolean).
-                break;
-
-            default:
-                ldcStringCpIndex = -1;
-                break;
-        }
-
-        invokestaticMethodRefCpIndex  = -1;
-        invokevirtualMethodRefCpIndex = -1;
-    }
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        int currentCpIndex = cpInstruction.cpIndex;
-
-        switch (cpInstruction.opcode)
-        {
-            case InstructionConstants.OP_LDC:
-            case InstructionConstants.OP_LDC_W:
-                // Are we loading a constant String?
-                int currentCpTag = classFile.getCpTag(currentCpIndex);
-                if (currentCpTag == ClassConstants.CONSTANT_String)
-                {
-                    // Remember it; it might be the argument of
-                    // Class.forName(String), class$(String), or
-                    // class$(String, boolean).
-                    ldcStringCpIndex = currentCpIndex;
-                }
-                else
-                {
-                    ldcStringCpIndex = -1;
-                }
-
-                invokestaticMethodRefCpIndex  = -1;
-                invokevirtualMethodRefCpIndex = -1;
-                break;
-
-            case InstructionConstants.OP_INVOKESTATIC:
-                // Are we invoking a static method that might have a constant
-                // String argument?
-                if (ldcStringCpIndex > 0)
-                {
-                    // Check whether the method reference points to Class.forName.
-                    if (classForNameChecker.check(classFile, currentCpIndex))
-                    {
-                        // Fill out the class file reference in the String.
-                        classFile.constantPoolEntryAccept(ldcStringCpIndex, this);
-                    }
-
-                    // We've dealt with this invocation, so we can forget about it.
-                    invokestaticMethodRefCpIndex = -1;
-                }
-                else
-                {
-                    // Remember it; it might still be a Class.forName with a
-                    // variable String argument.
-                    invokestaticMethodRefCpIndex = currentCpIndex;
-                }
-
-                ldcStringCpIndex              = -1;
-                invokevirtualMethodRefCpIndex = -1;
-                break;
-
-            case InstructionConstants.OP_INVOKEVIRTUAL:
-                // Are we invoking a virtual method right after a static method?
-                if (invokestaticMethodRefCpIndex > 0)
-                {
-                    // Remember it; it might be Class.newInstance after a Class.forName.
-                    invokevirtualMethodRefCpIndex = currentCpIndex;
-                }
-                else
-                {
-                    invokestaticMethodRefCpIndex  = -1;
-                    invokevirtualMethodRefCpIndex = -1;
-                }
-
-                ldcStringCpIndex = -1;
-                break;
-
-            case InstructionConstants.OP_CHECKCAST:
-                // Are we checking a cast right after a static method and a
-                // virtual method?
-                if (invokestaticMethodRefCpIndex  > 0 &&
-                    invokevirtualMethodRefCpIndex > 0)
-                {
-                    // Check whether the first method reference points to Class.forName,
-                    // and the second method reference to Class.newInstance.
-                    if (classForNameChecker.check(classFile, invokestaticMethodRefCpIndex) &&
-                        classNewInstanceChecker.check(classFile, invokevirtualMethodRefCpIndex))
-                    {
-                        // Note which class is being cast to.
-                        classFile.constantPoolEntryAccept(currentCpIndex, this);
-                    }
-                }
-
-                // We've handled the special case; forget about the indices.
-                clearConstantPoolIndices();
-                break;
-
-            default:
-                // Nothing interesting; just forget any stored indices.
-                clearConstantPoolIndices();
-                break;
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    /**
-     * Fills out the link to the referenced ClassFile.
-     */
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        // Save a reference to the corresponding class file.
-        String externalClassName = stringCpInfo.getString(classFile);
-        String internalClassName = ClassUtil.internalClassName(externalClassName);
-
-        stringCpInfo.referencedClassFile = findClass(internalClassName);
-    }
-
-
-    /**
-     * Prints out a note about the class cast to this class, if applicable.
-     */
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        if (notePrinter != null &&
-            (noteExceptionMatcher == null ||
-             !noteExceptionMatcher.matches(classCpInfo.getName(classFile))))
-        {
-            notePrinter.print("Note: " +
-                                 ClassUtil.externalClassName(classFile.getName()) +
-                                 " calls '(" +
-                                 ClassUtil.externalClassName(classCpInfo.getName(classFile)) +
-                                 ")Class.forName(variable).newInstance()'");
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Clears all references to the constant pool.
-     */
-    private void clearConstantPoolIndices()
-    {
-        ldcStringCpIndex              = -1;
-        invokestaticMethodRefCpIndex  = -1;
-        invokevirtualMethodRefCpIndex = -1;
-    }
-
-
-    /**
-     * Returns the class with the given name, either for the program class pool
-     * or from the library class pool, or <code>null</code> if it can't be found.
-     */
-    private ClassFile findClass(String name)
-    {
-        // First look for the class in the program class pool.
-        ClassFile classFile = programClassPool.getClass(name);
-
-        // Otherwise look for the class in the library class pool.
-        if (classFile == null)
-        {
-            classFile = libraryClassPool.getClass(name);
-        }
-
-        return classFile;
-    }
-}
diff --git a/src/proguard/classfile/util/ClassFileHierarchyInitializer.java b/src/proguard/classfile/util/ClassFileHierarchyInitializer.java
deleted file mode 100644
index ec6f3f6..0000000
--- a/src/proguard/classfile/util/ClassFileHierarchyInitializer.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/* $Id: ClassFileHierarchyInitializer.java,v 1.12.2.3 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassFileVisitor initializes the class hierarchy of all class files that
- * it visits.
- * <p>
- * Visited class files are added to the subclass list of their superclasses and
- * interfaces. These subclass lists make it more convenient to travel down the
- * class hierarchy.
- * <p>
- * Visited library class files get direct references to their superclasses and
- * interfaces, replacing the superclass names and interface names. The direct
- * references are equivalent to the names, but they are more efficient to work
- * with.
- * <p>
- * This visitor optionally prints warnings if some items can't be found.
- *
- * @author Eric Lafortune
- */
-public class ClassFileHierarchyInitializer
-  implements ClassFileVisitor,
-             CpInfoVisitor
-{
-    // A visitor info flag to indicate the class file has been initialized.
-    private static final Object INITIALIZED = new Object();
-
-    private ClassPool      programClassPool;
-    private ClassPool      libraryClassPool;
-    private WarningPrinter warningPrinter;
-
-
-    /**
-     * Creates a new ClassFileReferenceInitializer that initializes the
-     * hierarchy of all visited class files, optionally printing warnings if
-     * some classes can't be found.
-     */
-    public ClassFileHierarchyInitializer(ClassPool      programClassPool,
-                                         ClassPool      libraryClassPool,
-                                         WarningPrinter warningPrinter)
-    {
-        this.programClassPool = programClassPool;
-        this.libraryClassPool = libraryClassPool;
-        this.warningPrinter   = warningPrinter;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Haven't we initialized this class before?
-        if (!isInitialized(programClassFile))
-        {
-            // Mark this class.
-            markAsInitialized(programClassFile);
-
-            if (programClassFile.u2superClass != 0)
-            {
-                programClassFile.constantPoolEntryAccept(programClassFile.u2superClass,
-                                                         this);
-            }
-
-            // Add this class to the subclasses of its superclass.
-            if (programClassFile.u2superClass != 0)
-            {
-                addSubclass(programClassFile,
-                            programClassFile.getSuperClass(),
-                            programClassFile.getSuperName());
-            }
-
-            // Add this class to the subclasses of its interfaces.
-            for (int index = 0; index < programClassFile.u2interfacesCount; index++)
-            {
-                programClassFile.constantPoolEntryAccept(programClassFile.u2interfaces[index],
-                                                         this);
-
-                addSubclass(programClassFile,
-                            programClassFile.getInterface(index),
-                            programClassFile.getInterfaceName(index));
-            }
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Haven't we initialized this class before?
-        if (!isInitialized(libraryClassFile))
-        {
-            // Mark this class.
-            markAsInitialized(libraryClassFile);
-
-            // Have a closer look at the superclass.
-            String superClassName = libraryClassFile.superClassName;
-            if (superClassName != null)
-            {
-                // Find and initialize the super class.
-                ClassFile superClass = findAndInitializeClass(superClassName);
-
-                // Add this class to the subclasses of its superclass,
-                addSubclass(libraryClassFile,
-                            superClass,
-                            superClassName);
-
-                // Keep a reference to the superclass.
-                libraryClassFile.superClass = superClass;
-            }
-
-            // Have a closer look at the interface classes.
-            if (libraryClassFile.interfaceNames != null)
-            {
-                String[]    interfaceNames   = libraryClassFile.interfaceNames;
-                ClassFile[] interfaceClasses = new ClassFile[interfaceNames.length];
-
-                for (int index = 0; index < interfaceNames.length; index++)
-                {
-                    // Find and initialize the interface class.
-                    String    interfaceName  = interfaceNames[index];
-                    ClassFile interfaceClass = findAndInitializeClass(interfaceName);
-
-                    // Add this class to the subclasses of the interface class.
-                    addSubclass(libraryClassFile,
-                                interfaceClass,
-                                interfaceName);
-
-                    // Keep a reference to the interface class.
-                    interfaceClasses[index] = interfaceClass;
-                }
-
-                libraryClassFile.interfaceClasses = interfaceClasses;
-            }
-
-            // Discard the name Strings. From now on, we'll use the object
-            // references.
-            libraryClassFile.superClassName = null;
-            libraryClassFile.interfaceNames = null;
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        classCpInfo.referencedClassFile =
-            findAndInitializeClass(classCpInfo.getName(classFile));
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Finds and initializes a class with the given name.
-     *
-     * @see #findClass(String)
-     */
-    private ClassFile findAndInitializeClass(String name)
-    {
-        // Try to find the class file.
-        ClassFile referencedClassFile = findClass(name);
-
-        // Did we find the referenced class file in either class pool?
-        if (referencedClassFile != null)
-        {
-            // Initialize it.
-            referencedClassFile.accept(this);
-        }
-
-        return referencedClassFile;
-    }
-
-
-    /**
-     * 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 ClassFile findClass(String name)
-    {
-        // First look for the class in the program class pool.
-        ClassFile classFile = programClassPool.getClass(name);
-
-        // Otherwise look for the class in the library class pool.
-        if (classFile == null)
-        {
-            classFile = libraryClassPool.getClass(name);
-        }
-
-        return classFile;
-    }
-
-
-    private static void markAsInitialized(VisitorAccepter visitorAccepter)
-    {
-        visitorAccepter.setVisitorInfo(INITIALIZED);
-    }
-
-
-    private static boolean isInitialized(VisitorAccepter visitorAccepter)
-    {
-        return visitorAccepter.getVisitorInfo() == INITIALIZED;
-    }
-
-
-    private void addSubclass(ClassFile subclass,
-                             ClassFile classFile,
-                             String    className)
-    {
-        if (classFile != null)
-        {
-            classFile.addSubClass(subclass);
-        }
-        else if (warningPrinter != null)
-        {
-            // We didn't find the superclass or interface. Print a warning.
-            warningPrinter.print("Warning: " +
-                                 ClassUtil.externalClassName(subclass.getName()) +
-                                 ": can't find superclass or interface " +
-                                 ClassUtil.externalClassName(className));
-        }
-    }
-}
diff --git a/src/proguard/classfile/util/ClassFileReferenceInitializer.java b/src/proguard/classfile/util/ClassFileReferenceInitializer.java
deleted file mode 100644
index 0aef9c7..0000000
--- a/src/proguard/classfile/util/ClassFileReferenceInitializer.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/* $Id: ClassFileReferenceInitializer.java,v 1.31.2.5 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassFileVisitor initializes the references of all class files that
- * it visits.
- * <p>
- * All class constant pool entries get direct references to the corresponding
- * classes. These references make it more convenient to travel up and across
- * the class hierarchy.
- * <p>
- * All field and method reference constant pool entries get direct references
- * to the corresponding classes, fields, and methods.
- * <p>
- * All name and type constant pool entries get a list of direct references to
- * the classes listed in the type.
- * <p>
- * This visitor optionally prints warnings if some items can't be found.
- * <p>
- * The class file hierarchy must be initialized before using this visitor.
- *
- * @author Eric Lafortune
- */
-public class ClassFileReferenceInitializer
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor,
-             LocalVariableInfoVisitor,
-             LocalVariableTypeInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor
-{
-    private MemberFinder memberFinder = new MemberFinder();
-
-    private ClassPool      programClassPool;
-    private ClassPool      libraryClassPool;
-    private WarningPrinter warningPrinter;
-
-
-    /**
-     * Creates a new ClassFileReferenceInitializer that initializes the
-     * references of all visited class files, optionally printing warnings if
-     * some classes can't be found.
-     */
-    public ClassFileReferenceInitializer(ClassPool      programClassPool,
-                                         ClassPool      libraryClassPool,
-                                         WarningPrinter warningPrinter)
-    {
-        this.programClassPool = programClassPool;
-        this.libraryClassPool = libraryClassPool;
-        this.warningPrinter   = warningPrinter;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Initialize the constant pool entries.
-        programClassFile.constantPoolEntriesAccept(this);
-
-        // Initialize all fields and methods.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        // Initialize the attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Initialize all fields and methods.
-        libraryClassFile.fieldsAccept(this);
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        programFieldInfo.referencedClassFile =
-            findReferencedClass(programFieldInfo.getDescriptor(programClassFile));
-
-        // Initialize the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        programMethodInfo.referencedClassFiles =
-            findReferencedClasses(programMethodInfo.getDescriptor(programClassFile));
-
-        // Initialize the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        libraryFieldInfo.referencedClassFile =
-            findReferencedClass(libraryFieldInfo.getDescriptor(libraryClassFile));
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        libraryMethodInfo.referencedClassFiles =
-            findReferencedClasses(libraryMethodInfo.getDescriptor(libraryClassFile));
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        visitRefCpInfo(classFile, fieldrefCpInfo, true);
-    }
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        visitRefCpInfo(classFile, interfaceMethodrefCpInfo, false);
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        visitRefCpInfo(classFile, methodrefCpInfo, false);
-    }
-
-
-    private void visitRefCpInfo(ClassFile classFile, RefCpInfo refCpInfo, boolean isFieldRef)
-    {
-        String className = refCpInfo.getClassName(classFile);
-
-        // See if we can find the referenced class file.
-        // Unresolved references are assumed to refer to library class files
-        // that will not change anyway.
-        ClassFile referencedClassFile = findClass(className);
-
-        if (referencedClassFile != null)
-        {
-            String name = refCpInfo.getName(classFile);
-            String type = refCpInfo.getType(classFile);
-
-            // See if we can find the referenced class member somewhere in the
-            // hierarchy.
-            refCpInfo.referencedMemberInfo = memberFinder.findMember(classFile,
-                                                                     referencedClassFile,
-                                                                     name,
-                                                                     type,
-                                                                     isFieldRef);
-            refCpInfo.referencedClassFile  = memberFinder.correspondingClassFile();
-
-            // Check if we haven't found the class member anywhere in the
-            // hierarchy.
-            if (refCpInfo.referencedMemberInfo == null &&
-                warningPrinter != null)
-            {
-                warningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(classFile.getName()) +
-                                     ": can't find referenced " +
-                                     (isFieldRef ?
-                                         "field '"  + ClassUtil.externalFullFieldDescription(0, name, type) :
-                                         "method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
-                                     "' in class " +
-                                     ClassUtil.externalClassName(className));
-            }
-        }
-    }
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        classCpInfo.referencedClassFile =
-            findClass(classCpInfo.getName(classFile));
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        String className = enclosingMethodAttrInfo.getClassName(classFile);
-
-        // See if we can find the referenced class file.
-        ClassFile referencedClassFile = findClass(className);
-
-        if (referencedClassFile == null)
-        {
-            // We couldn't find the enclosing class.
-            if (warningPrinter != null)
-            {
-                warningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(classFile.getName()) +
-                                     ": can't find enclosing class " +
-                                     ClassUtil.externalClassName(className));
-            }
-
-            return;
-        }
-
-        // Make sure there is actually an enclosed method.
-        if (enclosingMethodAttrInfo.u2nameAndTypeIndex == 0)
-        {
-            return;
-        }
-
-        String name = enclosingMethodAttrInfo.getName(classFile);
-        String type = enclosingMethodAttrInfo.getType(classFile);
-
-        // See if we can find the method in the referenced class.
-        MethodInfo referencedMethodInfo = referencedClassFile.findMethod(name, type);
-
-        if (referencedMethodInfo == null)
-        {
-            // We couldn't find the enclosing method.
-            if (warningPrinter != null)
-            {
-                warningPrinter.print("Warning: " +
-                                     ClassUtil.externalClassName(classFile.getName()) +
-                                     ": can't find enclosing method '" +
-                                     ClassUtil.externalFullMethodDescription(className, 0, name, type) +
-                                     "' in class " +
-                                     ClassUtil.externalClassName(className));
-            }
-
-            return;
-        }
-
-        // Save the references.
-        enclosingMethodAttrInfo.referencedClassFile  = referencedClassFile;
-        enclosingMethodAttrInfo.referencedMethodInfo = referencedMethodInfo;
-    }
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Initialize the nested attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        // Initialize the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        // Initialize the local variable types.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        signatureAttrInfo.referencedClassFiles =
-            findReferencedClasses(classFile.getCpString(signatureAttrInfo.u2signatureIndex));
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        // Initialize the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        // Initialize the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        // Initialize the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        // Initialize the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        // Initialize the annotation.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
-    }
-
-
-    // Implementations for LocalVariableInfoVisitor.
-
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
-    {
-        localVariableInfo.referencedClassFile =
-            findReferencedClass(classFile.getCpString(localVariableInfo.u2descriptorIndex));
-    }
-
-
-    // Implementations for LocalVariableTypeInfoVisitor.
-
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
-    {
-        localVariableTypeInfo.referencedClassFiles =
-            findReferencedClasses(classFile.getCpString(localVariableTypeInfo.u2signatureIndex));
-    }
-
-
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
-    {
-        annotation.referencedClassFiles =
-            findReferencedClasses(classFile.getCpString(annotation.u2typeIndex));
-
-        // Initialize the element values.
-        annotation.elementValuesAccept(classFile, this);
-    }
-
-
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
-    {
-        initializeElementValue(classFile, annotation, constantElementValue);
-    }
-
-
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        initializeElementValue(classFile, annotation, enumConstantElementValue);
-
-        enumConstantElementValue.referencedClassFiles =
-            findReferencedClasses(classFile.getCpString(enumConstantElementValue.u2typeNameIndex));
-    }
-
-
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
-    {
-        initializeElementValue(classFile, annotation, classElementValue);
-
-        classElementValue.referencedClassFiles =
-            findReferencedClasses(classFile.getCpString(classElementValue.u2classInfoIndex));
-    }
-
-
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
-    {
-        initializeElementValue(classFile, annotation, annotationElementValue);
-
-        // Initialize the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
-    }
-
-
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        initializeElementValue(classFile, annotation, arrayElementValue);
-
-        // Initialize the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-    }
-
-
-    /**
-     * Initializes the referenced method of an element value, if any.
-     */
-    private void initializeElementValue(ClassFile classFile, Annotation annotation, ElementValue elementValue)
-    {
-        // See if we have a referenced class file.
-        if (annotation                      != null &&
-            annotation.referencedClassFiles != null &&
-            elementValue.u2elementName      != 0)
-        {
-            // See if we can find the method in the referenced class
-            // (ignoring the descriptor).
-            String name = classFile.getCpString(elementValue.u2elementName);
-
-            ClassFile referencedClassFile = annotation.referencedClassFiles[0];
-            elementValue.referencedClassFile  = referencedClassFile;
-            elementValue.referencedMethodInfo = referencedClassFile.findMethod(name, null);
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Returns the single class file referenced by the given descriptor, or
-     * <code>null</code> if there isn't any useful reference.
-     */
-    private ClassFile findReferencedClass(String descriptor)
-    {
-        DescriptorClassEnumeration enumeration =
-            new DescriptorClassEnumeration(descriptor);
-
-        if (enumeration.hasMoreClassNames())
-        {
-            return findClass(enumeration.nextClassName());
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Returns an array of class files referenced by the given descriptor, or
-     * <code>null</code> if there aren't any useful references.
-     */
-    private ClassFile[] findReferencedClasses(String descriptor)
-    {
-        DescriptorClassEnumeration enumeration =
-            new DescriptorClassEnumeration(descriptor);
-
-        int classCount = enumeration.classCount();
-        if (classCount > 0)
-        {
-            ClassFile[] referencedClassFiles = new ClassFile[classCount];
-
-            boolean foundReferencedClassFiles = false;
-
-            for (int index = 0; index < classCount; index++)
-            {
-                String name = enumeration.nextClassName();
-
-                ClassFile referencedClassFile = findClass(name);
-
-                if (referencedClassFile != null)
-                {
-                    referencedClassFiles[index] = referencedClassFile;
-                    foundReferencedClassFiles = true;
-                }
-            }
-
-            if (foundReferencedClassFiles)
-            {
-                return referencedClassFiles;
-            }
-        }
-
-        return null;
-    }
-
-
-    /**
-     * 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 ClassFile findClass(String name)
-    {
-        // First look for the class in the program class pool.
-        ClassFile classFile = programClassPool.getClass(name);
-
-        // Otherwise look for the class in the library class pool.
-        if (classFile == null)
-        {
-            classFile = libraryClassPool.getClass(name);
-        }
-
-        return classFile;
-    }
-}
diff --git a/src/proguard/classfile/util/ClassForNameChecker.java b/src/proguard/classfile/util/ClassForNameChecker.java
deleted file mode 100644
index b36ed9b..0000000
--- a/src/proguard/classfile/util/ClassForNameChecker.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/* $Id: ClassForNameChecker.java,v 1.12.2.3 2007/06/09 11:54:27 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This class can check whether method references point to <code>Class.forName</code>
- * or <code>.class</code>.
- *
- * @author Eric Lafortune
- */
-class      ClassForNameChecker
-implements CpInfoVisitor,
-           MemberInfoVisitor,
-           AttrInfoVisitor,
-           InstructionVisitor
-{
-    // These fields acts as a return variables and status variables for the visitors.
-    private boolean isClassForNameInvocation;
-    private boolean insideDotClassMethod;
-    private boolean firstInstructionOk;
-
-
-    /**
-     * Creates a new ClassForNameChecker.
-     */
-    public ClassForNameChecker()
-    {
-    }
-
-
-    /**
-     * Checks whether the specified method reference points to
-     * <code>Class.forName</code> or <code>.class</code>.
-     * @param classFile            the class file in which the method reference
-     *                             is located.
-     * @param methodrefCpInfoIndex the index of the method reference in the
-     *                             constant pool.
-     * @return a boolean that indicates whether the reference points to either
-     *         method.
-     */
-    public boolean check(ClassFile classFile, int methodrefCpInfoIndex)
-    {
-        isClassForNameInvocation = false;
-
-        classFile.constantPoolEntryAccept(methodrefCpInfoIndex, this);
-
-        return isClassForNameInvocation;
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-    /**
-     * Checks whether the given method reference points to
-     * <code>Class.forName</code> or <code>.class</code>.
-     */
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        String className  = methodrefCpInfo.getClassName(classFile);
-        String methodName = methodrefCpInfo.getName(classFile);
-        String methodType = methodrefCpInfo.getType(classFile);
-
-        // Is it a reference to "Class Class.forName(String)"?
-        isClassForNameInvocation =
-            className .equals(ClassConstants.INTERNAL_CLASS_NAME_JAVA_LANG_CLASS) &&
-            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME) &&
-            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME);
-
-        if (isClassForNameInvocation || insideDotClassMethod)
-        {
-            return;
-        }
-
-        // Is it a reference to .class? This construct is typically implemented
-        // as (static) "Class class$(String)" or "Class class$(String, boolean)".
-        // First check the type, which has to be right to start with.
-        isClassForNameInvocation =
-            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) ||
-            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES);
-
-        if (!isClassForNameInvocation)
-        {
-            return;
-        }
-
-        // Then check if the method perhaps still has its original "class$" name.
-        isClassForNameInvocation =
-            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS);
-
-        if (isClassForNameInvocation)
-        {
-            return;
-        }
-
-        // We're still not sure it's not a reference to .class, since the
-        // method name may have been changed or obfuscated. Perform a more
-        // detailed analysis by looking at the referenced method itself.
-        // We're looking for the method by name and type, because the
-        // referenced method hasn't been filled out yet. Make sure we don't
-        // do this recursively.
-        insideDotClassMethod = true;
-        classFile.methodAccept(methodName, methodType, this);
-        insideDotClassMethod = false;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-    /**
-     * Checks whether the given method is a .class implementation.
-     */
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        programMethodInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    /**
-     * Checks whether the given code is an implementation of class$(String) or
-     * class$(String, boolean).
-     */
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Check whether the first instruction recalls the first argument of
-        // this method.
-        firstInstructionOk = false;
-        codeAttrInfo.instructionAccept(classFile, methodInfo, this, 0);
-
-        // Continue checking whether the second instruction invokes
-        // Class.forName.
-        if (firstInstructionOk)
-        {
-            codeAttrInfo.instructionAccept(classFile, methodInfo, this, 1);
-        }
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    /**
-     * Checks whether this is a valid first instruction for a .class implementation.
-     */
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-        firstInstructionOk = variableInstruction.opcode == InstructionConstants.OP_ALOAD_0;
-    }
-
-    /**
-     * Checks whether this is a valid second instruction for a .class implementation.
-     */
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        if (firstInstructionOk &&
-            cpInstruction.opcode == InstructionConstants.OP_INVOKESTATIC)
-        {
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        }
-    }
-}
diff --git a/src/proguard/classfile/util/ClassNewInstanceChecker.java b/src/proguard/classfile/util/ClassNewInstanceChecker.java
deleted file mode 100644
index f08290a..0000000
--- a/src/proguard/classfile/util/ClassNewInstanceChecker.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* $Id: ClassNewInstanceChecker.java,v 1.8.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This class can check whether method references point to
- * <code>Class.newInstance</code>.
- *
- * @author Eric Lafortune
- */
-class      ClassNewInstanceChecker
-implements CpInfoVisitor
-{
-    // This field acts as a return variable for the CpInfoVisitor.
-    private boolean isClassNewInstanceInvocation;
-
-
-    /**
-     * Creates a new ClassNewInstanceChecker.
-     */
-    public ClassNewInstanceChecker()
-    {
-    }
-
-
-    /**
-     * Checks whether the specified method reference points to
-     * <code>Class.newInstance</code>.
-     * @param classFile            the class file in which the method reference
-     *                             is located.
-     * @param methodrefCpInfoIndex the index of the method reference in the
-     *                             constant pool.
-     * @return a boolean that indicates whether the reference points to the
-     *         method.
-     */
-    public boolean check(ClassFile classFile, int methodrefCpInfoIndex)
-    {
-        isClassNewInstanceInvocation = false;
-
-        classFile.constantPoolEntryAccept(methodrefCpInfoIndex, this);
-
-        return isClassNewInstanceInvocation;
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-    /**
-     * Checks whether the given method reference points to
-     * <code>Class.newInstance</code>.
-     */
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        String className  = methodrefCpInfo.getClassName(classFile);
-        String methodName = methodrefCpInfo.getName(classFile);
-        String methodType = methodrefCpInfo.getType(classFile);
-
-        // Is it a reference to "Object Class.newInstance()"?
-        isClassNewInstanceInvocation =
-            className .equals(ClassConstants.INTERNAL_CLASS_NAME_JAVA_LANG_CLASS) &&
-            methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE)   &&
-            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE);
-    }
-}
diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java
new file mode 100644
index 0000000..12b1ab2
--- /dev/null
+++ b/src/proguard/classfile/util/ClassReferenceInitializer.java
@@ -0,0 +1,494 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.visitor.*;
+
+
+/**
+ * This ClassVisitor initializes the references of all classes that
+ * it visits.
+ * <p>
+ * All class constant pool entries get direct references to the corresponding
+ * classes. These references make it more convenient to travel up and across
+ * the class hierarchy.
+ * <p>
+ * All field and method reference constant pool entries get direct references
+ * to the corresponding classes, fields, and methods.
+ * <p>
+ * All name and type constant pool entries get a list of direct references to
+ * the classes listed in the type.
+ * <p>
+ * This visitor optionally prints warnings if some items can't be found.
+ * <p>
+ * The class hierarchy must be initialized before using this visitor.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassReferenceInitializer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor,
+             AttributeVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter warningPrinter;
+
+    private final MemberFinder memberFinder = new MemberFinder();
+
+
+    /**
+     * Creates a new ClassReferenceInitializer that initializes the references
+     * of all visited class files, optionally printing warnings if some classes
+     * can't be found.
+     */
+    public ClassReferenceInitializer(ClassPool      programClassPool,
+                                     ClassPool      libraryClassPool,
+                                     WarningPrinter warningPrinter)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.warningPrinter   = warningPrinter;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Initialize the constant pool entries.
+        programClass.constantPoolEntriesAccept(this);
+
+        // Initialize all fields and methods.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+
+        // Initialize the attributes.
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Initialize all fields and methods.
+        libraryClass.fieldsAccept(this);
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        programField.referencedClass =
+            findReferencedClass(programField.getDescriptor(programClass));
+
+        // Initialize the attributes.
+        programField.attributesAccept(programClass, this);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        programMethod.referencedClasses =
+            findReferencedClasses(programMethod.getDescriptor(programClass));
+
+        // Initialize the attributes.
+        programMethod.attributesAccept(programClass, this);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        libraryField.referencedClass =
+            findReferencedClass(libraryField.getDescriptor(libraryClass));
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        libraryMethod.referencedClasses =
+            findReferencedClasses(libraryMethod.getDescriptor(libraryClass));
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Fill out the String class.
+        stringConstant.javaLangStringClass =
+            findClass(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        String className = refConstant.getClassName(clazz);
+
+        // See if we can find the referenced class.
+        // Unresolved references are assumed to refer to library classes
+        // that will not change anyway.
+        Clazz referencedClass = findClass(className);
+
+        if (referencedClass != null &&
+            !ClassUtil.isInternalArrayType(className))
+        {
+            String name = refConstant.getName(clazz);
+            String type = refConstant.getType(clazz);
+
+            boolean isFieldRef = refConstant.getTag() == ClassConstants.CONSTANT_Fieldref;
+
+            // See if we can find the referenced class member somewhere in the
+            // hierarchy.
+            refConstant.referencedMember = memberFinder.findMember(clazz,
+                                                                   referencedClass,
+                                                                   name,
+                                                                   type,
+                                                                   isFieldRef);
+            refConstant.referencedClass  = memberFinder.correspondingClass();
+
+            if (refConstant.referencedMember == null &&
+                warningPrinter != null)
+            {
+                // We've haven't found the class member anywhere in the hierarchy.
+                warningPrinter.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));
+            }
+        }
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Fill out the referenced class.
+        classConstant.referencedClass =
+            findClass(classConstant.getName(clazz));
+
+        // Fill out the Class class.
+        classConstant.javaLangClassClass =
+            findClass(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS);
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        String className = enclosingMethodAttribute.getClassName(clazz);
+
+        // See if we can find the referenced class.
+        Clazz referencedClass = findClass(className);
+
+        if (referencedClass == null)
+        {
+            // We couldn't find the enclosing class.
+            if (warningPrinter != null)
+            {
+                warningPrinter.print("Warning: " +
+                                     ClassUtil.externalClassName(clazz.getName()) +
+                                     ": can't find enclosing class " +
+                                     ClassUtil.externalClassName(className));
+            }
+
+            return;
+        }
+
+        // Make sure there is actually an enclosed method.
+        if (enclosingMethodAttribute.u2nameAndTypeIndex == 0)
+        {
+            return;
+        }
+
+        String name = enclosingMethodAttribute.getName(clazz);
+        String type = enclosingMethodAttribute.getType(clazz);
+
+        // See if we can find the method in the referenced class.
+        Method referencedMethod = referencedClass.findMethod(name, type);
+
+        if (referencedMethod == null)
+        {
+            // We couldn't find the enclosing method.
+            if (warningPrinter != null)
+            {
+                warningPrinter.print("Warning: " +
+                                     ClassUtil.externalClassName(clazz.getName()) +
+                                     ": can't find enclosing method '" +
+                                     ClassUtil.externalFullMethodDescription(className, 0, name, type) +
+                                     "' in class " +
+                                     ClassUtil.externalClassName(className));
+            }
+
+            return;
+        }
+
+        // Save the references.
+        enclosingMethodAttribute.referencedClass  = referencedClass;
+        enclosingMethodAttribute.referencedMethod = referencedMethod;
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Initialize the nested attributes.
+        codeAttribute.attributesAccept(clazz, method, this);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Initialize the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Initialize the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        signatureAttribute.referencedClasses =
+            findReferencedClasses(clazz.getString(signatureAttribute.u2signatureIndex));
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Initialize the annotations.
+        annotationsAttribute.annotationsAccept(clazz, this);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Initialize the annotations.
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // Initialize the annotation.
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        localVariableInfo.referencedClass =
+            findReferencedClass(clazz.getString(localVariableInfo.u2descriptorIndex));
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        localVariableTypeInfo.referencedClasses =
+            findReferencedClasses(clazz.getString(localVariableTypeInfo.u2signatureIndex));
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        annotation.referencedClasses =
+            findReferencedClasses(clazz.getString(annotation.u2typeIndex));
+
+        // Initialize the element values.
+        annotation.elementValuesAccept(clazz, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        initializeElementValue(clazz, annotation, constantElementValue);
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        initializeElementValue(clazz, annotation, enumConstantElementValue);
+
+        enumConstantElementValue.referencedClasses =
+            findReferencedClasses(clazz.getString(enumConstantElementValue.u2typeNameIndex));
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        initializeElementValue(clazz, annotation, classElementValue);
+
+        classElementValue.referencedClasses =
+            findReferencedClasses(clazz.getString(classElementValue.u2classInfoIndex));
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        initializeElementValue(clazz, annotation, annotationElementValue);
+
+        // Initialize the annotation.
+        annotationElementValue.annotationAccept(clazz, this);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        initializeElementValue(clazz, annotation, arrayElementValue);
+
+        // Initialize the element values.
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+    }
+
+
+    /**
+     * Initializes the referenced method of an element value, if any.
+     */
+    private void initializeElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+    {
+        // See if we have a referenced class.
+        if (annotation                      != null &&
+            annotation.referencedClasses    != null &&
+            elementValue.u2elementNameIndex != 0)
+        {
+            // See if we can find the method in the referenced class
+            // (ignoring the descriptor).
+            String name = clazz.getString(elementValue.u2elementNameIndex);
+
+            Clazz referencedClass = annotation.referencedClasses[0];
+            elementValue.referencedClass  = referencedClass;
+            elementValue.referencedMethod = referencedClass.findMethod(name, null);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the single class referenced by the given descriptor, or
+     * <code>null</code> if there isn't any useful reference.
+     */
+    private Clazz findReferencedClass(String descriptor)
+    {
+        DescriptorClassEnumeration enumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        if (enumeration.hasMoreClassNames())
+        {
+            return findClass(enumeration.nextClassName());
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns an array of classes referenced by the given descriptor, or
+     * <code>null</code> if there aren't any useful references.
+     */
+    private Clazz[] findReferencedClasses(String descriptor)
+    {
+        DescriptorClassEnumeration enumeration =
+            new DescriptorClassEnumeration(descriptor);
+
+        int classCount = enumeration.classCount();
+        if (classCount > 0)
+        {
+            Clazz[] referencedClasses = new Clazz[classCount];
+
+            boolean foundReferencedClasses = false;
+
+            for (int index = 0; index < classCount; index++)
+            {
+                String name = enumeration.nextClassName();
+
+                Clazz referencedClass = findClass(name);
+
+                if (referencedClass != null)
+                {
+                    referencedClasses[index] = referencedClass;
+                    foundReferencedClasses = true;
+                }
+            }
+
+            if (foundReferencedClasses)
+            {
+                return referencedClasses;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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;
+    }
+}
diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java
new file mode 100644
index 0000000..b2f7514
--- /dev/null
+++ b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java
@@ -0,0 +1,77 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This ClassVisitor adds all classes that it visits to the list of subclasses
+ * of their superclass. These subclass lists make it more convenient to travel
+ *
+ * @author Eric Lafortune
+ */
+public class ClassSubHierarchyInitializer
+implements   ClassVisitor
+{
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Add this class to the subclasses of its superclass.
+        addSubclass(programClass, programClass.getSuperClass());
+
+        // Add this class to the subclasses of its interfaces.
+        for (int index = 0; index < programClass.u2interfacesCount; index++)
+        {
+            addSubclass(programClass, programClass.getInterface(index));
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Add this class to the subclasses of its superclass,
+        addSubclass(libraryClass, libraryClass.superClass);
+
+        // Add this class to the subclasses of its interfaces.
+        Clazz[] interfaceClasses = libraryClass.interfaceClasses;
+        if (interfaceClasses != null)
+        {
+            for (int index = 0; index < interfaceClasses.length; index++)
+            {
+                // Add this class to the subclasses of the interface class.
+                addSubclass(libraryClass, interfaceClasses[index]);
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    private void addSubclass(Clazz subclass, Clazz clazz)
+    {
+        if (clazz != null)
+        {
+            clazz.addSubClass(subclass);
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java
new file mode 100644
index 0000000..aa12165
--- /dev/null
+++ b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java
@@ -0,0 +1,159 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.ClassConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This ClassVisitor initializes the superclass hierarchy of all classes that
+ * it visits.
+ * <p>
+ * Visited library classes get direct references to their superclasses and
+ * interfaces, replacing the superclass names and interface names. The direct
+ * references are equivalent to the names, but they are more efficient to work
+ * with.
+ * <p>
+ * This visitor optionally prints warnings if some items can't be found.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassSuperHierarchyInitializer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor
+{
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter warningPrinter;
+
+
+    /**
+     * Creates a new ClassSuperHierarchyInitializer that initializes the super
+     * hierarchy of all visited class files, optionally printing warnings if
+     * some classes can't be found.
+     */
+    public ClassSuperHierarchyInitializer(ClassPool      programClassPool,
+                                          ClassPool      libraryClassPool,
+                                          WarningPrinter warningPrinter)
+    {
+        this.programClassPool = programClassPool;
+        this.libraryClassPool = libraryClassPool;
+        this.warningPrinter   = warningPrinter;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Link to the super class.
+        if (programClass.u2superClass != 0)
+        {
+            programClass.constantPoolEntryAccept(programClass.u2superClass,
+                                                 this);
+        }
+
+        // Link to the interfaces.
+        for (int index = 0; index < programClass.u2interfacesCount; index++)
+        {
+            programClass.constantPoolEntryAccept(programClass.u2interfaces[index],
+                                                 this);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        String className = libraryClass.getName();
+
+        // Link to the super class.
+        String superClassName = libraryClass.superClassName;
+        if (superClassName != null)
+        {
+            // Keep a reference to the superclass.
+            libraryClass.superClass = findClass(className, superClassName);
+        }
+
+        // Link to the interfaces.
+        if (libraryClass.interfaceNames != null)
+        {
+            String[] interfaceNames   = libraryClass.interfaceNames;
+            Clazz[]  interfaceClasses = new Clazz[interfaceNames.length];
+
+            for (int index = 0; index < interfaceNames.length; index++)
+            {
+                // Keep a reference to the interface class.
+                interfaceClasses[index] =
+                    findClass(className, interfaceNames[index]);
+            }
+
+            libraryClass.interfaceClasses = interfaceClasses;
+        }
+
+        // Discard the name Strings. From now on, we'll use the object
+        // references.
+        libraryClass.superClassName = null;
+        libraryClass.interfaceNames = null;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        classConstant.referencedClass =
+            findClass(clazz.getName(), classConstant.getName(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 subClassName, 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);
+        }
+
+        if (clazz == null &&
+            warningPrinter != null)
+        {
+            // We didn't find the superclass or interface. Print a warning.
+            warningPrinter.print("Warning: " +
+                                 ClassUtil.externalClassName(subClassName) +
+                                 ": can't find superclass or interface " +
+                                 ClassUtil.externalClassName(name));
+        }
+
+        return clazz;
+    }
+}
diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java
index 53de27f..db3faeb 100644
--- a/src/proguard/classfile/util/ClassUtil.java
+++ b/src/proguard/classfile/util/ClassUtil.java
@@ -1,6 +1,6 @@
-/* $Id: ClassUtil.java,v 1.25.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,9 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.*;
-
-import java.io.*;
-import java.util.*;
+import proguard.classfile.ClassConstants;
 
+import java.util.List;
 
 /**
  * Utility methods for converting between internal and external representations
@@ -41,35 +39,107 @@ public class ClassUtil
 
 
     /**
-     * Checks whether the given class file magic number is correct.
+     * Checks whether the given class magic number is correct.
      * @param magicNumber the magic number.
-     * @throws IOException when the magic number is incorrect.
+     * @throws UnsupportedOperationException when the magic number is incorrect.
      */
-    public static void checkMagicNumber(int magicNumber) throws IOException
+    public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException
     {
         if (magicNumber != ClassConstants.MAGIC)
         {
-            throw new IOException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class file");
+            throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class");
+        }
+    }
+
+
+    /**
+     * Returns the combined class version number.
+     * @param majorVersion the major part of the class version number.
+     * @param minorVersion the minor part of the class version number.
+     * @return the combined class version number.
+     */
+    public static int internalClassVersion(int majorVersion, int minorVersion)
+    {
+        return (majorVersion << 16) | minorVersion;
+    }
+
+
+    /**
+     * Returns the major part of the given class version number.
+     * @param classVersion the combined class version number.
+     * @return the major part of the class version number.
+     */
+    public static int internalMajorClassVersion(int classVersion)
+    {
+        return classVersion >>> 16;
+    }
+
+
+    /**
+     * Returns the internal class version number.
+     * @param classVersion the external class version number.
+     * @return the internal class version number.
+     */
+    public static int internalMinorClassVersion(int classVersion)
+    {
+        return classVersion & 0xffff;
+    }
+
+
+    /**
+     * Returns the internal class version number.
+     * @param classVersion the external class version number.
+     * @return the internal class version number.
+     */
+    public static int internalClassVersion(String classVersion)
+    {
+        return
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) ||
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 :
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 :
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 :
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 :
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) ||
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 :
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) ||
+            classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 :
+                                                                             0;
+    }
+
+
+    /**
+     * Returns the minor part of the given class version number.
+     * @param classVersion the combined class version number.
+     * @return the minor part of the class version number.
+     */
+    public static String externalClassVersion(int classVersion)
+    {
+        switch (classVersion)
+        {
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0;
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2;
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3;
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4;
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5;
+            case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6;
+            default:                                        return null;
         }
     }
 
 
     /**
-     * Checks whether the given class file version numbers are supported.
-     * @param majorVersionNumber the major version number.
-     * @param minorVersionNumber the minor version number.
-     * @throws IOException when the version is not supported.
+     * Checks whether the given class version number is supported.
+     * @param classVersion the combined class version number.
+     * @throws UnsupportedOperationException when the version is not supported.
      */
-    public static void checkVersionNumbers(int majorVersionNumber, int minorVersionNumber) throws IOException
+    public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException
     {
-        if (majorVersionNumber < ClassConstants.MAJOR_VERSION_MIN ||
-            (majorVersionNumber == ClassConstants.MAJOR_VERSION_MIN &&
-             minorVersionNumber <  ClassConstants.MINOR_VERSION_MIN) ||
-            (majorVersionNumber == ClassConstants.MAJOR_VERSION_MAX &&
-             minorVersionNumber >  ClassConstants.MINOR_VERSION_MAX) ||
-            majorVersionNumber > ClassConstants.MAJOR_VERSION_MAX)
+        if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 ||
+            classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6)
         {
-            throw new IOException("Unsupported version number ["+majorVersionNumber+"."+minorVersionNumber+"] for class file format");
+            throw new UnsupportedOperationException("Unsupported version number ["+
+                                                    internalMajorClassVersion(classVersion)+"."+
+                                                    internalMinorClassVersion(classVersion)+"] for class format");
         }
     }
 
@@ -169,6 +239,24 @@ public class ClassUtil
 
 
     /**
+     * Returns whether the given internal class name is one of the interfaces
+     * that is implemented by all array types. These class names are
+     * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and
+     * "<code>java/io/Serializable</code>"
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return <code>true</code> if the given type is an array interface name,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isInternalArrayInterfaceName(String internalClassName)
+    {
+        return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName)    ||
+               ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) ||
+               ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName);
+    }
+
+
+    /**
      * Returns whether the given internal type is a plain primitive type
      * (not void).
      * @param internalType the internal type,
@@ -222,6 +310,46 @@ public class ClassUtil
 
 
     /**
+     * Returns the internal type of a given class name.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return the internal type,
+     *                          e.g. "<code>Ljava/lang/Object;</code>".
+     */
+    public static String internalTypeFromClassName(String internalClassName)
+    {
+        return internalArrayTypeFromClassName(internalClassName, 0);
+    }
+
+
+    /**
+     * Returns the internal array type of a given class name with a given number
+     * of dimensions. If the number of dimensions is 0, the class name itself is
+     * returned.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @param dimensionCount    the number of array dimensions.
+     * @return the internal array type of the array elements,
+     *                          e.g. "<code>Ljava/lang/Object;</code>".
+     */
+    public static String internalArrayTypeFromClassName(String internalClassName,
+                                                        int    dimensionCount)
+    {
+        StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2);
+
+        for (int dimension = 0; dimension < dimensionCount; dimension++)
+        {
+            buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY);
+        }
+
+        return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START)
+                     .append(internalClassName)
+                     .append(ClassConstants.INTERNAL_TYPE_CLASS_END)
+                     .toString();
+    }
+
+
+    /**
      * Returns the internal element type of a given internal array type.
      * @param internalArrayType the internal array type,
      *                          e.g. "<code>[[Ljava/lang/Object;</code>" or
@@ -418,10 +546,10 @@ public class ClassUtil
                                 (char)0;
 
         String internalType =
-            internalTypeChar != 0 ? ("" + internalTypeChar) :
-                                    (ClassConstants.INTERNAL_TYPE_CLASS_START +
-                                     internalClassName(externalType) +
-                                     ClassConstants.INTERNAL_TYPE_CLASS_END);
+            internalTypeChar != 0 ? String.valueOf(internalTypeChar) :
+                                    ClassConstants.INTERNAL_TYPE_CLASS_START +
+                                    internalClassName(externalType) +
+                                    ClassConstants.INTERNAL_TYPE_CLASS_END;
 
         // Prepend the array part, if any.
         for (int count = 0; count < dimensionCount; count++)
@@ -511,7 +639,7 @@ public class ClassUtil
         // Append the array part, if any.
         for (int count = 0; count < dimensionCount; count++)
         {
-            externalType = externalType + ClassConstants.EXTERNAL_TYPE_ARRAY;
+            externalType += ClassConstants.EXTERNAL_TYPE_ARRAY;
         }
 
         return externalType;
@@ -633,7 +761,7 @@ public class ClassUtil
     {
         return externalFieldAccessFlags(accessFlags) +
                externalType(internalFieldDescriptor) +
-               " " +
+               ' ' +
                fieldName;
     }
 
@@ -697,22 +825,19 @@ public class ClassUtil
 
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' ');
         }
-        else
+        else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
         {
-            if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
-            {
-                string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(" ");
-            }
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' ');
         }
 
         return string.toString();
@@ -749,31 +874,31 @@ public class ClassUtil
 
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' ');
         }
 
         return string.toString();
@@ -810,39 +935,39 @@ public class ClassUtil
 
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' ');
         }
         if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0)
         {
-            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(" ");
+            string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' ');
         }
 
         return string.toString();
@@ -881,11 +1006,10 @@ public class ClassUtil
                                                           String internalMethodDescriptor)
     {
         return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
-                    (ClassUtil.externalShortClassName(
-                     ClassUtil.externalClassName(internalClassName))) :
-                    (ClassUtil.externalMethodReturnType(internalMethodDescriptor) +
-                     " " +
-                     internalMethodName);
+            externalShortClassName(externalClassName(internalClassName)) :
+            (externalMethodReturnType(internalMethodDescriptor) +
+             ' ' +
+             internalMethodName);
     }
 
 
@@ -924,13 +1048,25 @@ public class ClassUtil
      */
     public static String internalPackageName(String internalClassName)
     {
-        int lastSeparatorIndex = internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
-        if (lastSeparatorIndex < 0)
-        {
-            lastSeparatorIndex = 0;
-        }
+        String internalPackagePrefix = internalPackagePrefix(internalClassName);
+        int length = internalPackagePrefix.length();
+        return length > 0 ?
+            internalPackagePrefix.substring(0, length - 1) :
+            "";
+    }
 
-        return internalClassName.substring(0, lastSeparatorIndex);
+
+    /**
+     * Returns the internal package prefix of the given internal class name.
+     * @param internalClassName the internal class name,
+     *                          e.g. "<code>java/lang/Object</code>".
+     * @return the internal package prefix,
+     *                          e.g. "<code>java/lang/</code>".
+     */
+    public static String internalPackagePrefix(String internalClassName)
+    {
+        return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
+                                                                            internalClassName.length() - 2) + 1);
     }
 
 
@@ -943,12 +1079,24 @@ public class ClassUtil
      */
     public static String externalPackageName(String externalClassName)
     {
-        int lastSeparatorIndex = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR);
-        if (lastSeparatorIndex < 0)
-        {
-            lastSeparatorIndex = 0;
-        }
+        String externalPackagePrefix = externalPackagePrefix(externalClassName);
+        int length = externalPackagePrefix.length();
+        return length > 0 ?
+            externalPackagePrefix.substring(0, length - 1) :
+            "";
+    }
+
 
-        return externalClassName.substring(0, lastSeparatorIndex);
+    /**
+     * Returns the external package prefix of the given external class name.
+     * @param externalClassName the external class name,
+     *                          e.g. "<code>java.lang.Object</code>".
+     * @return the external package prefix,
+     *                          e.g. "<code>java.lang.</code>".
+     */
+    public static String externalPackagePrefix(String externalClassName)
+    {
+        return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR,
+                                                                            externalClassName.length() - 2) + 1);
     }
 }
diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java
index e834373..336ea7c 100644
--- a/src/proguard/classfile/util/DescriptorClassEnumeration.java
+++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java
@@ -1,6 +1,6 @@
-/* $Id: DescriptorClassEnumeration.java,v 1.12.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 /**
  * A <code>DescriptorClassEnumeration</code> provides an enumeration of all
@@ -153,8 +153,8 @@ public class DescriptorClassEnumeration
             System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
             while (enumeration.hasMoreClassNames())
             {
-                System.out.println("  Name:  ["+enumeration.nextClassName()+"]");
-                System.out.println("  Fluff: ["+enumeration.nextFluff()+"]");
+                System.out.println(" Name:  ["+enumeration.nextClassName()+"]");
+                System.out.println(" Fluff: ["+enumeration.nextFluff()+"]");
             }
         }
         catch (Exception ex)
diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
new file mode 100644
index 0000000..43f4f2f
--- /dev/null
+++ b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java
@@ -0,0 +1,440 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.util.StringMatcher;
+
+/**
+ * This InstructionVisitor initializes any constant <code>Class.forName</code> or
+ * <code>.class</code> references of all classes it visits. More specifically,
+ * it fills out the references of string constant pool entries that refer to a
+ * class in the program class pool or in the library class pool.
+ * <p>
+ * It optionally prints notes if on usage of
+ * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
+ * <p>
+ * The class hierarchy must be initialized before using this visitor.
+ *
+ * @see ClassSuperHierarchyInitializer
+ *
+ * @author Eric Lafortune
+ */
+public class DynamicClassReferenceInitializer
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             AttributeVisitor
+{
+    public static final int X = InstructionSequenceMatcher.X;
+    public static final int Y = InstructionSequenceMatcher.Y;
+    public static final int Z = InstructionSequenceMatcher.Z;
+
+    public static final int A = InstructionSequenceMatcher.A;
+    public static final int B = InstructionSequenceMatcher.B;
+    public static final int C = InstructionSequenceMatcher.C;
+    public static final int D = InstructionSequenceMatcher.D;
+
+
+    private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[]
+    {
+        // 0
+        new MethodrefConstant(1, 2, null, null),
+        new ClassConstant(3, null),
+        new NameAndTypeConstant(4, 5),
+        new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME),
+
+        // 6
+        new MethodrefConstant(1, 7, null, null),
+        new NameAndTypeConstant(8, 9),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE),
+
+        // 10
+        new MethodrefConstant(1, 11, null, null),
+        new NameAndTypeConstant(12, 13),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE),
+    };
+
+    // Class.forName("SomeClass").
+    private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+    };
+
+    // (SomeClass)Class.forName(someName).newInstance().
+    private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6),
+        new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X),
+    };
+
+
+//    private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
+//    {
+//        new MethodrefConstant(A, 1, null, null),
+//        new NameAndTypeConstant(2, 3),
+//        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC),
+//        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC),
+//    };
+
+    private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(A, 1, null, null),
+        new NameAndTypeConstant(B, 2),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC),
+    };
+
+    // SomeClass.class = class$("SomeClass") (javac).
+    private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+    };
+
+
+//    private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
+//    {
+//        new MethodrefConstant(A, 1, null, null),
+//        new NameAndTypeConstant(2, 3),
+//        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES),
+//        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES),
+//    };
+
+    private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(A, 1, null, null),
+        new NameAndTypeConstant(B, 2),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES),
+    };
+
+    // SomeClass.class = class("SomeClass", false) (jikes).
+    private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+    };
+
+    // return Class.forName(v0).
+    private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
+    {
+        new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+        new SimpleInstruction(InstructionConstants.OP_ARETURN),
+    };
+
+    // return Class.forName(v0), if (!v1) .getComponentType().
+    private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[]
+    {
+        new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+        new VariableInstruction(InstructionConstants.OP_ALOAD_1),
+        new BranchInstruction(InstructionConstants.OP_IFNE, +6),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
+        new SimpleInstruction(InstructionConstants.OP_ARETURN),
+    };
+
+    // return Class.forName(v0).getComponentType().
+    private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[]
+    {
+        new VariableInstruction(InstructionConstants.OP_ALOAD_0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10),
+        new SimpleInstruction(InstructionConstants.OP_ARETURN),
+    };
+
+
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter notePrinter;
+    private final StringMatcher  noteExceptionMatcher;
+
+
+    private final InstructionSequenceMatcher constantClassForNameMatcher =
+        new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+                                       CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher classForNameCastMatcher =
+        new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+                                       CLASS_FOR_NAME_CAST_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher dotClassJavacMatcher =
+        new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS,
+                                       DOT_CLASS_JAVAC_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher dotClassJikesMatcher =
+        new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS,
+                                       DOT_CLASS_JIKES_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher dotClassJavacImplementationMatcher =
+        new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+                                       DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher dotClassJikesImplementationMatcher =
+        new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+                                       DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 =
+        new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS,
+                                       DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2);
+
+
+    // A field acting as a return variable for the visitors.
+    private boolean isClassForNameInvocation;
+
+
+    /**
+     * Creates a new DynamicClassReferenceInitializer that optionally prints
+     * notes, with optional class specifications for which never to print notes.
+     */
+    public DynamicClassReferenceInitializer(ClassPool      programClassPool,
+                                            ClassPool      libraryClassPool,
+                                            WarningPrinter notePrinter,
+                                            StringMatcher  noteExceptionMatcher)
+    {
+        this.programClassPool     = programClassPool;
+        this.libraryClassPool     = libraryClassPool;
+        this.notePrinter          = notePrinter;
+        this.noteExceptionMatcher = noteExceptionMatcher;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        // Try to match the Class.forName("SomeClass") construct.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           constantClassForNameMatcher);
+
+        // Did we find a match?
+        if (constantClassForNameMatcher.isMatching())
+        {
+            // Fill out the matched string constant.
+            clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this);
+
+            // Don't look for the dynamic construct.
+            classForNameCastMatcher.reset();
+        }
+
+        // Try to match the (SomeClass)Class.forName(someName).newInstance()
+        // construct.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           classForNameCastMatcher);
+
+        // Did we find a match?
+        if (classForNameCastMatcher.isMatching())
+        {
+            // Print out a note about the construct.
+            clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this);
+        }
+
+        // Try to match the javac .class construct.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           dotClassJavacMatcher);
+
+        // Did we find a match?
+        if (dotClassJavacMatcher.isMatching() &&
+            isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0)))
+        {
+            // Fill out the matched string constant.
+            clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this);
+        }
+
+        // Try to match the jikes .class construct.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           dotClassJikesMatcher);
+
+        // Did we find a match?
+        if (dotClassJikesMatcher.isMatching() &&
+            isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0)))
+        {
+            // Fill out the matched string constant.
+            clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this);
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    /**
+     * Fills out the link to the referenced class.
+     */
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Save a reference to the corresponding class.
+        String externalClassName = stringConstant.getString(clazz);
+        String internalClassName = ClassUtil.internalClassName(externalClassName);
+
+        stringConstant.referencedClass = findClass(internalClassName);
+    }
+
+
+    /**
+     * Prints out a note about the class cast to this class, if applicable.
+     */
+    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))))
+        {
+            notePrinter.print("Note: " +
+                              ClassUtil.externalClassName(clazz.getName()) +
+                              " calls '(" +
+                              ClassUtil.externalClassName(classConstant.getName(clazz)) +
+                              ")Class.forName(variable).newInstance()'");
+        }
+    }
+
+
+    /**
+     * Checks whether the referenced method is a .class method.
+     */
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+    {
+        String methodType = methodrefConstant.getType(clazz);
+
+        // Do the method's class and type match?
+        if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) ||
+            methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES))
+        {
+            String methodName = methodrefConstant.getName(clazz);
+
+            // Does the method's name match one of the special names?
+            isClassForNameInvocation =
+                methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) ||
+                methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES);
+
+            if (isClassForNameInvocation)
+            {
+                return;
+            }
+
+            String className  = methodrefConstant.getClassName(clazz);
+
+            // Note that we look for the class by name, since the referenced
+            // class has not been initialized yet.
+            Clazz referencedClass = programClassPool.getClass(className);
+            if (referencedClass != null)
+            {
+                // Check if the code of the referenced method is .class code.
+                // Note that we look for the method by name and type, since the
+                // referenced method has not been initialized yet.
+                referencedClass.methodAccept(methodName,
+                                             methodType,
+                                             new AllAttributeVisitor(this));
+            }
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Check whether this is class$(String), as generated by javac, or
+        // class(String, boolean), as generated by jikes, or an optimized
+        // version.
+        isClassForNameInvocation =
+            isDotClassMethodCode(clazz, method, codeAttribute,
+                                 dotClassJavacImplementationMatcher, 5)  ||
+            isDotClassMethodCode(clazz, method, codeAttribute,
+                                 dotClassJikesImplementationMatcher, 12) ||
+            isDotClassMethodCode(clazz, method, codeAttribute,
+                                 dotClassJikesImplementationMatcher2, 8);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the given method reference corresponds to a .class
+     * method, as generated by javac or by jikes.
+     */
+    private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)
+    {
+        isClassForNameInvocation = false;
+
+        // Check if the code of the referenced method is .class code.
+        clazz.constantPoolEntryAccept(methodrefConstantIndex, this);
+
+        return isClassForNameInvocation;
+    }
+
+
+    /**
+     * Returns whether the first whether the first instructions of the
+     * given code attribute match with the given instruction matcher.
+     */
+    private boolean isDotClassMethodCode(Clazz                      clazz,
+                                         Method                     method,
+                                         CodeAttribute              codeAttribute,
+                                         InstructionSequenceMatcher codeMatcher,
+                                         int                        codeLength)
+    {
+        // Check the minimum code length.
+        if (codeAttribute.u4codeLength < codeLength)
+        {
+            return false;
+        }
+
+        // Check the actual instructions.
+        codeMatcher.reset();
+        codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher);
+        return codeMatcher.isMatching();
+    }
+
+
+    /**
+     * 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;
+    }
+}
diff --git a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java
new file mode 100644
index 0000000..ff2ed07
--- /dev/null
+++ b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java
@@ -0,0 +1,590 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.visitor.*;
+import proguard.util.StringMatcher;
+
+/**
+ * This InstructionVisitor initializes any constant
+ * <code>Class.get[Declared]{Field,Method}</code> references of all instructions
+ * it visits. More specifically, it fills out the references of string constant
+ * pool entries that refer to a class member in the program class pool or in the
+ * library class pool.
+ * <p>
+ * It optionally prints notes if on usage of
+ * <code>(SomeClass)Class.forName(variable).newInstance()</code>.
+ * <p>
+ * The class hierarchy and references must be initialized before using this
+ * visitor.
+ *
+ * @see ClassSuperHierarchyInitializer
+ * @see ClassReferenceInitializer
+ *
+ * @author Eric Lafortune
+ */
+public class DynamicMemberReferenceInitializer
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             AttributeVisitor,
+             MemberVisitor
+{
+    public static final int X = InstructionSequenceMatcher.X;
+    public static final int Y = InstructionSequenceMatcher.Y;
+    public static final int Z = InstructionSequenceMatcher.Z;
+
+    public static final int A = InstructionSequenceMatcher.A;
+    public static final int B = InstructionSequenceMatcher.B;
+    public static final int C = InstructionSequenceMatcher.C;
+    public static final int D = InstructionSequenceMatcher.D;
+
+
+    private final Constant[] GET_FIELD_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(1, 2, null, null),
+        new ClassConstant(3, null),
+        new NameAndTypeConstant(4, 5),
+        new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_FIELD),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_FIELD),
+    };
+
+    private final Constant[] GET_DECLARED_FIELD_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(1, 2, null, null),
+        new ClassConstant(3, null),
+        new NameAndTypeConstant(4, 5),
+        new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_FIELD),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_FIELD),
+    };
+
+    private final Constant[] GET_METHOD_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(1, 2, null, null),
+        new ClassConstant(3, null),
+        new NameAndTypeConstant(4, 5),
+        new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_METHOD),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_METHOD),
+    };
+
+    private final Constant[] GET_DECLARED_METHOD_CONSTANTS = new Constant[]
+    {
+        new MethodrefConstant(1, 2, null, null),
+        new ClassConstant(3, null),
+        new NameAndTypeConstant(4, 5),
+        new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_METHOD),
+        new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_METHOD),
+    };
+
+    // SomeClass.class.get[Declared]Field("someField").
+    private final Instruction[] CONSTANT_GET_FIELD_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // SomeClass.class.get[Declared]Method("someMethod", new Class[] {}).
+    private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS0 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class }).
+    private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS1 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_LDC, A),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class, B.class }).
+    private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS2 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, X),
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_LDC, A),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+        new ConstantInstruction(InstructionConstants.OP_LDC, B),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // get[Declared]Field("someField").
+    private final Instruction[] GET_FIELD_INSTRUCTIONS = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // get[Declared]Method("someMethod", new Class[] {}).
+    private final Instruction[] GET_METHOD_INSTRUCTIONS0 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // get[Declared]Method("someMethod", new Class[] { A.class }).
+    private final Instruction[] GET_METHOD_INSTRUCTIONS1 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_LDC, A),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+    // get[Declared]Method("someMethod", new Class[] { A.class, B.class }).
+    private final Instruction[] GET_METHOD_INSTRUCTIONS2 = new Instruction[]
+    {
+        new ConstantInstruction(InstructionConstants.OP_LDC, Y),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+        new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+        new ConstantInstruction(InstructionConstants.OP_LDC, A),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new SimpleInstruction(InstructionConstants.OP_DUP),
+        new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+        new ConstantInstruction(InstructionConstants.OP_LDC, B),
+        new SimpleInstruction(InstructionConstants.OP_AASTORE),
+        new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0),
+    };
+
+
+    private final ClassPool      programClassPool;
+    private final ClassPool      libraryClassPool;
+    private final WarningPrinter notePrinter;
+    private final StringMatcher  noteFieldExceptionMatcher;
+    private final StringMatcher  noteMethodExceptionMatcher;
+
+
+    private final InstructionSequenceMatcher constantGetFieldMatcher =
+        new InstructionSequenceMatcher(GET_FIELD_CONSTANTS,
+                                       CONSTANT_GET_FIELD_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher constantGetDeclaredFieldMatcher =
+        new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS,
+                                       CONSTANT_GET_FIELD_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher constantGetMethodMatcher0 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS0);
+
+    private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher0 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS0);
+
+    private final InstructionSequenceMatcher constantGetMethodMatcher1 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS1);
+
+    private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher1 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS1);
+
+    private final InstructionSequenceMatcher constantGetMethodMatcher2 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS2);
+
+    private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher2 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       CONSTANT_GET_METHOD_INSTRUCTIONS2);
+
+    private final InstructionSequenceMatcher getFieldMatcher =
+        new InstructionSequenceMatcher(GET_FIELD_CONSTANTS,
+                                       GET_FIELD_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher getDeclaredFieldMatcher =
+        new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS,
+                                       GET_FIELD_INSTRUCTIONS);
+
+    private final InstructionSequenceMatcher getMethodMatcher0 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS0);
+
+    private final InstructionSequenceMatcher getDeclaredMethodMatcher0 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS0);
+
+    private final InstructionSequenceMatcher getMethodMatcher1 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS1);
+
+    private final InstructionSequenceMatcher getDeclaredMethodMatcher1 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS1);
+
+    private final InstructionSequenceMatcher getMethodMatcher2 =
+        new InstructionSequenceMatcher(GET_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS2);
+
+    private final InstructionSequenceMatcher getDeclaredMethodMatcher2 =
+        new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS,
+                                       GET_METHOD_INSTRUCTIONS2);
+
+    private final MemberFinder memberFinder = new MemberFinder();
+
+
+    // Fields acting as parameters for the visitors.
+    private Clazz   referencedClass;
+    private boolean isDeclared;
+    private boolean isField;
+
+
+
+    /**
+     * Creates a new DynamicMemberReferenceInitializer.
+     */
+    public DynamicMemberReferenceInitializer(ClassPool      programClassPool,
+                                             ClassPool      libraryClassPool,
+                                             WarningPrinter notePrinter,
+                                             StringMatcher  noteFieldExceptionMatcher,
+                                             StringMatcher  noteMethodExceptionMatcher)
+    {
+        this.programClassPool           = programClassPool;
+        this.libraryClassPool           = libraryClassPool;
+        this.notePrinter                = notePrinter;
+        this.noteFieldExceptionMatcher  = noteFieldExceptionMatcher;
+        this.noteMethodExceptionMatcher = noteMethodExceptionMatcher;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        // Try to match the SomeClass.class.getField("someField") construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetFieldMatcher,
+                       getFieldMatcher, true, false);
+
+        // Try to match the SomeClass.class.getDeclaredField("someField") construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetDeclaredFieldMatcher,
+                       getDeclaredFieldMatcher, true, true);
+
+        // Try to match the SomeClass.class.getMethod("someMethod", new Class[]
+        // {}) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetMethodMatcher0,
+                       getMethodMatcher0, false, false);
+
+        // Try to match the SomeClass.class.getDeclaredMethod("someMethod",
+        //  new Class[] {}) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetDeclaredMethodMatcher0,
+                       getDeclaredMethodMatcher0, false, true);
+
+        // Try to match the SomeClass.class.getMethod("someMethod", new Class[]
+        // { A.class }) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetMethodMatcher1,
+                       getMethodMatcher1, false, false);
+
+        // Try to match the SomeClass.class.getDeclaredMethod("someMethod",
+        //  new Class[] { A.class }) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetDeclaredMethodMatcher1,
+                       getDeclaredMethodMatcher1, false, true);
+
+        // Try to match the SomeClass.class.getMethod("someMethod", new Class[]
+        // { A.class, B.class }) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetMethodMatcher2,
+                       getMethodMatcher2, false, false);
+
+        // Try to match the SomeClass.class.getDeclaredMethod("someMethod",
+        //  new Class[] { A.class, B.class }) construct.
+        matchGetMember(clazz, method, codeAttribute, offset, instruction,
+                       constantGetDeclaredMethodMatcher2,
+                       getDeclaredMethodMatcher2, false, true);
+    }
+
+
+    /**
+     * Tries to match the next instruction and fills out the string constant
+     * or prints out a note accordingly.
+     */
+    private void matchGetMember(Clazz                      clazz,
+                                Method                     method,
+                                CodeAttribute              codeAttribute,
+                                int                        offset,
+                                Instruction                instruction,
+                                InstructionSequenceMatcher constantSequenceMatcher,
+                                InstructionSequenceMatcher variableSequenceMatcher,
+                                boolean                    isField,
+                                boolean                    isDeclared)
+    {
+        // Try to match the next instruction in the constant sequence.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           constantSequenceMatcher);
+
+        // Did we find a match to fill out the string constant?
+        if (constantSequenceMatcher.isMatching())
+        {
+            this.isField    = isField;
+            this.isDeclared = isDeclared;
+
+            // Get the member's class.
+            clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(X), this);
+
+            // Fill out the matched string constant.
+            clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(Y), this);
+
+            // Don't look for the dynamic construct.
+            variableSequenceMatcher.reset();
+        }
+
+        // Try to match the next instruction in the variable sequence.
+        instruction.accept(clazz, method, codeAttribute, offset,
+                           variableSequenceMatcher);
+
+        // Did we find a match to print out a note?
+        if (variableSequenceMatcher.isMatching())
+        {
+            // Print out a note about the dynamic invocation.
+            printDynamicInvocationNote(clazz,
+                                       variableSequenceMatcher,
+                                       isField,
+                                       isDeclared);
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    /**
+     * Remembers the referenced class.
+     */
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Remember the referenced class.
+        referencedClass = ClassUtil.isInternalArrayType(classConstant.getName(clazz)) ?
+            null :
+            classConstant.referencedClass;
+    }
+
+
+    /**
+     * Fills out the link to the referenced class member.
+     */
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        if (referencedClass != null)
+        {
+            String name = stringConstant.getString(clazz);
+
+            // See if we can find the referenced class member locally, or
+            // somewhere in the hierarchy.
+            Member referencedMember = isDeclared ? isField ?
+                (Member)referencedClass.findField(name, null) :
+                (Member)referencedClass.findMethod(name, null) :
+                (Member)memberFinder.findMember(clazz,
+                                                referencedClass,
+                                                name,
+                                                null,
+                                                isField);
+            if (referencedMember != null)
+            {
+                stringConstant.referencedMember = referencedMember;
+                stringConstant.referencedClass  = isDeclared ?
+                    referencedClass :
+                    memberFinder.correspondingClass();
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Prints out a note on the matched dynamic invocation, if necessary.
+     */
+    private void printDynamicInvocationNote(Clazz                      clazz,
+                                            InstructionSequenceMatcher noteSequenceMatcher,
+                                            boolean                    isField,
+                                            boolean                    isDeclared)
+    {
+        // Print out a note about the dynamic invocation.
+        if (notePrinter != null)
+        {
+            // Is the class member name in the list of exceptions?
+            StringMatcher noteExceptionMatcher = isField ?
+                noteFieldExceptionMatcher :
+                noteMethodExceptionMatcher;
+
+            int    memberNameIndex = noteSequenceMatcher.matchedConstantIndex(Y);
+            String memberName      = clazz.getStringString(memberNameIndex);
+
+            if (noteExceptionMatcher == null ||
+                !noteExceptionMatcher.matches(memberName))
+            {
+                // Compose the external member name and partial descriptor.
+                String externalMemberDescription = memberName;
+
+                if (!isField)
+                {
+                    externalMemberDescription += '(';
+                    for (int count = 0; count < 2; count++)
+                    {
+                        int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count);
+                        if (memberArgumentIndex > 0)
+                        {
+                            if (count > 0)
+                            {
+                                externalMemberDescription += ',';
+                            }
+                            String className = clazz.getClassName(memberArgumentIndex);
+                            externalMemberDescription += ClassUtil.isInternalArrayType(className) ?
+                                ClassUtil.externalType(className) :
+                                ClassUtil.externalClassName(className);
+                        }
+                    }
+                    externalMemberDescription += ')';
+                }
+
+                // Print out the actual note.
+                notePrinter.print("Note: " +
+                                  ClassUtil.externalClassName(clazz.getName()) +
+                                  " accesses a " +
+                                  (isDeclared ? "declared " : "") +
+                                  (isField    ? "field" : "method") +
+                                  " '" +
+                                  externalMemberDescription +
+                                  "' dynamically");
+
+                // Print out notes about potential candidates.
+                ClassVisitor classVisitor;
+
+                if (isField)
+                {
+                    classVisitor =
+                       new AllFieldVisitor(
+                       new MemberNameFilter(memberName, this));
+                }
+                else
+                {
+                    // Compose the partial method descriptor.
+                    String methodDescriptor = "(";
+                    for (int count = 0; count < 2; count++)
+                    {
+                        int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count);
+                        if (memberArgumentIndex > 0)
+                        {
+                            if (count > 0)
+                            {
+                                methodDescriptor += ',';
+                            }
+                            String className = clazz.getClassName(memberArgumentIndex);
+                            methodDescriptor += ClassUtil.isInternalArrayType(className) ?
+                                className :
+                                ClassUtil.internalTypeFromClassName(className);
+                        }
+                    }
+                    methodDescriptor += ")L///;";
+
+                    classVisitor =
+                        new AllMethodVisitor(
+                        new MemberNameFilter(memberName,
+                        new MemberDescriptorFilter(methodDescriptor, this)));
+                }
+
+                programClassPool.classesAcceptAlphabetically(classVisitor);
+                libraryClassPool.classesAcceptAlphabetically(classVisitor);
+            }
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    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)) +
+                           "; }'");
+    }
+
+
+    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)) +
+                           "; }'");
+    }
+
+
+    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)) +
+                           "; }'");
+    }
+
+
+    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)) +
+                           "; }'");
+    }
+}
diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java
index 9e7abbc..271004f 100644
--- a/src/proguard/classfile/util/ExternalTypeEnumeration.java
+++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java
@@ -1,6 +1,6 @@
-/* $Id: ExternalTypeEnumeration.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 
 /**
diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/src/proguard/classfile/util/InstructionSequenceMatcher.java
new file mode 100644
index 0000000..00e137d
--- /dev/null
+++ b/src/proguard/classfile/util/InstructionSequenceMatcher.java
@@ -0,0 +1,624 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+
+/**
+ * This InstructionVisitor checks whether a given pattern instruction sequence
+ * occurs in the instructions that are visited. The arguments of the
+ * instruction sequence can be wildcards that are matched.
+ *
+ * @author Eric Lafortune
+ */
+public class InstructionSequenceMatcher
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor
+{
+    /*
+    private static       boolean DEBUG      = false;
+    public  static       boolean DEBUG_MORE = false;
+    /*/
+    private static final boolean DEBUG      = false;
+    private static final boolean DEBUG_MORE = false;
+    //*/
+
+    public static final int X = 0x40000000;
+    public static final int Y = 0x40000001;
+    public static final int Z = 0x40000002;
+
+    public static final int A = 0x40000003;
+    public static final int B = 0x40000004;
+    public static final int C = 0x40000005;
+    public static final int D = 0x40000006;
+
+
+    private final Constant[]    patternConstants;
+    private final Instruction[] patternInstructions;
+
+    private boolean matching;
+    private int   patternInstructionIndex;
+    private final int[] matchedInstructionOffsets;
+    private int   matchedArgumentFlags;
+    private final int[] matchedArguments = new int[7];
+    private int   matchedConstantFlags;
+    private final int[] matchedConstantIndices;
+
+    // Fields acting as a parameter and a return value for visitor methods.
+    private Constant patternConstant;
+    private boolean  matchingConstant;
+
+
+    /**
+     * Creates a new InstructionSequenceMatcher.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @param patternInstructions     the pattern instruction sequence.
+     */
+    public InstructionSequenceMatcher(Constant[]    patternConstants,
+                                      Instruction[] patternInstructions)
+    {
+        this.patternConstants    = patternConstants;
+        this.patternInstructions = patternInstructions;
+
+        matchedInstructionOffsets = new int[patternInstructions.length];
+        matchedConstantIndices    = new int[patternConstants.length];
+    }
+
+
+    /**
+     * Starts matching from the first instruction again next time.
+     */
+    public void reset()
+    {
+        patternInstructionIndex = 0;
+        matchedArgumentFlags    = 0;
+        matchedConstantFlags    = 0;
+    }
+
+
+    public boolean isMatching()
+    {
+        return matching;
+    }
+
+
+    public int instructionCount()
+    {
+        return patternInstructions.length;
+    }
+
+
+    public int matchedInstructionOffset(int index)
+    {
+        return matchedInstructionOffsets[index];
+    }
+
+
+    public int matchedArgument(int argument)
+    {
+        int argumentIndex = argument - X;
+        return argumentIndex < 0 ?
+            argument :
+            matchedArguments[argumentIndex];
+    }
+
+
+    public int[] matchedArguments(int[] arguments)
+    {
+        int[] matchedArguments = new int[arguments.length];
+
+        for (int index = 0; index < arguments.length; index++)
+        {
+            matchedArguments[index] = matchedArgument(arguments[index]);
+        }
+
+        return matchedArguments;
+    }
+
+
+    public int matchedConstantIndex(int constantIndex)
+    {
+        int argumentIndex = constantIndex - X;
+        return argumentIndex < 0 ?
+            matchedConstantIndices[constantIndex] :
+            matchedArguments[argumentIndex];
+    }
+
+
+    public int matchedBranchOffset(int offset, int branchOffset)
+    {
+        int argumentIndex = branchOffset - X;
+        return argumentIndex < 0 ?
+            branchOffset :
+            matchedArguments[argumentIndex] - offset;
+    }
+
+
+    public int[] matchedJumpOffsets(int offset, int[] jumpOffsets)
+    {
+        int[] matchedJumpOffsets = new int[jumpOffsets.length];
+
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            matchedJumpOffsets[index] = matchedBranchOffset(offset,
+                                                            jumpOffsets[index]);
+        }
+
+        return matchedJumpOffsets;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the sequence.
+        boolean condition =
+            matchingOpcodes(simpleInstruction, patternInstruction) &&
+            matchingArguments(simpleInstruction.constant,
+                              ((SimpleInstruction)patternInstruction).constant);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   simpleInstruction);
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the sequence.
+        boolean condition =
+            matchingOpcodes(variableInstruction, patternInstruction) &&
+            matchingArguments(variableInstruction.variableIndex,
+                              ((VariableInstruction)patternInstruction).variableIndex) &&
+            matchingArguments(variableInstruction.constant,
+                              ((VariableInstruction)patternInstruction).constant);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   variableInstruction);
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the sequence.
+        boolean condition =
+            matchingOpcodes(constantInstruction, patternInstruction) &&
+            matchingConstantIndices(clazz,
+                                    constantInstruction.constantIndex,
+                                    ((ConstantInstruction)patternInstruction).constantIndex) &&
+            matchingArguments(constantInstruction.constant,
+                              ((ConstantInstruction)patternInstruction).constant);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   constantInstruction);
+    }
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the from
+        // sequence.
+        boolean condition =
+            matchingOpcodes(branchInstruction, patternInstruction) &&
+            matchingBranchOffsets(offset,
+                                  branchInstruction.branchOffset,
+                                  ((BranchInstruction)patternInstruction).branchOffset);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   branchInstruction);
+    }
+
+
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the sequence.
+        boolean condition =
+            matchingOpcodes(tableSwitchInstruction, patternInstruction) &&
+            matchingBranchOffsets(offset,
+                                  tableSwitchInstruction.defaultOffset,
+                                  ((TableSwitchInstruction)patternInstruction).defaultOffset) &&
+            matchingArguments(tableSwitchInstruction.lowCase,
+                              ((TableSwitchInstruction)patternInstruction).lowCase)  &&
+            matchingArguments(tableSwitchInstruction.highCase,
+                              ((TableSwitchInstruction)patternInstruction).highCase) &&
+            matchingJumpOffsets(offset,
+                                tableSwitchInstruction.jumpOffsets,
+                                ((TableSwitchInstruction)patternInstruction).jumpOffsets);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   tableSwitchInstruction);
+    }
+
+
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
+
+        // Check if the instruction matches the next instruction in the sequence.
+        boolean condition =
+            matchingOpcodes(lookUpSwitchInstruction, patternInstruction) &&
+            matchingBranchOffsets(offset,
+                                  lookUpSwitchInstruction.defaultOffset,
+                                  ((LookUpSwitchInstruction)patternInstruction).defaultOffset) &&
+            matchingArguments(lookUpSwitchInstruction.cases,
+                              ((LookUpSwitchInstruction)patternInstruction).cases) &&
+            matchingJumpOffsets(offset,
+                                lookUpSwitchInstruction.jumpOffsets,
+                                ((LookUpSwitchInstruction)patternInstruction).jumpOffsets);
+
+        // Check if the instruction sequence is matching now.
+        checkMatch(condition,
+                   clazz,
+                   method,
+                   codeAttribute,
+                   offset,
+                   lookUpSwitchInstruction);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant;
+
+        // Compare the integer values.
+        matchingConstant = integerConstant.getValue() ==
+                           integerPatternConstant.getValue();
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        LongConstant longPatternConstant = (LongConstant)patternConstant;
+
+        // Compare the long values.
+        matchingConstant = longConstant.getValue() ==
+                           longPatternConstant.getValue();
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        FloatConstant floatPatternConstant = (FloatConstant)patternConstant;
+
+        // Compare the float values.
+        matchingConstant = floatConstant.getValue() ==
+                           floatPatternConstant.getValue();
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant;
+
+        // Compare the double values.
+        matchingConstant = doubleConstant.getValue() ==
+                           doublePatternConstant.getValue();
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        StringConstant stringPatternConstant = (StringConstant)patternConstant;
+
+        // Check the UTF-8 constant.
+        matchingConstant =
+            matchingConstantIndices(clazz,
+                                    stringConstant.u2stringIndex,
+                                    stringPatternConstant.u2stringIndex);
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant;
+
+        // Compare the actual strings.
+        matchingConstant = utf8Constant.getString().equals(
+                           utf8PatternConstant.getString());
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        RefConstant refPatternConstant = (RefConstant)patternConstant;
+
+        // Check the class and the name and type.
+        matchingConstant =
+            matchingConstantIndices(clazz,
+                                    refConstant.getClassIndex(),
+                                    refPatternConstant.getClassIndex()) &&
+            matchingConstantIndices(clazz,
+                                    refConstant.getNameAndTypeIndex(),
+                                    refPatternConstant.getNameAndTypeIndex());
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        ClassConstant classPatternConstant = (ClassConstant)patternConstant;
+
+        // Check the class name.
+        matchingConstant =
+            matchingConstantIndices(clazz,
+                                    classConstant.u2nameIndex,
+                                    classPatternConstant.u2nameIndex);
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant;
+
+        // Check the name and the descriptor.
+        matchingConstant =
+            matchingConstantIndices(clazz,
+                                    nameAndTypeConstant.u2nameIndex,
+                                    typePatternConstant.u2nameIndex) &&
+            matchingConstantIndices(clazz,
+                                    nameAndTypeConstant.u2descriptorIndex,
+                                    typePatternConstant.u2descriptorIndex);
+    }
+
+
+    // Small utility methods.
+
+    private boolean matchingOpcodes(Instruction instruction1,
+                                    Instruction instruction2)
+    {
+        // Check the opcode.
+        return instruction1.opcode            == instruction2.opcode ||
+               instruction1.canonicalOpcode() == instruction2.opcode;
+    }
+
+
+    private boolean matchingArguments(int argument1,
+                                      int argument2)
+    {
+        int argumentIndex = argument2 - X;
+        if (argumentIndex < 0)
+        {
+            // Check the literal argument.
+            return argument1 == argument2;
+        }
+        else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0)
+        {
+            // Store a wildcard argument.
+            matchedArguments[argumentIndex] = argument1;
+            matchedArgumentFlags |= 1 << argumentIndex;
+
+            return true;
+        }
+        else
+        {
+            // Check the previously stored wildcard argument.
+            return matchedArguments[argumentIndex] == argument1;
+        }
+    }
+
+
+    private boolean matchingArguments(int[] arguments1,
+                                      int[] arguments2)
+    {
+        if (arguments1.length != arguments2.length)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < arguments1.length; index++)
+        {
+            if (!matchingArguments(arguments1[index], arguments2[index]))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    private boolean matchingConstantIndices(Clazz clazz,
+                                            int   constantIndex1,
+                                            int   constantIndex2)
+    {
+        if (constantIndex2 >= X)
+        {
+            // Check the constant index.
+            return matchingArguments(constantIndex1, constantIndex2);
+        }
+        else if ((matchedConstantFlags & (1 << constantIndex2)) == 0)
+        {
+            // Check the actual constant.
+            matchingConstant = false;
+            patternConstant  = patternConstants[constantIndex2];
+
+            if (clazz.getTag(constantIndex1) == patternConstant.getTag())
+            {
+                clazz.constantPoolEntryAccept(constantIndex1, this);
+
+                if (matchingConstant)
+                {
+                    // Store the constant index.
+                    matchedConstantIndices[constantIndex2] = constantIndex1;
+                    matchedConstantFlags |= 1 << constantIndex2;
+                }
+            }
+
+            return matchingConstant;
+        }
+        else
+        {
+            // Check a previously stored constant index.
+            return matchedConstantIndices[constantIndex2] == constantIndex1;
+        }
+    }
+
+
+    private boolean matchingBranchOffsets(int offset,
+                                          int branchOffset1,
+                                          int branchOffset2)
+    {
+        int argumentIndex = branchOffset2 - X;
+        if (argumentIndex < 0)
+        {
+            // Check the literal argument.
+            return branchOffset1 == branchOffset2;
+        }
+        else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0)
+        {
+            // Store a wildcard argument.
+            matchedArguments[argumentIndex] = offset + branchOffset1;
+            matchedArgumentFlags |= 1 << argumentIndex;
+
+            return true;
+        }
+        else
+        {
+            // Check the previously stored wildcard argument.
+            return matchedArguments[argumentIndex] == offset + branchOffset1;
+        }
+    }
+
+
+    private boolean matchingJumpOffsets(int   offset,
+                                        int[] jumpOffsets1,
+                                        int[] jumpOffsets2)
+    {
+        if (jumpOffsets1.length != jumpOffsets2.length)
+        {
+            return false;
+        }
+
+        for (int index = 0; index < jumpOffsets1.length; index++)
+        {
+            if (!matchingBranchOffsets(offset,
+                                       jumpOffsets1[index],
+                                       jumpOffsets2[index]))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    private void checkMatch(boolean       condition,
+                            Clazz         clazz,
+                            Method        method,
+                            CodeAttribute codeAttribute,
+                            int           offset,
+                            Instruction   instruction)
+    {
+        if (DEBUG_MORE)
+        {
+            System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t   ")+instruction.toString(offset));
+        }
+
+        // Did the instruction match?
+        if (condition)
+        {
+            // Remember the offset of the matching instruction.
+            matchedInstructionOffsets[patternInstructionIndex] = offset;
+
+            // Try to match the next instruction next time.
+            patternInstructionIndex++;
+
+            // Did we match all instructions in the sequence?
+            matching = patternInstructionIndex == patternInstructions.length;
+
+            if (matching)
+            {
+                if (DEBUG)
+                {
+                    System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+                    for (int index = 0; index < patternInstructionIndex; index++)
+                    {
+                        System.out.println("    "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index]));
+                    }
+                }
+
+                // Start matching from the first instruction again next time.
+                reset();
+            }
+        }
+        else
+        {
+            // The instruction didn't match.
+            matching = false;
+
+            // Is this a failed second instruction?
+            boolean retry = patternInstructionIndex == 1;
+
+            // Start matching from the first instruction next time.
+            reset();
+
+            // Retry a failed second instruction as a first instruction.
+            if (retry)
+            {
+                instruction.accept(clazz, method, codeAttribute, offset, this);
+            }
+        }
+    }
+}
diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java
index 1a884cd..e425b9b 100644
--- a/src/proguard/classfile/util/InternalTypeEnumeration.java
+++ b/src/proguard/classfile/util/InternalTypeEnumeration.java
@@ -1,6 +1,6 @@
-/* $Id: InternalTypeEnumeration.java,v 1.9.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,13 +20,13 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 
 /**
  * An <code>InternalTypeEnumeration</code> provides an enumeration of all
- * types listed in a given internal descriptor string. The return type can
- * retrieved separately.
+ * parameter types listed in a given internal descriptor string. The return type
+ * can retrieved separately.
  * <p>
  * A <code>InternalTypeEnumeration</code> object can be reused for processing
  * different subsequent descriptors, by means of the <code>setDescriptor</code>
diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java
index fec6115..d5c9baa 100644
--- a/src/proguard/classfile/util/MemberFinder.java
+++ b/src/proguard/classfile/util/MemberFinder.java
@@ -1,6 +1,6 @@
-/* $Id: MemberFinder.java,v 1.7.2.5 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -30,79 +30,81 @@ import proguard.classfile.visitor.*;
  * @author Eric Lafortune
  */
 public class MemberFinder
-  implements MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private static class MemberFoundException extends IllegalArgumentException {};
+    private static class MemberFoundException extends RuntimeException {}
     private static final MemberFoundException MEMBER_FOUND = new MemberFoundException();
 
-    private ClassFile  classFile;
-    private MemberInfo memberInfo;
+    private Clazz  clazz;
+    private Member member;
 
 
     /**
      * Finds the field with the given name and descriptor in the given
-     * class file or its hierarchy.
+     * class or its hierarchy.
      */
-    public FieldInfo findField(ClassFile referencingClassFile,
-                               ClassFile classFile,
-                               String    name,
-                               String    descriptor)
+    public Field findField(Clazz  referencingClass,
+                           Clazz  clazz,
+                           String name,
+                           String descriptor)
     {
-        return (FieldInfo)findMember(referencingClassFile, classFile, name, descriptor, true);
+        return (Field)findMember(referencingClass, clazz, name, descriptor, true);
     }
 
 
     /**
      * Finds the method with the given name and descriptor in the given
-     * class file or its hierarchy.
+     * class or its hierarchy.
      */
-    public MethodInfo findMethod(ClassFile referencingClassFile,
-                                 ClassFile classFile,
-                                 String    name,
-                                 String    descriptor)
+    public Method findMethod(Clazz  referencingClass,
+                             Clazz  clazz,
+                             String name,
+                             String descriptor)
     {
-        return (MethodInfo)findMember(referencingClassFile, classFile, name, descriptor, false);
+        return (Method)findMember(referencingClass, clazz, name, descriptor, false);
     }
 
 
     /**
      * Finds the class member with the given name and descriptor in the given
-     * class file or its hierarchy.
+     * class or its hierarchy.
      */
-    public MemberInfo findMember(ClassFile referencingClassFile,
-                                 ClassFile classFile,
-                                 String    name,
-                                 String    descriptor,
-                                 boolean   isField)
+    public Member findMember(Clazz   referencingClass,
+                             Clazz   clazz,
+                             String  name,
+                             String  descriptor,
+                             boolean isField)
     {
         // Organize a search in the hierarchy of superclasses and interfaces.
         // The class member may be in a different class, if the code was
         // compiled with "-target 1.2" or higher (the default in JDK 1.4).
         try
         {
-            this.classFile  = null;
-            this.memberInfo = null;
-            classFile.hierarchyAccept(true, true, true, false, isField ?
-                (ClassFileVisitor)new NamedFieldVisitor(name, descriptor,
-                                  new MemberInfoClassFileAccessFilter(referencingClassFile, this)) :
-                (ClassFileVisitor)new NamedMethodVisitor(name, descriptor,
-                                  new MemberInfoClassFileAccessFilter(referencingClassFile, this)));
+            this.clazz  = null;
+            this.member = null;
+            clazz.hierarchyAccept(true, true, true, false, isField ?
+                (ClassVisitor)new NamedFieldVisitor(name, descriptor,
+                              new MemberClassAccessFilter(referencingClass, this)) :
+                (ClassVisitor)new NamedMethodVisitor(name, descriptor,
+                              new MemberClassAccessFilter(referencingClass, this)));
         }
         catch (MemberFoundException ex)
         {
+            // We've found the member we were looking for.
         }
 
-        return memberInfo;
+        return member;
     }
 
 
     /**
-     * Returns the corresponding class file of the most recently found class
+     * Returns the corresponding class of the most recently found class
      * member.
      */
-    public ClassFile correspondingClassFile()
+    public Clazz correspondingClass()
     {
-        return classFile;
+        return clazz;
     }
 
 
@@ -110,20 +112,21 @@ public class MemberFinder
      * Returns whether the given method is overridden anywhere down the class
      * hierarchy.
      */
-    public boolean isOverriden(ClassFile  classFile,
-                               MethodInfo methodInfo)
+    public boolean isOverriden(Clazz  clazz,
+                               Method method)
     {
-        String name       = methodInfo.getName(classFile);
-        String descriptor = methodInfo.getDescriptor(classFile);
+        String name       = method.getName(clazz);
+        String descriptor = method.getDescriptor(clazz);
 
         // Go looking for the method down the class hierarchy.
         try
         {
-            this.classFile  = null;
-            this.memberInfo = null;
-            classFile.hierarchyAccept(false, false, false, true,
-                                      new NamedMethodVisitor(name, descriptor,
-                                      new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
+            this.clazz  = null;
+            this.member = null;
+
+            clazz.hierarchyAccept(false, false, false, true,
+                new NamedMethodVisitor(name, descriptor,
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
         }
         catch (MemberFoundException ex)
         {
@@ -139,24 +142,24 @@ public class MemberFinder
      * Returns whether the given field is shadowed anywhere down the class
      * hierarchy.
      */
-    public boolean isShadowed(ClassFile classFile,
-                              FieldInfo fieldInfo)
+    public boolean isShadowed(Clazz clazz,
+                              Field field)
     {
-        String name       = fieldInfo.getName(classFile);
-        String descriptor = fieldInfo.getDescriptor(classFile);
+        String name       = field.getName(clazz);
+        String descriptor = field.getDescriptor(clazz);
 
-        // Go looking for the method down the class hierarchy.
+        // Go looking for the field down the class hierarchy.
         try
         {
-            this.classFile  = null;
-            this.memberInfo = null;
-            classFile.hierarchyAccept(false, false, false, true,
-                                      new NamedFieldVisitor(name, descriptor,
-                                      new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
+            this.clazz  = null;
+            this.member = null;
+            clazz.hierarchyAccept(false, false, false, true,
+                new NamedFieldVisitor(name, descriptor,
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this)));
         }
         catch (MemberFoundException ex)
         {
-            // We've found an overriding method.
+            // We've found a shadowing field.
             return true;
         }
 
@@ -164,65 +167,30 @@ public class MemberFinder
     }
 
 
-//    // Implementations for ClassFileVisitor.
-//
-//    public void visitProgramClassFile(ProgramClassFile programClassFile)
-//    {
-//        visitClassFile(programClassFile);
-//    }
+//    // Implementations for ClassVisitor.
 //
-//
-//    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+//    private void visitAnyClass(Clazz clazz)
 //    {
-//        visitClassFile(libraryClassFile);
-//    }
-//
-//
-//    private void visitClassFile(ClassFile classFile)
-//    {
-//        if (memberInfo == null)
+//        if (member == null)
 //        {
-//            memberInfo = isField ?
-//                (MemberInfo)classFile.findField(name, descriptor) :
-//                (MemberInfo)classFile.findMethod(name, descriptor);
+//            member = isField ?
+//                (Member)clazz.findField(name, descriptor) :
+//                (Member)clazz.findMethod(name, descriptor);
 //
-//            if (memberInfo != null)
+//            if (member != null)
 //            {
-//                this.classFile = classFile;
+//                this.clazz = clazz;
 //            }
 //        }
 //    }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        visitMemberInfo(libraryClassFile, libraryFieldInfo);
-    }
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        visitMemberInfo(libraryClassFile, libraryMethodInfo);
-    }
-
+    // Implementations for MemberVisitor.
 
-    private void visitMemberInfo(ClassFile classFile, MemberInfo memberInfo)
+    public void visitAnyMember(Clazz clazz, Member member)
     {
-        this.classFile  = classFile;
-        this.memberInfo = memberInfo;
+        this.clazz  = clazz;
+        this.member = member;
 
         throw MEMBER_FOUND;
     }
diff --git a/src/proguard/classfile/util/MethodInfoLinker.java b/src/proguard/classfile/util/MethodInfoLinker.java
deleted file mode 100644
index 6d5e6a2..0000000
--- a/src/proguard/classfile/util/MethodInfoLinker.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/* $Id: MethodInfoLinker.java,v 1.2.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.util;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-import java.util.*;
-
-/**
- * This ClassFileVisitor links all corresponding non-private methods in the class
- * hierarchies of all visited classes. Visited classes are typically all class
- * files that are not being subclassed. Chains of links that have been created
- * in previous invocations are merged with new chains of links, in order to
- * create a consistent set of chains.
- * <p>
- * As a MemberInfoVisitor, it links all corresponding methods that it ever
- * visits.
- *
- * @author Eric Lafortune
- */
-public class MethodInfoLinker
-  implements ClassFileVisitor,
-             MemberInfoVisitor
-{
-    // An object that is reset and reused every time.
-    // The map: [class member name+' '+descriptor - class member info]
-    private final Map memberInfoMap = new HashMap();
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        visitClassFile(programClassFile);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        visitClassFile(libraryClassFile);
-    }
-
-
-    /**
-     * Links all corresponding methods in the given class file and all of its
-     * super classes and interfaces.
-     */
-    private void visitClassFile(ClassFile classFile)
-    {
-        // Collect all non-private members in this class hierarchy.
-        classFile.hierarchyAccept(true, true, true, false,
-            new AllMemberInfoVisitor(
-            new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-            this)));
-
-        // Clean up for the next class hierarchy.
-        memberInfoMap.clear();
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        visitMemberInfo(libraryClassFile, libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        visitMemberInfo(libraryClassFile, libraryMethodInfo);
-    }
-
-
-    /**
-     * Links the given member into the chains of links. Class initialization
-     * methods and constructors are ignored.
-     * @param classFile  the class file of the given method.
-     * @param memberInfo the method to be linked.
-     */
-    private void visitMemberInfo(ClassFile classFile, MemberInfo memberInfo)
-    {
-        // Get the method's original name and descriptor.
-        String name       = memberInfo.getName(classFile);
-        String descriptor = memberInfo.getDescriptor(classFile);
-
-        // Special cases: <clinit> and <init> are always kept unchanged.
-        // We can ignore them here.
-        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
-            name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
-        {
-            return;
-        }
-
-        // Get the last member in the chain.
-        MemberInfo thisLastMemberInfo = lastMemberInfo(memberInfo);
-
-        // See if we've already come across a member with the same name and
-        // descriptor.
-        String key = name + ' ' + descriptor;
-        MemberInfo otherMemberInfo = (MemberInfo)memberInfoMap.get(key);
-
-        if (otherMemberInfo == null)
-        {
-            // Store the new class member info in the map.
-            memberInfoMap.put(key, thisLastMemberInfo);
-        }
-        else
-        {
-            // Get the last member in the other chain.
-            MemberInfo otherLastMemberInfo = lastMemberInfo(otherMemberInfo);
-
-            // Check if both link chains aren't already ending in the same element.
-            if (!thisLastMemberInfo.equals(otherLastMemberInfo))
-            {
-                // Merge the two chains, with the library members last.
-                if (otherLastMemberInfo instanceof LibraryMemberInfo)
-                {
-                    thisLastMemberInfo.setVisitorInfo(otherLastMemberInfo);
-                }
-                else
-                {
-                    otherLastMemberInfo.setVisitorInfo(thisLastMemberInfo);
-                }
-            }
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Finds the last member in the linked list of related members.
-     * @param memberInfo the given member.
-     * @return the last member in the linked list.
-     */
-    public static MemberInfo lastMemberInfo(MemberInfo memberInfo)
-    {
-        MemberInfo lastMemberInfo = memberInfo;
-        while (lastMemberInfo.getVisitorInfo() != null &&
-               lastMemberInfo.getVisitorInfo() instanceof MemberInfo)
-        {
-            lastMemberInfo = (MemberInfo)lastMemberInfo.getVisitorInfo();
-        }
-
-        return lastMemberInfo;
-    }
-
-    
-    /**
-     * Finds the last visitor accepter in the linked list of visitors.
-     * @param visitorAccepter the given method.
-     * @return the last method in the linked list.
-     */
-    public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter)
-    {
-        VisitorAccepter lastVisitorAccepter = visitorAccepter;
-        while (lastVisitorAccepter.getVisitorInfo() != null &&
-               lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
-        {
-            lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
-        }
-
-        return lastVisitorAccepter;
-    }
-}
diff --git a/src/proguard/classfile/util/MethodLinker.java b/src/proguard/classfile/util/MethodLinker.java
new file mode 100644
index 0000000..2afd59b
--- /dev/null
+++ b/src/proguard/classfile/util/MethodLinker.java
@@ -0,0 +1,154 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.*;
+
+import java.util.*;
+
+/**
+ * This ClassVisitor links all corresponding non-private methods in the class
+ * hierarchies of all visited classes. Visited classes are typically all class
+ * files that are not being subclassed. Chains of links that have been created
+ * in previous invocations are merged with new chains of links, in order to
+ * create a consistent set of chains.
+ * <p>
+ * As a MemberVisitor, it links all corresponding class members that it visits,
+ * including fields and private class members.
+ * <p>
+ * Class initialization methods and constructors are always ignored.
+ *
+ * @author Eric Lafortune
+ */
+public class MethodLinker
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor
+{
+    // An object that is reset and reused every time.
+    // The map: [class member name+' '+descriptor - class member info]
+    private final Map memberMap = new HashMap();
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitAnyClass(Clazz clazz)
+    {
+        // Collect all non-private members in this class hierarchy.
+        clazz.hierarchyAccept(true, true, true, false,
+            new AllMethodVisitor(
+            new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+            this)));
+
+        // Clean up for the next class hierarchy.
+        memberMap.clear();
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitAnyMember(Clazz clazz, Member member)
+    {
+        // Get the class member's name and descriptor.
+        String name       = member.getName(clazz);
+        String descriptor = member.getDescriptor(clazz);
+
+        // Special cases: <clinit> and <init> are always kept unchanged.
+        // We can ignore them here.
+        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
+            name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+        {
+            return;
+        }
+
+        // Get the last method in the chain.
+        Member thisLastMember = lastMember(member);
+
+        // See if we've already come across a method with the same name and
+        // descriptor.
+        String key = name + ' ' + descriptor;
+        Member otherMember = (Member)memberMap.get(key);
+
+        if (otherMember == null)
+        {
+            // Store the new class method in the map.
+            memberMap.put(key, thisLastMember);
+        }
+        else
+        {
+            // Get the last method in the other chain.
+            Member otherLastMember = lastMember(otherMember);
+
+            // Check if both link chains aren't already ending in the same element.
+            if (!thisLastMember.equals(otherLastMember))
+            {
+                // Merge the two chains, with the library members last.
+                if (otherLastMember instanceof LibraryMember)
+                {
+                    thisLastMember.setVisitorInfo(otherLastMember);
+                }
+                else
+                {
+                    otherLastMember.setVisitorInfo(thisLastMember);
+                }
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Finds the last class member in the linked list of related class members.
+     * @param member the given class member.
+     * @return the last class member in the linked list.
+     */
+    public static Member lastMember(Member member)
+    {
+        Member lastMember = member;
+        while (lastMember.getVisitorInfo() != null &&
+               lastMember.getVisitorInfo() instanceof Member)
+        {
+            lastMember = (Member)lastMember.getVisitorInfo();
+        }
+
+        return lastMember;
+    }
+
+
+    /**
+     * Finds the last visitor accepter in the linked list of visitors.
+     * @param visitorAccepter the given method.
+     * @return the last method in the linked list.
+     */
+    public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter)
+    {
+        VisitorAccepter lastVisitorAccepter = visitorAccepter;
+        while (lastVisitorAccepter.getVisitorInfo() != null &&
+               lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
+        {
+            lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
+        }
+
+        return lastVisitorAccepter;
+    }
+}
diff --git a/src/proguard/classfile/util/SimplifiedVisitor.java b/src/proguard/classfile/util/SimplifiedVisitor.java
new file mode 100644
index 0000000..b9fa59f
--- /dev/null
+++ b/src/proguard/classfile/util/SimplifiedVisitor.java
@@ -0,0 +1,810 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.instruction.*;
+
+/**
+ * This abstract utility class allows to implement various visitor interfaces
+ * with simplified methods. The provided methods delegate to other versions
+ * with fewer arguments or more general arguments.
+ *
+ * @author Eric Lafortune
+ * @noinspection AbstractClassWithoutAbstractMethods
+ */
+public abstract class SimplifiedVisitor
+{
+    // Simplifications for ClassVisitor.
+
+    /**
+     * Visits any type of class member of the given class.
+     */
+    public void visitAnyClass(Clazz Clazz)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        visitAnyClass(programClass);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        visitAnyClass(libraryClass);
+    }
+
+
+    // Simplifications for MemberVisitor.
+
+    /**
+     * Visits any type of class member of the given class.
+     */
+    public void visitAnyMember(Clazz clazz, Member member)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    /**
+     * Visits any type of class member of the given program class.
+     */
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        visitAnyMember(programClass, programMember);
+    }
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        visitProgramMember(programClass, programField);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        visitProgramMember(programClass, programMethod);
+    }
+
+
+    /**
+     * Visits any type of class member of the given library class.
+     */
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+    {
+        visitAnyMember(libraryClass, libraryMember);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        visitLibraryMember(libraryClass, libraryField);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        visitLibraryMember(libraryClass, libraryMethod);
+    }
+
+
+    // Simplifications for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        visitAnyConstant(clazz, integerConstant);
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        visitAnyConstant(clazz, longConstant);
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        visitAnyConstant(clazz, floatConstant);
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        visitAnyConstant(clazz, doubleConstant);
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        visitAnyConstant(clazz, stringConstant);
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        visitAnyConstant(clazz, utf8Constant);
+    }
+
+
+    /**
+     * Visits any type of RefConstant of the given class.
+     */
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        visitAnyConstant(clazz, refConstant);
+    }
+
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        visitAnyRefConstant(clazz, fieldrefConstant);
+    }
+
+
+    /**
+     * Visits any type of method RefConstant of the given class.
+     */
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        visitAnyRefConstant(clazz, refConstant);
+    }
+
+
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
+    {
+        visitAnyMethodrefConstant(clazz, interfaceMethodrefConstant);
+    }
+
+
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+    {
+        visitAnyMethodrefConstant(clazz, methodrefConstant);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        visitAnyConstant(clazz, classConstant);
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        visitAnyConstant(clazz, nameAndTypeConstant);
+    }
+
+
+    // Simplifications for AttributeVisitor.
+
+    /**
+     * Visit any type of attribute.
+     */
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        visitAnyAttribute(clazz, unknownAttribute);
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        visitAnyAttribute(clazz, sourceFileAttribute);
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        visitAnyAttribute(clazz, sourceDirAttribute);
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        visitAnyAttribute(clazz, innerClassesAttribute);
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        visitAnyAttribute(clazz, enclosingMethodAttribute);
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        visitAnyAttribute(clazz, deprecatedAttribute);
+    }
+
+
+    /**
+     * Visits the given DeprecatedAttribute of any type of class member.
+     */
+    public void visitDeprecatedAttribute(Clazz clazz, Member member, DeprecatedAttribute deprecatedAttribute)
+    {
+        visitDeprecatedAttribute(clazz, deprecatedAttribute);
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        visitDeprecatedAttribute(clazz, (Member)field, deprecatedAttribute);
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
+    {
+        visitDeprecatedAttribute(clazz, (Member)method, deprecatedAttribute);
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        visitAnyAttribute(clazz, syntheticAttribute);
+    }
+
+
+    /**
+     * Visits the given SyntheticAttribute of any type of class member.
+     */
+    public void visitSyntheticAttribute(Clazz clazz, Member member, SyntheticAttribute syntheticAttribute)
+    {
+        visitSyntheticAttribute(clazz, syntheticAttribute);
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
+    {
+        visitSyntheticAttribute(clazz, (Member)field, syntheticAttribute);
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
+    {
+        visitSyntheticAttribute(clazz, (Member)method, syntheticAttribute);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        visitAnyAttribute(clazz, signatureAttribute);
+    }
+
+
+    /**
+     * Visits the given SignatureAttribute of any type of class member.
+     */
+    public void visitSignatureAttribute(Clazz clazz, Member member, SignatureAttribute signatureAttribute)
+    {
+        visitSignatureAttribute(clazz, signatureAttribute);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute)
+    {
+        visitSignatureAttribute(clazz, (Member)field, signatureAttribute);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
+    {
+        visitSignatureAttribute(clazz, (Member)method, signatureAttribute);
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        visitAnyAttribute(clazz, constantValueAttribute);
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        visitAnyAttribute(clazz, exceptionsAttribute);
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        visitAnyAttribute(clazz, codeAttribute);
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        visitAnyAttribute(clazz, stackMapAttribute);
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        visitAnyAttribute(clazz, stackMapTableAttribute);
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        visitAnyAttribute(clazz, lineNumberTableAttribute);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        visitAnyAttribute(clazz, localVariableTableAttribute);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        visitAnyAttribute(clazz, localVariableTypeTableAttribute);
+    }
+
+
+    /**
+     * Visits any type of AnnotationsAttribute of a class.
+     */
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        visitAnyAttribute(clazz, annotationsAttribute);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        visitAnyAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute);
+    }
+
+
+    /**
+     * Visits the given RuntimeVisibleAnnotationsAttribute of any type of class member.
+     */
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)field, runtimeVisibleAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)method, runtimeVisibleAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        visitAnyAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute);
+    }
+
+
+    /**
+     * Visits the given RuntimeInvisibleAnnotationsAttribute of any type of class member.
+     */
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)field, runtimeInvisibleAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)method, runtimeInvisibleAnnotationsAttribute);
+    }
+
+
+    /**
+     * Visits any type of ParameterAnnotationsAttribute.
+     */
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        visitAnyAttribute(clazz, parameterAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        visitAnyParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute);
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        visitAnyParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute);
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        visitAnyAttribute(clazz, annotationDefaultAttribute);
+    }
+
+
+    // Simplifications for InstructionVisitor.
+
+    /**
+     * Visits any type of Instruction.
+     */
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        visitAnyInstruction(clazz, method, codeAttribute, offset, variableInstruction);
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction);
+    }
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction);
+    }
+
+
+    /**
+     * Visits either type of SwitchInstruction.
+     */
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        visitAnyInstruction(clazz, method, codeAttribute, offset, switchInstruction);
+    }
+
+
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        visitAnySwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction);
+    }
+
+
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        visitAnySwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction);
+    }
+
+
+    // Simplifications for StackMapFrameVisitor.
+
+    /**
+     * Visits any type of VerificationType.
+     */
+    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame)
+    {
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameZeroFrame);
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
+    }
+
+
+    public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame)
+    {
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, lessZeroFrame);
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
+    }
+
+
+    // Simplifications for VerificationTypeVisitor.
+
+    /**
+     * Visits any type of VerificationType.
+     */
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, integerType);
+    }
+
+
+    public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, floatType);
+    }
+
+
+    public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, longType);
+    }
+
+
+    public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, doubleType);
+    }
+
+
+    public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, topType);
+    }
+
+
+    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, objectType);
+    }
+
+
+    public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, nullType);
+    }
+
+
+    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedType);
+    }
+
+
+    public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType)
+    {
+        visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedThisType);
+    }
+
+
+    public void visitStackIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType)
+    {
+        visitIntegerType(clazz, method, codeAttribute, offset, integerType);
+    }
+
+
+    public void visitStackFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType)
+    {
+        visitFloatType(clazz, method, codeAttribute, offset, floatType);
+    }
+
+
+    public void visitStackLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType)
+    {
+        visitLongType(clazz, method, codeAttribute, offset, longType);
+    }
+
+
+    public void visitStackDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType)
+    {
+        visitDoubleType(clazz, method, codeAttribute, offset, doubleType);
+    }
+
+
+    public void visitStackTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType)
+    {
+        visitTopType(clazz, method, codeAttribute, offset, topType);
+    }
+
+
+    public void visitStackObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType)
+    {
+        visitObjectType(clazz, method, codeAttribute, offset, objectType);
+    }
+
+
+    public void visitStackNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType)
+    {
+        visitNullType(clazz, method, codeAttribute, offset, nullType);
+    }
+
+
+    public void visitStackUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType)
+    {
+        visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType);
+    }
+
+
+    public void visitStackUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType)
+    {
+        visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType);
+    }
+
+
+
+    public void visitVariablesIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType)
+    {
+        visitIntegerType(clazz, method, codeAttribute, offset, integerType);
+    }
+
+
+    public void visitVariablesFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType)
+    {
+        visitFloatType(clazz, method, codeAttribute, offset, floatType);
+    }
+
+
+    public void visitVariablesLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType)
+    {
+        visitLongType(clazz, method, codeAttribute, offset, longType);
+    }
+
+
+    public void visitVariablesDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType)
+    {
+        visitDoubleType(clazz, method, codeAttribute, offset, doubleType);
+    }
+
+
+    public void visitVariablesTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType)
+    {
+        visitTopType(clazz, method, codeAttribute, offset, topType);
+    }
+
+
+    public void visitVariablesObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType)
+    {
+        visitObjectType(clazz, method, codeAttribute, offset, objectType);
+    }
+
+
+    public void visitVariablesNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType)
+    {
+        visitNullType(clazz, method, codeAttribute, offset, nullType);
+    }
+
+
+    public void visitVariablesUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType)
+    {
+        visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType);
+    }
+
+
+    public void visitVariablesUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType)
+    {
+        visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType);
+    }
+
+
+    // Simplifications for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    /**
+     * Visits the given Annotation of any type of class member.
+     */
+    public void visitAnnotation(Clazz clazz, Member member, Annotation annotation)
+    {
+        visitAnnotation(clazz, annotation);
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Field field, Annotation annotation)
+    {
+        visitAnnotation(clazz, (Member)field, annotation);
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Method method, Annotation annotation)
+    {
+        visitAnnotation(clazz, (Member)method, annotation);
+    }
+
+
+    public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation)
+    {
+        visitAnnotation(clazz, method, annotation);
+    }
+
+
+    // Simplifications for ElementValueVisitor.
+
+    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+    {
+        throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called");
+    }
+
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        visitAnyElementValue(clazz, annotation, constantElementValue);
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        visitAnyElementValue(clazz, annotation, enumConstantElementValue);
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        visitAnyElementValue(clazz, annotation, classElementValue);
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        visitAnyElementValue(clazz, annotation, annotationElementValue);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        visitAnyElementValue(clazz, annotation, arrayElementValue);
+    }
+}
diff --git a/src/proguard/classfile/util/StringSharer.java b/src/proguard/classfile/util/StringSharer.java
new file mode 100644
index 0000000..d808c78
--- /dev/null
+++ b/src/proguard/classfile/util/StringSharer.java
@@ -0,0 +1,155 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.util;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This ClassVisitor shares strings in the class files that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class StringSharer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             AttributeVisitor
+{
+    // A fields acting as an argument for the visitor methods.
+    private String name;
+    private String type;
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Replace name strings in the constant pool by shared strings.
+        programClass.constantPoolEntriesAccept(this);
+
+        // Replace attribute name strings in the constant pool by internalized
+        // strings.
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Replace the super class name string by the shared name string.
+        Clazz superClass = libraryClass.superClass;
+        if (superClass != null)
+        {
+            libraryClass.superClassName = superClass.getName();
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitAnyStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        Member referencedMember = stringConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            Clazz referencedClass = stringConstant.referencedClass;
+
+            // Put the actual class member's name in the class pool.
+            name = referencedMember.getName(referencedClass);
+            clazz.constantPoolEntryAccept(stringConstant.u2stringIndex, this);
+        }
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            Clazz referencedClass = refConstant.referencedClass;
+
+            // Put the actual class member's name and type strings in the class
+            // pool.
+            name = referencedMember.getName(referencedClass);
+            type = referencedMember.getDescriptor(referencedClass);
+            clazz.constantPoolEntryAccept(refConstant.u2nameAndTypeIndex, this);
+        }
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        if (name != null)
+        {
+            // Put the actual class member's name and type strings in the class
+            // pool.
+            clazz.constantPoolEntryAccept(nameAndTypeConstant.u2nameIndex, this);
+            name = type;
+            clazz.constantPoolEntryAccept(nameAndTypeConstant.u2descriptorIndex, this);
+        }
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        Clazz referencedClass = classConstant.referencedClass;
+        if (referencedClass != null)
+        {
+            // Put the actual class's name string in the class pool.
+            name = referencedClass.getName();
+            clazz.constantPoolEntryAccept(classConstant.u2nameIndex, this);
+        }
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        // Do we have a new string to put into this constant?
+        if (name != null)
+        {
+            // Replace the string, if it's actually the same.
+            if (name.equals(utf8Constant.getString()))
+            {
+                utf8Constant.setString(name);
+            }
+
+            name = null;
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
+    {
+        // Put the internalized attribute's name string in the class pool.
+        name = attribute.getAttributeName(clazz).intern();
+        clazz.constantPoolEntryAccept(attribute.u2attributeNameIndex, this);
+    }
+}
diff --git a/src/proguard/classfile/util/WarningPrinter.java b/src/proguard/classfile/util/WarningPrinter.java
index 7bfa0cd..fd7c18c 100644
--- a/src/proguard/classfile/util/WarningPrinter.java
+++ b/src/proguard/classfile/util/WarningPrinter.java
@@ -1,6 +1,6 @@
-/* $Id: WarningPrinter.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,6 @@
  */
 package proguard.classfile.util;
 
-import proguard.classfile.visitor.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.*;
-
 import java.io.PrintStream;
 
 /**
@@ -34,7 +29,7 @@ import java.io.PrintStream;
  */
 public class WarningPrinter
 {
-    private PrintStream printStream;
+    private final PrintStream printStream;
     private int         warningCount;
 
 
diff --git a/src/proguard/classfile/visitor/AllClassFileVisitor.java b/src/proguard/classfile/visitor/AllClassVisitor.java
similarity index 64%
copy from src/proguard/classfile/visitor/AllClassFileVisitor.java
copy to src/proguard/classfile/visitor/AllClassVisitor.java
index 5e690fa..3e7c863 100644
--- a/src/proguard/classfile/visitor/AllClassFileVisitor.java
+++ b/src/proguard/classfile/visitor/AllClassVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: AllClassFileVisitor.java,v 1.11.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,28 +20,28 @@
  */
 package proguard.classfile.visitor;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassPool;
 
 
 /**
- * This ClassPoolVisitor lets a given ClassFileVisitor visit all ClassFile
+ * This ClassPoolVisitor lets a given ClassVisitor visit all Clazz
  * objects of the class pools it visits.
  *
  * @author Eric Lafortune
  */
-public class AllClassFileVisitor implements ClassPoolVisitor
+public class AllClassVisitor implements ClassPoolVisitor
 {
-    private ClassFileVisitor classFileVisitor;
+    private final ClassVisitor classVisitor;
 
 
-    public AllClassFileVisitor(ClassFileVisitor classFileVisitor)
+    public AllClassVisitor(ClassVisitor classVisitor)
     {
-        this.classFileVisitor = classFileVisitor;
+        this.classVisitor = classVisitor;
     }
 
 
     public void visitClassPool(ClassPool classPool)
     {
-        classPool.classFilesAccept(classFileVisitor);
+        classPool.classesAccept(classVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/AllFieldVisitor.java b/src/proguard/classfile/visitor/AllFieldVisitor.java
index 04b79e0..f9d0d56 100644
--- a/src/proguard/classfile/visitor/AllFieldVisitor.java
+++ b/src/proguard/classfile/visitor/AllFieldVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: AllFieldVisitor.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,32 +24,32 @@ import proguard.classfile.*;
 
 
 /**
- * This ClassFileVisitor lets a given MemberInfoVisitor visit all FieldMemberInfo
- * objects of the class files it visits.
+ * This ClassVisitor lets a given MemberVisitor visit all FieldMember
+ * objects of the classes      it visits.
  *
  * @author Eric Lafortune
  */
-public class AllFieldVisitor implements ClassFileVisitor
+public class AllFieldVisitor implements ClassVisitor
 {
-    private MemberInfoVisitor memberInfoVisitor;
+    private final MemberVisitor memberVisitor;
 
 
-    public AllFieldVisitor(MemberInfoVisitor memberInfoVisitor)
+    public AllFieldVisitor(MemberVisitor memberVisitor)
     {
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        programClassFile.fieldsAccept(memberInfoVisitor);
+        programClass.fieldsAccept(memberVisitor);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        libraryClassFile.fieldsAccept(memberInfoVisitor);
+        libraryClass.fieldsAccept(memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/AllMemberInfoVisitor.java b/src/proguard/classfile/visitor/AllMemberInfoVisitor.java
deleted file mode 100644
index b05027a..0000000
--- a/src/proguard/classfile/visitor/AllMemberInfoVisitor.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/* $Id: AllMemberInfoVisitor.java,v 1.11.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This ClassFileVisitor lets a given MemberInfoVisitor visit all MemberInfo
- * objects of the class files it visits.
- *
- * @author Eric Lafortune
- */
-public class AllMemberInfoVisitor implements ClassFileVisitor
-{
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    public AllMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        programClassFile.fieldsAccept(memberInfoVisitor);
-        programClassFile.methodsAccept(memberInfoVisitor);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        libraryClassFile.fieldsAccept(memberInfoVisitor);
-        libraryClassFile.methodsAccept(memberInfoVisitor);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/visitor/AllMemberVisitor.java
similarity index 54%
copy from src/proguard/classfile/visitor/ClassCounter.java
copy to src/proguard/classfile/visitor/AllMemberVisitor.java
index 892bcbc..843aecb 100644
--- a/src/proguard/classfile/visitor/ClassCounter.java
+++ b/src/proguard/classfile/visitor/AllMemberVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ClassCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,37 +21,37 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
+
 
 /**
- * This ClassFileVisitor counts the number of classes that has been visited.
+ * This ClassVisitor lets a given MemberVisitor visit all Member
+ * objects of the classes      it visits.
  *
  * @author Eric Lafortune
  */
-public class ClassCounter implements ClassFileVisitor
+public class AllMemberVisitor implements ClassVisitor
 {
-    private int count;
+    private final MemberVisitor memberVisitor;
 
 
-    /**
-     * Returns the number of classes that has been visited so far.
-     */
-    public int getCount()
+    public AllMemberVisitor(MemberVisitor memberVisitor)
     {
-        return count;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        count++;
+        programClass.fieldsAccept(memberVisitor);
+        programClass.methodsAccept(memberVisitor);
     }
 
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        count++;
+        libraryClass.fieldsAccept(memberVisitor);
+        libraryClass.methodsAccept(memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/AllMethodVisitor.java b/src/proguard/classfile/visitor/AllMethodVisitor.java
index 2353dac..ace33ed 100644
--- a/src/proguard/classfile/visitor/AllMethodVisitor.java
+++ b/src/proguard/classfile/visitor/AllMethodVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: AllMethodVisitor.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,32 +24,32 @@ import proguard.classfile.*;
 
 
 /**
- * This ClassFileVisitor lets a given MemberInfoVisitor visit all MethodMemberInfo
- * objects of the class files it visits.
+ * This ClassVisitor lets a given MemberVisitor visit all MethodMember
+ * objects of the classes      it visits.
  *
  * @author Eric Lafortune
  */
-public class AllMethodVisitor implements ClassFileVisitor
+public class AllMethodVisitor implements ClassVisitor
 {
-    private MemberInfoVisitor memberInfoVisitor;
+    private final MemberVisitor memberVisitor;
 
 
-    public AllMethodVisitor(MemberInfoVisitor memberInfoVisitor)
+    public AllMethodVisitor(MemberVisitor memberVisitor)
     {
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        programClassFile.methodsAccept(memberInfoVisitor);
+        programClass.methodsAccept(memberVisitor);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        libraryClassFile.methodsAccept(memberInfoVisitor);
+        libraryClass.methodsAccept(memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/BottomClassFileFilter.java b/src/proguard/classfile/visitor/BottomClassFilter.java
similarity index 52%
rename from src/proguard/classfile/visitor/BottomClassFileFilter.java
rename to src/proguard/classfile/visitor/BottomClassFilter.java
index 0f07e0c..3236554 100644
--- a/src/proguard/classfile/visitor/BottomClassFileFilter.java
+++ b/src/proguard/classfile/visitor/BottomClassFilter.java
@@ -1,6 +1,6 @@
-/* $Id: BottomClassFileFilter.java,v 1.6.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,46 +24,46 @@ import proguard.classfile.*;
 
 
 /**
- * This <code>ClassFileVisitor</code> delegates its visits to another given
- * <code>ClassFileVisitor</code>, but only when visiting class files that don't
+ * This <code>ClassVisitor</code> delegates its visits to another given
+ * <code>ClassVisitor</code>, but only when visiting classes that don't
  * have any subclasses.
  *
  * @author Eric Lafortune
  */
-public class BottomClassFileFilter implements ClassFileVisitor
+public class BottomClassFilter implements ClassVisitor
 {
-    private ClassFileVisitor classFileVisitor;
+    private final ClassVisitor classVisitor;
 
 
     /**
-     * Creates a new ProgramClassFileFilter.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
+     * Creates a new ProgramClassFilter.
+     * @param classVisitor     the <code>ClassVisitor</code> to which visits
      *                         will be delegated.
      */
-    public BottomClassFileFilter(ClassFileVisitor classFileVisitor)
+    public BottomClassFilter(ClassVisitor classVisitor)
     {
-        this.classFileVisitor = classFileVisitor;
+        this.classVisitor = classVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Is this a bottom class in the class hierarchy?
-        if (programClassFile.subClasses == null)
+        if (programClass.subClasses == null)
         {
-            classFileVisitor.visitProgramClassFile(programClassFile);
+            classVisitor.visitProgramClass(programClass);
         }
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         // Is this a bottom class in the class hierarchy?
-        if (libraryClassFile.subClasses == null)
+        if (libraryClass.subClasses == null)
         {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+            classVisitor.visitLibraryClass(libraryClass);
         }
     }
 }
diff --git a/src/proguard/classfile/visitor/ClassFileAccessFilter.java b/src/proguard/classfile/visitor/ClassAccessFilter.java
similarity index 56%
rename from src/proguard/classfile/visitor/ClassFileAccessFilter.java
rename to src/proguard/classfile/visitor/ClassAccessFilter.java
index 42b57c4..a6761fa 100644
--- a/src/proguard/classfile/visitor/ClassFileAccessFilter.java
+++ b/src/proguard/classfile/visitor/ClassAccessFilter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassFileAccessFilter.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,56 +24,56 @@ import proguard.classfile.*;
 
 
 /**
- * This <code>ClassFileVisitor</code> delegates its visits to another given
- * <code>ClassFileVisitor</code>, but only when the visited class file
+ * This <code>ClassVisitor</code>     delegates its visits to another given
+ * <code>ClassVisitor</code>, but only when the visited class
  * has the proper access flags.
  *
  * @see ClassConstants
  *
  * @author Eric Lafortune
  */
-public class ClassFileAccessFilter implements ClassFileVisitor
+public class ClassAccessFilter implements ClassVisitor
 {
-    private int              requiredSetAccessFlags;
-    private int              requiredUnsetAccessFlags;
-    private ClassFileVisitor classFileVisitor;
+    private final int          requiredSetAccessFlags;
+    private final int          requiredUnsetAccessFlags;
+    private final ClassVisitor classVisitor;
 
 
     /**
-     * Creates a new ClassFileAccessFilter.
+     * Creates a new ClassAccessFilter.
      * @param requiredSetAccessFlags   the class access flags that should be
      *                                 set.
      * @param requiredUnsetAccessFlags the class access flags that should be
      *                                 unset.
-     * @param classFileVisitor         the <code>ClassFileVisitor</code> to
+     * @param classVisitor             the <code>ClassVisitor</code> to
      *                                 which visits will be delegated.
      */
-    public ClassFileAccessFilter(int              requiredSetAccessFlags,
-                                 int              requiredUnsetAccessFlags,
-                                 ClassFileVisitor classFileVisitor)
+    public ClassAccessFilter(int          requiredSetAccessFlags,
+                             int          requiredUnsetAccessFlags,
+                             ClassVisitor classVisitor)
     {
         this.requiredSetAccessFlags   = requiredSetAccessFlags;
         this.requiredUnsetAccessFlags = requiredUnsetAccessFlags;
-        this.classFileVisitor         = classFileVisitor;
+        this.classVisitor             = classVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        if (accepted(programClassFile.getAccessFlags()))
+        if (accepted(programClass.getAccessFlags()))
         {
-            classFileVisitor.visitProgramClassFile(programClassFile);
+            classVisitor.visitProgramClass(programClass);
         }
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        if (accepted(libraryClassFile.getAccessFlags()))
+        if (accepted(libraryClass.getAccessFlags()))
         {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
+            classVisitor.visitLibraryClass(libraryClass);
         }
     }
 
diff --git a/src/proguard/classfile/visitor/ClassCleaner.java b/src/proguard/classfile/visitor/ClassCleaner.java
new file mode 100644
index 0000000..91e6cee
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassCleaner.java
@@ -0,0 +1,275 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.Constant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This <code>ClassVisitor</code> removes all visitor information of the
+ * classes it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassCleaner
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
+             ExceptionInfoVisitor,
+             InnerClassesInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        clean(programClass);
+
+        programClass.constantPoolEntriesAccept(this);
+
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        clean(libraryClass);
+
+        libraryClass.fieldsAccept(this);
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant)
+    {
+        clean(constant);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        clean(programMember);
+
+        programMember.attributesAccept(programClass, this);
+    }
+
+
+    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+    {
+        clean(libraryMember);
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
+    {
+        clean(attribute);
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        clean(innerClassesAttribute);
+
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        clean(exceptionsAttribute);
+
+        exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this);
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        clean(codeAttribute);
+
+        codeAttribute.exceptionsAccept(clazz, method, this);
+        codeAttribute.attributesAccept(clazz, method, this);
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        clean(stackMapAttribute);
+
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        clean(stackMapTableAttribute);
+
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        clean(annotationsAttribute);
+
+        annotationsAttribute.annotationsAccept(clazz, this);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        clean(parameterAnnotationsAttribute);
+
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        clean(annotationDefaultAttribute);
+
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+    {
+        clean(innerClassesInfo);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        clean(exceptionInfo);
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame)
+    {
+        clean(sameZeroFrame);
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        clean(sameOneFrame);
+        
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame)
+    {
+        clean(lessZeroFrame);
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        clean(moreZeroFrame);
+        
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        clean(fullFrame);
+        
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType)
+    {
+        clean(verificationType);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        clean(annotation);
+
+        annotation.elementValuesAccept(clazz, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+    {
+        clean(elementValue);
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        clean(annotationElementValue);
+
+        annotationElementValue.annotationAccept(clazz, this);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        clean(arrayElementValue);
+    }
+
+
+    // Small utility methods.
+
+    private void clean(VisitorAccepter visitorAccepter)
+    {
+        visitorAccepter.setVisitorInfo(null);
+    }
+}
diff --git a/src/proguard/classfile/FieldInfo.java b/src/proguard/classfile/visitor/ClassCollector.java
similarity index 51%
rename from src/proguard/classfile/FieldInfo.java
rename to src/proguard/classfile/visitor/ClassCollector.java
index d9a4a34..252000f 100644
--- a/src/proguard/classfile/FieldInfo.java
+++ b/src/proguard/classfile/visitor/ClassCollector.java
@@ -1,8 +1,7 @@
-/* $Id: FieldInfo.java,v 1.13.2.2 2007/01/18 21:31:51 eric Exp $
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 1999      Mark Welsh (markw at retrologic.com)
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
  * This library is free software; you can redistribute it and/or modify it
@@ -19,15 +18,41 @@
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.classfile;
+package proguard.classfile.visitor;
 
+import proguard.classfile.Clazz;
+import proguard.classfile.util.SimplifiedVisitor;
 
+import java.util.Set;
 
 /**
- * Representation of a field from a class file.
+ * This <code>ClassVisitor</code> collects the classes that it visits in the
+ * given collection.
  *
  * @author Eric Lafortune
  */
-public interface FieldInfo extends MemberInfo
+public class ClassCollector
+extends      SimplifiedVisitor
+implements   ClassVisitor
 {
+    private final Set set;
+
+
+    /**
+     * Creates a new ClassCollector.
+     * @param set the <code>Set</code> in which all class names will be
+     *            collected.
+     */
+    public ClassCollector(Set set)
+    {
+        this.set = set;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitAnyClass(Clazz clazz)
+    {
+        set.add(clazz);
+    }
 }
diff --git a/src/proguard/classfile/visitor/ClassCounter.java b/src/proguard/classfile/visitor/ClassCounter.java
index 892bcbc..63ec942 100644
--- a/src/proguard/classfile/visitor/ClassCounter.java
+++ b/src/proguard/classfile/visitor/ClassCounter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,14 +21,13 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
 
 /**
- * This ClassFileVisitor counts the number of classes that has been visited.
+ * This ClassVisitor counts the number of classes that has been visited.
  *
  * @author Eric Lafortune
  */
-public class ClassCounter implements ClassFileVisitor
+public class ClassCounter implements ClassVisitor
 {
     private int count;
 
@@ -42,15 +41,15 @@ public class ClassCounter implements ClassFileVisitor
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         count++;
     }
 
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         count++;
     }
diff --git a/src/proguard/classfile/visitor/ClassFileCleaner.java b/src/proguard/classfile/visitor/ClassFileCleaner.java
deleted file mode 100644
index a97a492..0000000
--- a/src/proguard/classfile/visitor/ClassFileCleaner.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/* $Id: ClassFileCleaner.java,v 1.18.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-
-/**
- * This <code>ClassFileVisitor</code> removes all visitor information of the
- * class files it visits.
- *
- * @author Eric Lafortune
- */
-public class ClassFileCleaner
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
-             ExceptionInfoVisitor,
-             InnerClassesInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor
-{
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        clean(programClassFile);
-
-        programClassFile.constantPoolEntriesAccept(this);
-
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        clean(libraryClassFile);
-
-        libraryClassFile.fieldsAccept(this);
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        clean(classCpInfo);
-    }
-
-
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
-    {
-        clean(doubleCpInfo);
-    }
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        clean(fieldrefCpInfo);
-    }
-
-
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
-    {
-        clean(floatCpInfo);
-    }
-
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
-    {
-        clean(integerCpInfo);
-    }
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        clean(interfaceMethodrefCpInfo);
-    }
-
-
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
-    {
-        clean(longCpInfo);
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        clean(methodrefCpInfo);
-    }
-
-
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
-    {
-        clean(nameAndTypeCpInfo);
-    }
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        clean(stringCpInfo);
-    }
-
-
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
-    {
-        clean(utf8CpInfo);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
-    {
-        clean(programMemberInfo);
-
-        programMemberInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        clean(libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        clean(libraryMethodInfo);
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
-    {
-        clean(unknownAttrInfo);
-    }
-
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        clean(innerClassesAttrInfo);
-
-        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
-    }
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        clean(enclosingMethodAttrInfo);
-    }
-
-
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
-    {
-        clean(constantValueAttrInfo);
-    }
-
-
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
-    {
-        clean(exceptionsAttrInfo);
-
-        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
-    }
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        clean(codeAttrInfo);
-
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
-    {
-        clean(lineNumberTableAttrInfo);
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        clean(localVariableTableAttrInfo);
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        clean(localVariableTypeTableAttrInfo);
-    }
-
-
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
-    {
-        clean(sourceFileAttrInfo);
-    }
-
-
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
-    {
-        clean(sourceDirAttrInfo);
-    }
-
-
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
-    {
-        clean(deprecatedAttrInfo);
-    }
-
-
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
-    {
-        clean(syntheticAttrInfo);
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        clean(signatureAttrInfo);
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        clean(runtimeVisibleAnnotationsAttrInfo);
-
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        clean(runtimeInvisibleAnnotationsAttrInfo);
-
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        clean(runtimeVisibleParameterAnnotationsAttrInfo);
-
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        clean(runtimeInvisibleParameterAnnotationsAttrInfo);
-
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        clean(annotationDefaultAttrInfo);
-
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
-    }
-
-
-    // Implementations for InnerClassesInfoVisitor.
-
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
-    {
-        clean(innerClassesInfo);
-    }
-
-
-    // Implementations for ExceptionInfoVisitor.
-
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
-    {
-        clean(exceptionInfo);
-    }
-
-
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
-    {
-        clean(annotation);
-
-        annotation.elementValuesAccept(classFile, this);
-    }
-
-
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
-    {
-        clean(constantElementValue);
-    }
-
-
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        clean(enumConstantElementValue);
-    }
-
-
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
-    {
-        clean(classElementValue);
-    }
-
-
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
-    {
-        clean(annotationElementValue);
-
-        annotationElementValue.annotationAccept(classFile, this);
-    }
-
-
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        clean(arrayElementValue);
-    }
-
-
-    // Small utility methods.
-
-    private void clean(VisitorAccepter visitorAccepter)
-    {
-        visitorAccepter.setVisitorInfo(null);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java
deleted file mode 100644
index 86b06db..0000000
--- a/src/proguard/classfile/visitor/ClassFileHierarchyTraveler.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/* $Id: ClassFileHierarchyTraveler.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> lets a given <code>ClassFileVisitor</code>
- * optionally travel to the visited class, its superclass, its interfaces, and
- * its subclasses.
- *
- * @author Eric Lafortune
- */
-public class ClassFileHierarchyTraveler implements ClassFileVisitor
-{
-    private boolean visitThisClass;
-    private boolean visitSuperClass;
-    private boolean visitInterfaces;
-    private boolean visitSubclasses;
-
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new ClassFileHierarchyTraveler.
-     * @param visitThisClass   specifies whether to visit the originally visited
-     *                         classes.
-     * @param visitSuperClass  specifies whether to visit the super classes of
-     *                         the visited classes.
-     * @param visitInterfaces  specifies whether to visit the interfaces of
-     *                         the visited classes.
-     * @param visitSubclasses  specifies whether to visit the subclasses of
-     *                         the visited classes.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to
-     *                         which visits will be delegated.
-     */
-    public ClassFileHierarchyTraveler(boolean          visitThisClass,
-                                      boolean          visitSuperClass,
-                                      boolean          visitInterfaces,
-                                      boolean          visitSubclasses,
-                                      ClassFileVisitor classFileVisitor)
-    {
-        this.visitThisClass  = visitThisClass;
-        this.visitSuperClass = visitSuperClass;
-        this.visitInterfaces = visitInterfaces;
-        this.visitSubclasses = visitSubclasses;
-
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        programClassFile.hierarchyAccept(visitThisClass,
-                                         visitSuperClass,
-                                         visitInterfaces,
-                                         visitSubclasses,
-                                         classFileVisitor);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        libraryClassFile.hierarchyAccept(visitThisClass,
-                                         visitSuperClass,
-                                         visitInterfaces,
-                                         visitSubclasses,
-                                         classFileVisitor);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java b/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java
deleted file mode 100644
index f618320..0000000
--- a/src/proguard/classfile/visitor/ClassFileMemberInfoVisitor.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/* $Id: ClassFileMemberInfoVisitor.java,v 1.5.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This MemberInfoVisitor delegates all visits to a given ClassFileVisitor.
- * The latter visits the class file of each visited class member, although
- * never twice in a row.
- *
- * @author Eric Lafortune
- */
-public class ClassFileMemberInfoVisitor implements MemberInfoVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-    private ClassFile lastVisitedClassFile;
-
-
-    public ClassFileMemberInfoVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (!programClassFile.equals(lastVisitedClassFile))
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-
-            lastVisitedClassFile = programClassFile;
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (!programClassFile.equals(lastVisitedClassFile))
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-
-            lastVisitedClassFile = programClassFile;
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (!libraryClassFile.equals(lastVisitedClassFile))
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-
-            lastVisitedClassFile = libraryClassFile;
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (!libraryClassFile.equals(lastVisitedClassFile))
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-
-            lastVisitedClassFile = libraryClassFile;
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFileNameFilter.java b/src/proguard/classfile/visitor/ClassFileNameFilter.java
deleted file mode 100644
index a92de54..0000000
--- a/src/proguard/classfile/visitor/ClassFileNameFilter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/* $Id: ClassFileNameFilter.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.util.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> delegates its visits to another given
- * <code>ClassFileVisitor</code>, but only when the visited class file
- * has a name that matches a given regular expression.
- *
- * @author Eric Lafortune
- */
-public class ClassFileNameFilter implements ClassFileVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-    private StringMatcher    regularExpressionMatcher;
-
-
-    /**
-     * Creates a new ClassFileNameFilter.
-     * @param classFileVisitor  the <code>ClassFileVisitor</code> to which visits
-     *                          will be delegated.
-     * @param regularExpression the regular expression against which class names
-     *                          will be matched.
-     */
-    public ClassFileNameFilter(ClassFileVisitor classFileVisitor,
-                               String           regularExpression)
-    {
-        this.classFileVisitor         = classFileVisitor;
-        this.regularExpressionMatcher = new ClassNameListMatcher(regularExpression);
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        if (accepted(programClassFile.getName()))
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        if (accepted(libraryClassFile.getName()))
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-        }
-    }
-
-
-    // Small utility methods.
-
-    private boolean accepted(String name)
-    {
-        return regularExpressionMatcher.matches(name);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFilePresenceFilter.java b/src/proguard/classfile/visitor/ClassFilePresenceFilter.java
deleted file mode 100644
index 96038bb..0000000
--- a/src/proguard/classfile/visitor/ClassFilePresenceFilter.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/* $Id: ClassFilePresenceFilter.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-/**
- * This <code>ClassFileVisitor</code> delegates its visits to one of two
- * <code>ClassFileVisitor</code> instances, depending on whether the name of
- * the visited class file is present in a given <code>ClassPool</code> or not.
- *
- * @author Eric Lafortune
- */
-public class ClassFilePresenceFilter implements ClassFileVisitor
-{
-    private ClassPool        classPool;
-    private ClassFileVisitor presentClassFileVisitor;
-    private ClassFileVisitor missingClassFileVisitor;
-
-
-    /**
-     * Creates a new ClassFilePresenceFilter.
-     * @param classPool               the <code>ClassPool</code> in which the
-     *                                presence will be tested.
-     * @param presentClassFileVisitor the <code>ClassFileVisitor</code> to which
-     *                                visits of present class files will be
-     *                                delegated.
-     * @param missingClassFileVisitor the <code>ClassFileVisitor</code> to which
-     *                                visits of missing class files will be
-     *                                delegated.
-     */
-    public ClassFilePresenceFilter(ClassPool        classPool,
-                                   ClassFileVisitor presentClassFileVisitor,
-                                   ClassFileVisitor missingClassFileVisitor)
-    {
-        this.classPool               = classPool;
-        this.presentClassFileVisitor = presentClassFileVisitor;
-        this.missingClassFileVisitor = missingClassFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        ClassFileVisitor classFileVisitor = classFileVisitor(programClassFile);
-
-        if (classFileVisitor != null)
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        ClassFileVisitor classFileVisitor = classFileVisitor(libraryClassFile);
-
-        if (classFileVisitor != null)
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Returns the appropriate <code>ClassFileVisitor</code>.
-     */
-    private ClassFileVisitor classFileVisitor(ClassFile classFile)
-    {
-        return classPool.getClass(classFile.getName()) != null ?
-            presentClassFileVisitor :
-            missingClassFileVisitor;
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFilePrinter.java b/src/proguard/classfile/visitor/ClassFilePrinter.java
deleted file mode 100644
index 1af6d4a..0000000
--- a/src/proguard/classfile/visitor/ClassFilePrinter.java
+++ /dev/null
@@ -1,680 +0,0 @@
-/* $Id: ClassFilePrinter.java,v 1.31.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-
-import java.io.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> prints out the complete internal
- * structure of the class files it visits.
- *
- * @author Eric Lafortune
- */
-public class ClassFilePrinter
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
-             ExceptionInfoVisitor,
-             InnerClassesInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor,
-             InstructionVisitor
-{
-    private static final String INDENTATION = "  ";
-
-    private PrintStream ps;
-    private int         indentation;
-
-
-    /**
-     * Creates a new ClassFilePrinter that prints to <code>System.out</code>.
-     */
-    public ClassFilePrinter()
-    {
-        this(System.out);
-    }
-
-
-    /**
-     * Creates a new ClassFilePrinter that prints to the given
-     * <code>PrintStream</code>.
-     */
-    public ClassFilePrinter(PrintStream printStream)
-    {
-        ps = printStream;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        println("_____________________________________________________________________");
-        println(visitorInfo(programClassFile) + " CLASS: " + programClassFile.getName());
-        println("Minor version: " + Integer.toHexString(programClassFile.u2minorVersion));
-        println("Major version: " + Integer.toHexString(programClassFile.u2majorVersion));
-        println("Access:        " + ClassUtil.externalClassAccessFlags(programClassFile.u2accessFlags) + "(" + Integer.toHexString(programClassFile.u2accessFlags) + ")");
-        println("Superclass:    " + programClassFile.getSuperName());
-        println();
-
-        println("Interfaces (count = " + programClassFile.u2interfacesCount + "):");
-        indent();
-        for (int i = 0; i < programClassFile.u2interfacesCount; i++)
-        {
-            println("+ " + programClassFile.getCpClassNameString(programClassFile.u2interfaces[i]));
-        }
-        outdent();
-        println();
-
-        println("Constant Pool (count = " + programClassFile.u2constantPoolCount + "):");
-        indent();
-        programClassFile.constantPoolEntriesAccept(this);
-        outdent();
-        println();
-
-        println("Fields (count = " + programClassFile.u2fieldsCount + "):");
-        indent();
-        programClassFile.fieldsAccept(this);
-        outdent();
-        println();
-
-        println("Methods (count = " + programClassFile.u2methodsCount + "):");
-        indent();
-        programClassFile.methodsAccept(this);
-        outdent();
-        println();
-
-        println("Class file attributes (count = " + programClassFile.u2attributesCount + "):");
-        indent();
-        programClassFile.attributesAccept(this);
-        outdent();
-        println();
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        println("_____________________________________________________________________");
-        println(visitorInfo(libraryClassFile) + " LIBRARY CLASS: " + libraryClassFile.getName());
-        println("Access:     "  + ClassUtil.externalClassAccessFlags(libraryClassFile.u2accessFlags) + "(" + Integer.toHexString(libraryClassFile.u2accessFlags) + ")");
-        println("Superclass: "  + libraryClassFile.getSuperName());
-        println();
-
-        println("Interfaces (count = " + libraryClassFile.interfaceClasses.length + "):");
-        for (int i = 0; i < libraryClassFile.interfaceClasses.length; i++)
-        {
-            ClassFile interfaceClass = libraryClassFile.interfaceClasses[i];
-            if (interfaceClass != null)
-            {
-                println("  + " + interfaceClass.getName());
-            }
-        }
-
-        println("Fields (count = " + libraryClassFile.fields.length + "):");
-        libraryClassFile.fieldsAccept(this);
-
-        println("Methods (count = " + libraryClassFile.methods.length + "):");
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        println(visitorInfo(classCpInfo) + " Class [" +
-                classFile.getCpString(classCpInfo.u2nameIndex) + "]");
-    }
-
-
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
-    {
-        println(visitorInfo(doubleCpInfo) + " Double [" +
-                Double.longBitsToDouble(((long)doubleCpInfo.u4highBytes << 32) |
-                                         (long)doubleCpInfo.u4lowBytes) + "]");
-    }
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        println(visitorInfo(fieldrefCpInfo) + " Fieldref [" +
-                classFile.getCpClassNameString(fieldrefCpInfo.u2classIndex)  + "." +
-                classFile.getCpNameString(fieldrefCpInfo.u2nameAndTypeIndex) + " " +
-                classFile.getCpTypeString(fieldrefCpInfo.u2nameAndTypeIndex) + "]");
-    }
-
-
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
-    {
-        println(visitorInfo(floatCpInfo) + " Float [" +
-                Float.intBitsToFloat(floatCpInfo.u4bytes) + "]");
-    }
-
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
-    {
-        println(visitorInfo(integerCpInfo) + " Integer [" +
-                integerCpInfo.u4bytes + "]");
-    }
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        println(visitorInfo(interfaceMethodrefCpInfo) + " InterfaceMethodref [" +
-                classFile.getCpClassNameString(interfaceMethodrefCpInfo.u2classIndex)  + "." +
-                classFile.getCpNameString(interfaceMethodrefCpInfo.u2nameAndTypeIndex) + " " +
-                classFile.getCpTypeString(interfaceMethodrefCpInfo.u2nameAndTypeIndex) + "]");
-    }
-
-
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
-    {
-        println(visitorInfo(longCpInfo) + " Long [" +
-                (((long)longCpInfo.u4highBytes << 32) |
-                  (long)longCpInfo.u4lowBytes) + "]");
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        println(visitorInfo(methodrefCpInfo) + " Methodref [" +
-                classFile.getCpClassNameString(methodrefCpInfo.u2classIndex)  + "." +
-                classFile.getCpNameString(methodrefCpInfo.u2nameAndTypeIndex) + " " +
-                classFile.getCpTypeString(methodrefCpInfo.u2nameAndTypeIndex) + "]");
-    }
-
-
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
-    {
-        println(visitorInfo(nameAndTypeCpInfo) + " NameAndType [" +
-                classFile.getCpString(nameAndTypeCpInfo.u2nameIndex) + " " +
-                classFile.getCpString(nameAndTypeCpInfo.u2descriptorIndex) + "]");
-    }
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        println(visitorInfo(stringCpInfo) + " String [" +
-                classFile.getCpString(stringCpInfo.u2stringIndex) + "]");
-    }
-
-
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
-    {
-        println(visitorInfo(utf8CpInfo) + " Utf8 [" +
-                utf8CpInfo.getString() + "]");
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
-    {
-        println(visitorInfo(programMemberInfo) + " " +
-                programClassFile.getCpString(programMemberInfo.u2nameIndex) + " " +
-                programClassFile.getCpString(programMemberInfo.u2descriptorIndex));
-
-        if (programMemberInfo.u2attributesCount > 0)
-        {
-            indent();
-            println("Class member attributes (count = " + programMemberInfo.u2attributesCount + "):");
-            programMemberInfo.attributesAccept(programClassFile, this);
-            outdent();
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        visitLibraryMemberInfo(libraryClassFile, libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        visitLibraryMemberInfo(libraryClassFile, libraryMethodInfo);
-    }
-
-
-    private void visitLibraryMemberInfo(LibraryClassFile libraryClassFile, LibraryMemberInfo libraryMemberInfo)
-    {
-        println(visitorInfo(libraryMemberInfo) + " " +
-                libraryMemberInfo.getName(libraryClassFile) + " " +
-                libraryMemberInfo.getDescriptor(libraryClassFile));
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-    // Note that attributes are typically only referenced once, so we don't
-    // test if they are marked already.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
-    {
-        println(visitorInfo(unknownAttrInfo) +
-                " Unknown attribute (" + classFile.getCpString(unknownAttrInfo.u2attrNameIndex) + ")");
-    }
-
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        println(visitorInfo(innerClassesAttrInfo) +
-                " Inner classes attribute (count = " + innerClassesAttrInfo.u2numberOfClasses + ")");
-
-        indent();
-        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        println(visitorInfo(enclosingMethodAttrInfo) +
-                " Enclosing method attribute [" +
-                classFile.getCpClassNameString(enclosingMethodAttrInfo.u2classIndex)  +
-                (enclosingMethodAttrInfo.u2nameAndTypeIndex == 0 ? "" : "." +
-                 classFile.getCpNameString(enclosingMethodAttrInfo.u2nameAndTypeIndex) + " " +
-                 classFile.getCpTypeString(enclosingMethodAttrInfo.u2nameAndTypeIndex)) + "]");
-    }
-
-
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
-    {
-        println(visitorInfo(constantValueAttrInfo) +
-                " Constant value attribute:");
-
-        classFile.constantPoolEntryAccept(constantValueAttrInfo.u2constantValueIndex, this);
-    }
-
-
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
-    {
-        println(visitorInfo(exceptionsAttrInfo) +
-                " Exceptions attribute (count = " + exceptionsAttrInfo.u2numberOfExceptions + ")");
-
-        indent();
-        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
-        outdent();
-    }
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        println(visitorInfo(codeAttrInfo) +
-                " Code attribute instructions (code length = "+ codeAttrInfo.u4codeLength +
-                ", locals = "+ codeAttrInfo.u2maxLocals +
-                ", stack = "+ codeAttrInfo.u2maxStack + "):");
-
-        indent();
-
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
-
-        println("Code attribute exceptions (count = " +
-                codeAttrInfo.u2exceptionTableLength + "):");
-
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
-
-        println("Code attribute attributes (attribute count = " +
-                codeAttrInfo.u2attributesCount + "):");
-
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-
-        outdent();
-    }
-
-
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
-    {
-        println(visitorInfo(lineNumberTableAttrInfo) +
-                " Line number table attribute (count = " +
-                lineNumberTableAttrInfo.u2lineNumberTableLength + ")");
-        // ...
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        println(visitorInfo(localVariableTableAttrInfo) +
-                " Local variable table attribute (count = " +
-                localVariableTableAttrInfo.u2localVariableTableLength + ")");
-        // ...
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        println(visitorInfo(localVariableTypeTableAttrInfo) +
-                " Local variable type table attribute (count = "+
-                localVariableTypeTableAttrInfo.u2localVariableTypeTableLength + ")");
-        // ...
-    }
-
-
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
-    {
-        println(visitorInfo(sourceFileAttrInfo) +
-                " Source file attribute:");
-
-        indent();
-        classFile.constantPoolEntryAccept(sourceFileAttrInfo.u2sourceFileIndex, this);
-        outdent();
-    }
-
-
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
-    {
-        println(visitorInfo(sourceDirAttrInfo) +
-                " Source dir attribute:");
-
-        indent();
-        classFile.constantPoolEntryAccept(sourceDirAttrInfo.u2sourceDirIndex, this);
-        outdent();
-    }
-
-
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
-    {
-        println(visitorInfo(deprecatedAttrInfo) +
-                " Deprecated attribute");
-    }
-
-
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
-    {
-        println(visitorInfo(syntheticAttrInfo) +
-                " Synthetic attribute");
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        println(visitorInfo(signatureAttrInfo) +
-                " Signature attribute:");
-
-        indent();
-        classFile.constantPoolEntryAccept(signatureAttrInfo.u2signatureIndex, this);
-        outdent();
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        println(visitorInfo(runtimeVisibleAnnotationsAttrInfo) +
-                " Runtime visible annotation attribute:");
-
-        indent();
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        println(visitorInfo(runtimeInvisibleAnnotationsAttrInfo) +
-                " Runtime invisible annotation attribute:");
-
-        indent();
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        println(visitorInfo(runtimeVisibleParameterAnnotationsAttrInfo) +
-                " Runtime visible parameter annotation attribute:");
-
-        indent();
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        println(visitorInfo(runtimeInvisibleParameterAnnotationsAttrInfo) +
-                " Runtime invisible parameter annotation attribute:");
-
-        indent();
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        println(visitorInfo(annotationDefaultAttrInfo) +
-                " Annotation default attribute:");
-
-        indent();
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
-        outdent();
-    }
-
-
-    // Implementations for InnerClassesInfoVisitor.
-
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
-    {
-        println(visitorInfo(innerClassesInfo) +
-                " InnerClassesInfo:");
-
-        indent();
-        if (innerClassesInfo.u2innerClassInfoIndex != 0)
-        {
-            classFile.constantPoolEntryAccept(innerClassesInfo.u2innerClassInfoIndex, this);
-        }
-
-        if (innerClassesInfo.u2outerClassInfoIndex != 0)
-        {
-            classFile.constantPoolEntryAccept(innerClassesInfo.u2outerClassInfoIndex, this);
-        }
-
-        if (innerClassesInfo.u2innerNameIndex != 0)
-        {
-            classFile.constantPoolEntryAccept(innerClassesInfo.u2innerNameIndex, this);
-        }
-        outdent();
-    }
-
-
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
-    {
-        println(visitorInfo(annotation) +
-                " Annotation [" + classFile.getCpString(annotation.u2typeIndex) + "]:");
-
-        indent();
-        annotation.elementValuesAccept(classFile, this);
-        outdent();
-    }
-
-
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
-    {
-        println(visitorInfo(constantElementValue) +
-                " Constant element value [" +
-                (constantElementValue.u2elementName == 0 ? "(default)" :
-                classFile.getCpString(constantElementValue.u2elementName)) + "]");
-
-        indent();
-        classFile.constantPoolEntryAccept(constantElementValue.u2constantValueIndex, this);
-        outdent();
-    }
-
-
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        println(visitorInfo(enumConstantElementValue) +
-                " Enum constant element value [" +
-                (enumConstantElementValue.u2elementName == 0 ? "(default)" :
-                classFile.getCpString(enumConstantElementValue.u2elementName)) + ", " +
-                classFile.getCpString(enumConstantElementValue.u2typeNameIndex)  + ", " +
-                classFile.getCpString(enumConstantElementValue.u2constantNameIndex) + "]");
-    }
-
-
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
-    {
-        println(visitorInfo(classElementValue) +
-                " Class element value [" +
-                (classElementValue.u2elementName == 0 ? "(default)" :
-                classFile.getCpString(classElementValue.u2elementName)) + ", " +
-                classFile.getCpString(classElementValue.u2classInfoIndex) + "]");
-    }
-
-
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
-    {
-        println(visitorInfo(annotationElementValue) +
-                " Annotation element value [" +
-                (annotationElementValue.u2elementName == 0 ? "(default)" :
-                classFile.getCpString(annotationElementValue.u2elementName)) + "]:");
-
-        indent();
-        annotationElementValue.annotationAccept(classFile, this);
-        outdent();
-    }
-
-
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        println(visitorInfo(arrayElementValue) +
-                " Array element value [" +
-                (arrayElementValue.u2elementName == 0 ? "(default)" :
-                classFile.getCpString(arrayElementValue.u2elementName)) + "]:");
-
-        indent();
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-        outdent();
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-    }
-
-
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-    }
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-
-        indent();
-        classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        outdent();
-    }
-
-
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-    }
-
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-    }
-
-
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
-    {
-        println(InstructionFactory.create(codeAttrInfo.code, offset).toString(offset));
-    }
-
-
-    // Implementations for ExceptionInfoVisitor.
-
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
-    {
-        println(visitorInfo(exceptionInfo) +
-                " ExceptionInfo:");
-
-        if (exceptionInfo.u2catchType != 0)
-        {
-            classFile.constantPoolEntryAccept(exceptionInfo.u2catchType, this);
-        }
-    }
-
-
-    // Small utility methods.
-
-    private void indent()
-    {
-        indentation++;
-    }
-
-    private void outdent()
-    {
-        indentation--;
-    }
-
-    private void println()
-    {
-        ps.println();
-    }
-
-    private void println(String string)
-    {
-        for (int i = 0; i < indentation; i++)
-        {
-            ps.print(INDENTATION);
-        }
-
-        ps.println(string);
-    }
-
-
-    private String visitorInfo(VisitorAccepter visitorAccepter)
-    {
-        return visitorAccepter.getVisitorInfo() == null ? "-" : "+";
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassFileVersionCounter.java b/src/proguard/classfile/visitor/ClassFileVersionCounter.java
deleted file mode 100644
index 6736b2b..0000000
--- a/src/proguard/classfile/visitor/ClassFileVersionCounter.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/* $Id: ClassFileVersionCounter.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-/**
- * This <code>ClassFileVisitor</code> counts the number of visited program class
- * files that have at least the given version number.
- *
- * @author Eric Lafortune
- */
-public class ClassFileVersionCounter implements ClassFileVisitor
-{
-    private int majorVersionNumber;
-    private int minorVersionNumber;
-
-    private int count;
-
-
-    /**
-     * Creates a new ClassFileVersionChecker.
-     * @param majorVersionNumber the major version number.
-     * @param minorVersionNumber the minor version number.
-     */
-    public ClassFileVersionCounter(int majorVersionNumber,
-                                   int minorVersionNumber)
-    {
-        this.majorVersionNumber = majorVersionNumber;
-        this.minorVersionNumber = minorVersionNumber;
-    }
-
-
-    /**
-     * Returns the number of visited program class files with the given minimum
-     * version number.
-     */
-    public int getCount()
-    {
-        return count;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        if ((programClassFile.u2majorVersion == majorVersionNumber &&
-             programClassFile.u2minorVersion >=  minorVersionNumber) ||
-            programClassFile.u2majorVersion > majorVersionNumber)
-        {
-            count++;
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Library class files don't store their version numbers.
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassForNameClassFileVisitor.java b/src/proguard/classfile/visitor/ClassForNameClassFileVisitor.java
deleted file mode 100644
index 2450ac6..0000000
--- a/src/proguard/classfile/visitor/ClassForNameClassFileVisitor.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $Id: ClassForNameClassFileVisitor.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.util.*;
-
-
-/**
- * This CpInfoVisitor lets a given <code>ClassFileVisitor</code> visit all
- * constant classes involved in any <code>Class.forName</code> constructs
- * that it visits.
- *
- * @see DotClassClassFileVisitor
- *
- * @author Eric Lafortune
- */
-public class ClassForNameClassFileVisitor implements CpInfoVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new ClassForNameClassFileVisitor
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to which
-     *                         visits will be delegated.
-     */
-    public ClassForNameClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        // Visit the referenced class from the ClassFile.forName construct,
-        // if any.
-        stringCpInfo.referencedClassAccept(classFileVisitor);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassForNameClassVisitor.java b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java
new file mode 100644
index 0000000..b4d163a
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassForNameClassVisitor.java
@@ -0,0 +1,66 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+
+/**
+ * This ConstantVisitor lets a given <code>ClassVisitor</code> visit all
+ * constant classes involved in any <code>Class.forName</code> constructs that
+ * it visits.
+ *
+ * @see DotClassClassVisitor
+ *
+ * @author Eric Lafortune
+ */
+public class ClassForNameClassVisitor
+extends      SimplifiedVisitor
+implements   ConstantVisitor
+{
+    private final ClassVisitor classVisitor;
+
+
+    /**
+     * Creates a new ClassHierarchyTraveler.
+     * @param classVisitor the <code>ClassVisitor</code> to which visits will
+     *                     be delegated.
+     */
+    public ClassForNameClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Visit the referenced class from the Class.forName construct, if any.
+        stringConstant.referencedClassAccept(classVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassHierarchyTraveler.java b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java
new file mode 100644
index 0000000..c248d3c
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassHierarchyTraveler.java
@@ -0,0 +1,91 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>ClassVisitor</code> lets a given <code>ClassVisitor</code>
+ * optionally travel to the visited class, its superclass, its interfaces, and
+ * its subclasses.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassHierarchyTraveler implements ClassVisitor
+{
+    private final boolean visitThisClass;
+    private final boolean visitSuperClass;
+    private final boolean visitInterfaces;
+    private final boolean visitSubclasses;
+
+    private final ClassVisitor classVisitor;
+
+
+    /**
+     * Creates a new ClassHierarchyTraveler.
+     * @param visitThisClass  specifies whether to visit the originally visited
+     *                        classes.
+     * @param visitSuperClass specifies whether to visit the super classes of
+     *                        the visited classes.
+     * @param visitInterfaces specifies whether to visit the interfaces of
+     *                        the visited classes.
+     * @param visitSubclasses specifies whether to visit the subclasses of
+     *                        the visited classes.
+     * @param classVisitor    the <code>ClassVisitor</code> to
+     *                        which visits will be delegated.
+     */
+    public ClassHierarchyTraveler(boolean      visitThisClass,
+                                  boolean      visitSuperClass,
+                                  boolean      visitInterfaces,
+                                  boolean      visitSubclasses,
+                                  ClassVisitor classVisitor)
+    {
+        this.visitThisClass  = visitThisClass;
+        this.visitSuperClass = visitSuperClass;
+        this.visitInterfaces = visitInterfaces;
+        this.visitSubclasses = visitSubclasses;
+
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        programClass.hierarchyAccept(visitThisClass,
+                                     visitSuperClass,
+                                     visitInterfaces,
+                                     visitSubclasses,
+                                     classVisitor);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        libraryClass.hierarchyAccept(visitThisClass,
+                                     visitSuperClass,
+                                     visitInterfaces,
+                                     visitSubclasses,
+                                     classVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassMemberVisitor.java b/src/proguard/classfile/visitor/ClassMemberVisitor.java
new file mode 100644
index 0000000..6735773
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassMemberVisitor.java
@@ -0,0 +1,88 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberVisitor delegates all visits to a given ClassVisitor.
+ * The latter visits the class  of each visited class member, although
+ * never twice in a row.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassMemberVisitor implements MemberVisitor
+{
+    private final ClassVisitor classVisitor;
+
+    private Clazz lastVisitedClass;
+
+
+    public ClassMemberVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (!programClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitProgramClass(programClass);
+
+            lastVisitedClass = programClass;
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (!programClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitProgramClass(programClass);
+
+            lastVisitedClass = programClass;
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (!libraryClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+
+            lastVisitedClass = libraryClass;
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (!libraryClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+
+            lastVisitedClass = libraryClass;
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassNameFilter.java b/src/proguard/classfile/visitor/ClassNameFilter.java
new file mode 100644
index 0000000..0d3f578
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassNameFilter.java
@@ -0,0 +1,80 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+/**
+ * This <code>ClassVisitor</code> delegates its visits to another given
+ * <code>ClassVisitor</code>, but only when the visited class has a name that
+ * matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassNameFilter implements ClassVisitor
+{
+    private final StringMatcher regularExpressionMatcher;
+    private final ClassVisitor  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(String       regularExpression,
+                           ClassVisitor classVisitor)
+    {
+        this.regularExpressionMatcher = new ListParser(new ClassNameParser()).parse(regularExpression);
+        this.classVisitor             = classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (accepted(programClass.getName()))
+        {
+            classVisitor.visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        if (accepted(libraryClass.getName()))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/ClassPoolFiller.java
index ddde8ab..2c5e813 100644
--- a/src/proguard/classfile/visitor/ClassPoolFiller.java
+++ b/src/proguard/classfile/visitor/ClassPoolFiller.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPoolFiller.java,v 1.9.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,18 +21,20 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.util.*;
+import proguard.classfile.util.SimplifiedVisitor;
 
 
 /**
- * This ClassFileVisitor collects all the class files it visits in a given
+ * This ClassVisitor collects all the classes it visits in a given
  * class pool.
  *
  * @author Eric Lafortune
  */
-public class ClassPoolFiller implements ClassFileVisitor
+public class ClassPoolFiller
+extends      SimplifiedVisitor
+implements   ClassVisitor
 {
-    private ClassPool classPool;
+    private final ClassPool classPool;
 
 
     /**
@@ -44,16 +46,10 @@ public class ClassPoolFiller implements ClassFileVisitor
     }
 
 
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        classPool.addClass(programClassFile);
-    }
-
+    // Implementations for ClassVisitor.
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitAnyClass(Clazz clazz)
     {
-        classPool.addClass(libraryClassFile);
+        classPool.addClass(clazz);
     }
 }
diff --git a/src/proguard/classfile/visitor/ClassPoolVisitor.java b/src/proguard/classfile/visitor/ClassPoolVisitor.java
index 1579a7e..b81ab89 100644
--- a/src/proguard/classfile/visitor/ClassPoolVisitor.java
+++ b/src/proguard/classfile/visitor/ClassPoolVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPoolVisitor.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.classfile.visitor;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassPool;
 
 
 /**
diff --git a/src/proguard/classfile/visitor/ClassPresenceFilter.java b/src/proguard/classfile/visitor/ClassPresenceFilter.java
new file mode 100644
index 0000000..0f8250d
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassPresenceFilter.java
@@ -0,0 +1,93 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+/**
+ * This <code>ClassVisitor</code> delegates its visits to one of two
+ * <code>ClassVisitor</code> instances, depending on whether the name of
+ * the visited class file is present in a given <code>ClassPool</code> or not.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPresenceFilter implements ClassVisitor
+{
+    private final ClassPool    classPool;
+    private final ClassVisitor presentClassVisitor;
+    private final ClassVisitor missingClassVisitor;
+
+
+    /**
+     * Creates a new ClassPresenceFilter.
+     * @param classPool           the <code>ClassPool</code> in which the
+     *                            presence will be tested.
+     * @param presentClassVisitor the <code>ClassVisitor</code> to which visits
+     *                            of present class files will be delegated.
+     * @param missingClassVisitor the <code>ClassVisitor</code> to which visits
+     *                            of missing class files will be delegated.
+     */
+    public ClassPresenceFilter(ClassPool    classPool,
+                               ClassVisitor presentClassVisitor,
+                               ClassVisitor missingClassVisitor)
+    {
+        this.classPool           = classPool;
+        this.presentClassVisitor = presentClassVisitor;
+        this.missingClassVisitor = missingClassVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        ClassVisitor classFileVisitor = classFileVisitor(programClass);
+
+        if (classFileVisitor != null)
+        {
+            classFileVisitor.visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        ClassVisitor classFileVisitor = classFileVisitor(libraryClass);
+
+        if (classFileVisitor != null)
+        {
+            classFileVisitor.visitLibraryClass(libraryClass);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns the appropriate <code>ClassVisitor</code>.
+     */
+    private ClassVisitor classFileVisitor(Clazz clazz)
+    {
+        return classPool.getClass(clazz.getName()) != null ?
+            presentClassVisitor :
+            missingClassVisitor;
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassPrinter.java b/src/proguard/classfile/visitor/ClassPrinter.java
new file mode 100644
index 0000000..9538b28
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassPrinter.java
@@ -0,0 +1,956 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+
+import java.io.PrintStream;
+
+
+/**
+ * This <code>ClassVisitor</code> prints out the complete internal
+ * structure of the classes it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassPrinter
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
+             ExceptionInfoVisitor,
+             InnerClassesInfoVisitor,
+             StackMapFrameVisitor,
+             VerificationTypeVisitor,
+             LineNumberInfoVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor,
+             InstructionVisitor
+{
+    private static final String INDENTATION = "  ";
+
+    private final PrintStream ps;
+    private int         indentation;
+
+
+    /**
+     * Creates a new ClassPrinter that prints to <code>System.out</code>.
+     */
+    public ClassPrinter()
+    {
+        this(System.out);
+    }
+
+
+    /**
+     * Creates a new ClassPrinter that prints to the given
+     * <code>PrintStream</code>.
+     */
+    public ClassPrinter(PrintStream printStream)
+    {
+        ps = printStream;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        println("_____________________________________________________________________");
+        println(visitorInfo(programClass) + " " +
+                "Program class: " + programClass.getName());
+        indent();
+        println("Superclass:    " + programClass.getSuperName());
+        println("Major version: 0x" + Integer.toHexString(ClassUtil.internalMajorClassVersion(programClass.u4version)));
+        println("Minor version: 0x" + Integer.toHexString(ClassUtil.internalMinorClassVersion(programClass.u4version)));
+        println("Access flags:  0x" + Integer.toHexString(programClass.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalClassAccessFlags(programClass.u2accessFlags) +
+                ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ? "class " : "") +
+                ClassUtil.externalClassName(programClass.getName()) +
+                (programClass.u2superClass == 0 ? "" : " extends " +
+                ClassUtil.externalClassName(programClass.getSuperName())));
+        outdent();
+        println();
+
+        println("Interfaces (count = " + programClass.u2interfacesCount + "):");
+        indent();
+        for (int index = 0; index < programClass.u2interfacesCount; index++)
+        {
+            println("+ " + programClass.getClassName(programClass.u2interfaces[index]));
+        }
+        outdent();
+        println();
+
+        println("Constant Pool (count = " + programClass.u2constantPoolCount + "):");
+        indent();
+        programClass.constantPoolEntriesAccept(this);
+        outdent();
+        println();
+
+        println("Fields (count = " + programClass.u2fieldsCount + "):");
+        indent();
+        programClass.fieldsAccept(this);
+        outdent();
+        println();
+
+        println("Methods (count = " + programClass.u2methodsCount + "):");
+        indent();
+        programClass.methodsAccept(this);
+        outdent();
+        println();
+
+        println("Class file attributes (count = " + programClass.u2attributesCount + "):");
+        indent();
+        programClass.attributesAccept(this);
+        outdent();
+        println();
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        println("_____________________________________________________________________");
+        println(visitorInfo(libraryClass) + " " +
+                "Library class: " + libraryClass.getName());
+        indent();
+        println("Superclass:    " + libraryClass.getSuperName());
+        println("Access flags:  0x" + Integer.toHexString(libraryClass.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalClassAccessFlags(libraryClass.u2accessFlags) +
+                ((libraryClass.u2accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ? "class " : "") +
+                ClassUtil.externalClassName(libraryClass.getName()) + " extends "  +
+                ClassUtil.externalClassName(libraryClass.getSuperName()));
+        outdent();
+        println();
+
+        println("Interfaces (count = " + libraryClass.interfaceClasses.length + "):");
+        for (int index = 0; index < libraryClass.interfaceClasses.length; index++)
+        {
+            Clazz interfaceClass = libraryClass.interfaceClasses[index];
+            if (interfaceClass != null)
+            {
+                println("  + " + interfaceClass.getName());
+            }
+        }
+
+        println("Fields (count = " + libraryClass.fields.length + "):");
+        libraryClass.fieldsAccept(this);
+
+        println("Methods (count = " + libraryClass.methods.length + "):");
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
+    {
+        println(visitorInfo(integerConstant) + " Integer [" +
+                integerConstant.getValue() + "]");
+    }
+
+
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
+    {
+        println(visitorInfo(longConstant) + " Long [" +
+                longConstant.getValue() + "]");
+    }
+
+
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
+    {
+        println(visitorInfo(floatConstant) + " Float [" +
+                floatConstant.getValue() + "]");
+    }
+
+
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
+    {
+        println(visitorInfo(doubleConstant) + " Double [" +
+                doubleConstant.getValue() + "]");
+    }
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        println(visitorInfo(stringConstant) + " String [" +
+                clazz.getString(stringConstant.u2stringIndex) + "]");
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        println(visitorInfo(utf8Constant) + " Utf8 [" +
+                utf8Constant.getString() + "]");
+    }
+
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        println(visitorInfo(fieldrefConstant) + " Fieldref [" +
+                clazz.getClassName(fieldrefConstant.u2classIndex)  + "." +
+                clazz.getName(fieldrefConstant.u2nameAndTypeIndex) + " " +
+                clazz.getType(fieldrefConstant.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
+    {
+        println(visitorInfo(interfaceMethodrefConstant) + " InterfaceMethodref [" +
+                clazz.getClassName(interfaceMethodrefConstant.u2classIndex)  + "." +
+                clazz.getName(interfaceMethodrefConstant.u2nameAndTypeIndex) + " " +
+                clazz.getType(interfaceMethodrefConstant.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+    {
+        println(visitorInfo(methodrefConstant) + " Methodref [" +
+                clazz.getClassName(methodrefConstant.u2classIndex)  + "." +
+                clazz.getName(methodrefConstant.u2nameAndTypeIndex) + " " +
+                clazz.getType(methodrefConstant.u2nameAndTypeIndex) + "]");
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        println(visitorInfo(classConstant) + " Class [" +
+                clazz.getString(classConstant.u2nameIndex) + "]");
+    }
+
+
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
+    {
+        println(visitorInfo(nameAndTypeConstant) + " NameAndType [" +
+                clazz.getString(nameAndTypeConstant.u2nameIndex) + " " +
+                clazz.getString(nameAndTypeConstant.u2descriptorIndex) + "]");
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        println(visitorInfo(programField) + " " +
+                "Field:        " +
+                programField.getName(programClass) + " " +
+                programField.getDescriptor(programClass));
+
+        indent();
+        println("Access flags: 0x" + Integer.toHexString(programField.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalFullFieldDescription(programField.u2accessFlags,
+                                                       programField.getName(programClass),
+                                                       programField.getDescriptor(programClass)));
+
+        visitMember(programClass, programField);
+        outdent();
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        println(visitorInfo(programMethod) + " " +
+                "Method:       " +
+                programMethod.getName(programClass) +
+                programMethod.getDescriptor(programClass));
+
+        indent();
+        println("Access flags: 0x" + Integer.toHexString(programMethod.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalFullMethodDescription(programClass.getName(),
+                                                        programMethod.u2accessFlags,
+                                                        programMethod.getName(programClass),
+                                                        programMethod.getDescriptor(programClass)));
+
+        visitMember(programClass, programMethod);
+        outdent();
+    }
+
+
+    private void visitMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        if (programMember.u2attributesCount > 0)
+        {
+            println("Class member attributes (count = " + programMember.u2attributesCount + "):");
+            programMember.attributesAccept(programClass, this);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        println(visitorInfo(libraryField) + " " +
+                "Field:        " +
+                libraryField.getName(libraryClass) + " " +
+                libraryField.getDescriptor(libraryClass));
+
+        indent();
+        println("Access flags: 0x" + Integer.toHexString(libraryField.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalFullFieldDescription(libraryField.u2accessFlags,
+                                                       libraryField.getName(libraryClass),
+                                                       libraryField.getDescriptor(libraryClass)));
+        outdent();
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        println(visitorInfo(libraryMethod) + " " +
+                "Method:       " +
+                libraryMethod.getName(libraryClass) + " " +
+                libraryMethod.getDescriptor(libraryClass));
+
+        indent();
+        println("Access flags: 0x" + Integer.toHexString(libraryMethod.u2accessFlags));
+        println("  = " +
+                ClassUtil.externalFullMethodDescription(libraryClass.getName(),
+                                                        libraryMethod.u2accessFlags,
+                                                        libraryMethod.getName(libraryClass),
+                                                        libraryMethod.getDescriptor(libraryClass)));
+        outdent();
+    }
+
+
+    // Implementations for AttributeVisitor.
+    // Note that attributes are typically only referenced once, so we don't
+    // test if they are marked already.
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        println(visitorInfo(unknownAttribute) +
+                " Unknown attribute (" + clazz.getString(unknownAttribute.u2attributeNameIndex) + ")");
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        println(visitorInfo(sourceFileAttribute) +
+                " Source file attribute:");
+
+        indent();
+        clazz.constantPoolEntryAccept(sourceFileAttribute.u2sourceFileIndex, this);
+        outdent();
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        println(visitorInfo(sourceDirAttribute) +
+                " Source dir attribute:");
+
+        indent();
+        clazz.constantPoolEntryAccept(sourceDirAttribute.u2sourceDirIndex, this);
+        outdent();
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        println(visitorInfo(innerClassesAttribute) +
+                " Inner classes attribute (count = " + innerClassesAttribute.u2classesCount + ")");
+
+        indent();
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
+        outdent();
+    }
+
+
+    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)) + "]");
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        println(visitorInfo(deprecatedAttribute) +
+                " Deprecated attribute");
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        println(visitorInfo(syntheticAttribute) +
+                " Synthetic attribute");
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        println(visitorInfo(signatureAttribute) +
+                " Signature attribute:");
+
+        indent();
+        clazz.constantPoolEntryAccept(signatureAttribute.u2signatureIndex, this);
+        outdent();
+    }
+
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        println(visitorInfo(constantValueAttribute) +
+                " Constant value attribute:");
+
+        clazz.constantPoolEntryAccept(constantValueAttribute.u2constantValueIndex, this);
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        println(visitorInfo(exceptionsAttribute) +
+                " Exceptions attribute (count = " + exceptionsAttribute.u2exceptionIndexTableLength + ")");
+
+        indent();
+        exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this);
+        outdent();
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        println(visitorInfo(codeAttribute) +
+                " Code attribute instructions (code length = "+ codeAttribute.u4codeLength +
+                ", locals = "+ codeAttribute.u2maxLocals +
+                ", stack = "+ codeAttribute.u2maxStack + "):");
+
+        indent();
+
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        println("Code attribute exceptions (count = " +
+                codeAttribute.u2exceptionTableLength + "):");
+
+        codeAttribute.exceptionsAccept(clazz, method, this);
+
+        println("Code attribute attributes (attribute count = " +
+                codeAttribute.u2attributesCount + "):");
+
+        codeAttribute.attributesAccept(clazz, method, this);
+
+        outdent();
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
+    {
+        println(visitorInfo(codeAttribute) +
+                " Stack map attribute (count = "+
+                stackMapAttribute.u2stackMapFramesCount + "):");
+
+        indent();
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+        outdent();
+    }
+
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
+    {
+        println(visitorInfo(codeAttribute) +
+                " Stack map table attribute (count = "+
+                stackMapTableAttribute.u2stackMapFramesCount + "):");
+
+        indent();
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
+        outdent();
+    }
+
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
+    {
+        println(visitorInfo(lineNumberTableAttribute) +
+                " Line number table attribute (count = " +
+                lineNumberTableAttribute.u2lineNumberTableLength + ")");
+
+        indent();
+        lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
+        outdent();
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        println(visitorInfo(localVariableTableAttribute) +
+                " Local variable table attribute (count = " +
+                localVariableTableAttribute.u2localVariableTableLength + ")");
+
+        indent();
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+        outdent();
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        println(visitorInfo(localVariableTypeTableAttribute) +
+                " Local variable type table attribute (count = "+
+                localVariableTypeTableAttribute.u2localVariableTypeTableLength + ")");
+
+        indent();
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
+    {
+        println(visitorInfo(runtimeVisibleAnnotationsAttribute) +
+                " Runtime visible annotations attribute:");
+
+        indent();
+        runtimeVisibleAnnotationsAttribute.annotationsAccept(clazz, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
+    {
+        println(visitorInfo(runtimeInvisibleAnnotationsAttribute) +
+                " Runtime invisible annotations attribute:");
+
+        indent();
+        runtimeInvisibleAnnotationsAttribute.annotationsAccept(clazz, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
+    {
+        println(visitorInfo(runtimeVisibleParameterAnnotationsAttribute) +
+                " Runtime visible parameter annotations attribute:");
+
+        indent();
+        runtimeVisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+        outdent();
+    }
+
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
+    {
+        println(visitorInfo(runtimeInvisibleParameterAnnotationsAttribute) +
+                " Runtime invisible parameter annotations attribute:");
+
+        indent();
+        runtimeInvisibleParameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+        outdent();
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        println(visitorInfo(annotationDefaultAttribute) +
+                " Annotation default attribute:");
+
+        indent();
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+        outdent();
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+    {
+        println(visitorInfo(innerClassesInfo) +
+                " InnerClassesInfo:");
+
+        indent();
+        if (innerClassesInfo.u2innerClassIndex != 0)
+        {
+            clazz.constantPoolEntryAccept(innerClassesInfo.u2innerClassIndex, this);
+        }
+
+        if (innerClassesInfo.u2outerClassIndex != 0)
+        {
+            clazz.constantPoolEntryAccept(innerClassesInfo.u2outerClassIndex, this);
+        }
+
+        if (innerClassesInfo.u2innerNameIndex != 0)
+        {
+            clazz.constantPoolEntryAccept(innerClassesInfo.u2innerNameIndex, this);
+        }
+        outdent();
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        println(instruction.toString(offset));
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        println(constantInstruction.toString(offset));
+
+        indent();
+        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+        outdent();
+    }
+
+
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
+    {
+        println(tableSwitchInstruction.toString(offset));
+
+        indent();
+
+        int[] jumpOffsets = tableSwitchInstruction.jumpOffsets;
+
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            int jumpOffset = jumpOffsets[index];
+            println(Integer.toString(tableSwitchInstruction.lowCase + index)  + ": offset = " + jumpOffset + ", target = " + (offset + jumpOffset));
+        }
+
+        int defaultOffset = tableSwitchInstruction.defaultOffset;
+        println("default: offset = " + defaultOffset + ", target = "+ (offset + defaultOffset));
+
+        outdent();
+    }
+
+
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    {
+        println(lookUpSwitchInstruction.toString(offset));
+
+        indent();
+
+        int[] cases       = lookUpSwitchInstruction.cases;
+        int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets;
+
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            int jumpOffset = jumpOffsets[index];
+            println(Integer.toString(cases[index])  + ": offset = " + jumpOffset + ", target = " + (offset + jumpOffset));
+        }
+
+        int defaultOffset = lookUpSwitchInstruction.defaultOffset;
+        println("default: offset = " + defaultOffset + ", target = "+ (offset + defaultOffset));
+
+        outdent();
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        println(visitorInfo(exceptionInfo) +
+                " ExceptionInfo (" +
+                exceptionInfo.u2startPC + " -> " +
+                exceptionInfo.u2endPC + ": " +
+                exceptionInfo.u2handlerPC + "):");
+
+        if (exceptionInfo.u2catchType != 0)
+        {
+            clazz.constantPoolEntryAccept(exceptionInfo.u2catchType, this);
+        }
+    }
+
+
+    // Implementations for StackMapFrameVisitor.
+
+    public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame)
+    {
+        println(visitorInfo(sameZeroFrame) +
+                " [" + offset  + "]" +
+                " Var: ..., Stack: (empty)");
+    }
+
+
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
+    {
+        print(visitorInfo(sameOneFrame) +
+              " [" + offset  + "]" +
+              " Var: ..., Stack: ");
+
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
+
+        println();
+    }
+
+
+    public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame)
+    {
+        println(visitorInfo(lessZeroFrame) +
+                " [" + offset  + "]" +
+                " Var: -" + lessZeroFrame.choppedVariablesCount +
+                ", Stack: (empty)");
+    }
+
+
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
+    {
+        print(visitorInfo(moreZeroFrame) +
+              " [" + offset  + "]" +
+              " Var: ...");
+
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
+
+        ps.println(", Stack: (empty)");
+    }
+
+
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
+    {
+        print(visitorInfo(fullFrame) +
+              " [" + offset  + "]" +
+              " Var: ");
+
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+
+        ps.print(", Stack: ");
+
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
+
+        println();
+    }
+
+
+    // Implementations for VerificationTypeVisitor.
+
+    public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType)
+    {
+        ps.print("[i]");
+    }
+
+
+    public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType)
+    {
+        ps.print("[f]");
+    }
+
+
+    public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType)
+    {
+        ps.print("[l]");
+    }
+
+
+    public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType)
+    {
+        ps.print("[d]");
+    }
+
+
+    public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType)
+    {
+        ps.print("[T]");
+    }
+
+
+    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
+    {
+        ps.print("[a:" + clazz.getClassName(objectType.u2classIndex) + "]");
+    }
+
+
+    public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType)
+    {
+        ps.print("[n]");
+    }
+
+
+    public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
+    {
+        ps.print("[u:" + uninitializedType.u2newInstructionOffset + "]");
+    }
+
+
+    public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType)
+    {
+        ps.print("[u:this]");
+    }
+
+
+    // Implementations for LineNumberInfoVisitor.
+
+    public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
+    {
+        println("[" + lineNumberInfo.u2startPC + "] -> line " +
+                lineNumberInfo.u2lineNumber);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        println("#" + localVariableInfo.u2index + ": " +
+                localVariableInfo.u2startPC + " -> " +
+                (localVariableInfo.u2startPC + localVariableInfo.u2length) + " [" +
+                clazz.getString(localVariableInfo.u2descriptorIndex) + " " +
+                clazz.getString(localVariableInfo.u2nameIndex) + "]");
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        println("#" + localVariableTypeInfo.u2index + ": " +
+                localVariableTypeInfo.u2startPC + " -> " +
+                (localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length) + " [" +
+                clazz.getString(localVariableTypeInfo.u2signatureIndex) + " " +
+                clazz.getString(localVariableTypeInfo.u2nameIndex) + "]");
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        println(visitorInfo(annotation) +
+                " Annotation [" + clazz.getString(annotation.u2typeIndex) + "]:");
+
+        indent();
+        annotation.elementValuesAccept(clazz, this);
+        outdent();
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        println(visitorInfo(constantElementValue) +
+                " Constant element value [" +
+                (constantElementValue.u2elementNameIndex == 0 ? "(default)" :
+                clazz.getString(constantElementValue.u2elementNameIndex)) + " '" +
+                constantElementValue.u1tag + "']");
+
+        indent();
+        clazz.constantPoolEntryAccept(constantElementValue.u2constantValueIndex, this);
+        outdent();
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        println(visitorInfo(enumConstantElementValue) +
+                " Enum constant element value [" +
+                (enumConstantElementValue.u2elementNameIndex == 0 ? "(default)" :
+                clazz.getString(enumConstantElementValue.u2elementNameIndex)) + ", " +
+                clazz.getString(enumConstantElementValue.u2typeNameIndex)  + ", " +
+                clazz.getString(enumConstantElementValue.u2constantNameIndex) + "]");
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        println(visitorInfo(classElementValue) +
+                " Class element value [" +
+                (classElementValue.u2elementNameIndex == 0 ? "(default)" :
+                clazz.getString(classElementValue.u2elementNameIndex)) + ", " +
+                clazz.getString(classElementValue.u2classInfoIndex) + "]");
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        println(visitorInfo(annotationElementValue) +
+                " Annotation element value [" +
+                (annotationElementValue.u2elementNameIndex == 0 ? "(default)" :
+                clazz.getString(annotationElementValue.u2elementNameIndex)) + "]:");
+
+        indent();
+        annotationElementValue.annotationAccept(clazz, this);
+        outdent();
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        println(visitorInfo(arrayElementValue) +
+                " Array element value [" +
+                (arrayElementValue.u2elementNameIndex == 0 ? "(default)" :
+                clazz.getString(arrayElementValue.u2elementNameIndex)) + "]:");
+
+        indent();
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+        outdent();
+    }
+
+
+    // Small utility methods.
+
+    private void indent()
+    {
+        indentation++;
+    }
+
+    private void outdent()
+    {
+        indentation--;
+    }
+
+    private void println(String string)
+    {
+        print(string);
+        println();
+
+    }
+
+    private void print(String string)
+    {
+        for (int index = 0; index < indentation; index++)
+        {
+            ps.print(INDENTATION);
+        }
+
+        ps.print(string);
+    }
+
+    private void println()
+    {
+        ps.println();
+    }
+
+
+    private String visitorInfo(VisitorAccepter visitorAccepter)
+    {
+        return visitorAccepter.getVisitorInfo() == null ? "-" : "+";
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassVersionFilter.java b/src/proguard/classfile/visitor/ClassVersionFilter.java
new file mode 100644
index 0000000..ae0fe43
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassVersionFilter.java
@@ -0,0 +1,72 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+/**
+ * This <code>ClassVisitor</code> delegates its visits to program classes to
+ * another given <code>ClassVisitor</code>, but only when the class version
+ * number of the visited program class lies in a given range.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassVersionFilter implements ClassVisitor
+{
+    private final int          minimumClassVersion;
+    private final int          maximumClassVersion;
+    private final ClassVisitor classVisitor;
+
+
+    /**
+     * Creates a new ClassVersionFilter.
+     * @param minimumClassVersion the minimum class version number.
+     * @param maximumClassVersion the maximum class version number.
+     * @param classVisitor        the <code>ClassVisitor</code> to which visits
+     *                            will be delegated.
+     */
+    public ClassVersionFilter(int          minimumClassVersion,
+                              int          maximumClassVersion,
+                              ClassVisitor classVisitor)
+    {
+        this.minimumClassVersion = minimumClassVersion;
+        this.maximumClassVersion = maximumClassVersion;
+        this.classVisitor        = classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (programClass.u4version >= minimumClassVersion &&
+            programClass.u4version <= maximumClassVersion)
+        {
+            classVisitor.visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Library classes don't have version numbers.
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassVersionSetter.java b/src/proguard/classfile/visitor/ClassVersionSetter.java
new file mode 100644
index 0000000..d441333
--- /dev/null
+++ b/src/proguard/classfile/visitor/ClassVersionSetter.java
@@ -0,0 +1,83 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+import java.util.Set;
+
+/**
+ * This <code>ClassVisitor</code> sets the version number of the program classes
+ * that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassVersionSetter implements ClassVisitor
+{
+    private final int classVersion;
+
+    private final Set newerClassVersions;
+
+
+    /**
+     * Creates a new ClassVersionSetter.
+     * @param classVersion the class version number.
+     */
+    public ClassVersionSetter(int classVersion)
+    {
+        this(classVersion, null);
+    }
+
+
+    /**
+     * Creates a new ClassVersionSetter that also stores any newer class version
+     * numbers that it encounters while visiting program classes.
+     * @param classVersion       the class version number.
+     * @param newerClassVersions the <code>Set</code> in which newer class
+     *                           version numbers can be collected.
+     */
+    public ClassVersionSetter(int classVersion,
+                              Set newerClassVersions)
+    {
+        this.classVersion       = classVersion;
+        this.newerClassVersions = newerClassVersions;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (programClass.u4version > classVersion &&
+            newerClassVersions != null)
+        {
+            newerClassVersions.add(new Integer(programClass.u4version));
+        }
+
+        programClass.u4version = classVersion;
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Library classes don't have version numbers.
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileVisitor.java b/src/proguard/classfile/visitor/ClassVisitor.java
similarity index 72%
copy from src/proguard/classfile/visitor/ClassFileVisitor.java
copy to src/proguard/classfile/visitor/ClassVisitor.java
index 79aff22..3e215b1 100644
--- a/src/proguard/classfile/visitor/ClassFileVisitor.java
+++ b/src/proguard/classfile/visitor/ClassVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ClassFileVisitor.java,v 1.8.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -25,12 +25,12 @@ import proguard.classfile.*;
 
 /**
  * This interface specifies the methods for a visitor of
- * <code>ClassFile</code> objects.
+ * <code>Clazz</code> objects.
  *
  * @author Eric Lafortune
  */
-public interface ClassFileVisitor
+public interface ClassVisitor
 {
-    public void visitProgramClassFile(ProgramClassFile programClassFile);
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile);
+    public void visitProgramClass(ProgramClass programClass);
+    public void visitLibraryClass(LibraryClass libraryClass);
 }
diff --git a/src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
similarity index 55%
rename from src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java
rename to src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
index ce3b059..09376ef 100644
--- a/src/proguard/classfile/visitor/ConcreteClassFileDownTraveler.java
+++ b/src/proguard/classfile/visitor/ConcreteClassDownTraveler.java
@@ -1,6 +1,6 @@
-/* $Id: ConcreteClassFileDownTraveler.java,v 1.8.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,77 +24,77 @@ import proguard.classfile.*;
 
 
 /**
- * This <code>ClassFileVisitor</code> lets a given <code>ClassFileVisitor</code>
+ * This <code>ClassVisitor</code> lets a given <code>ClassVisitor</code>
  * travel to the first concrete subclasses down in its hierarchy of abstract
  * classes and concrete classes.
  *
  * @author Eric Lafortune
  */
-public class ConcreteClassFileDownTraveler
-  implements ClassFileVisitor
+public class ConcreteClassDownTraveler
+implements   ClassVisitor
 {
-    private ClassFileVisitor classFileVisitor;
+    private final ClassVisitor classVisitor;
 
 
     /**
-     * Creates a new ConcreteClassFileDownTraveler.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to
+     * Creates a new ConcreteClassDownTraveler.
+     * @param classVisitor     the <code>ClassVisitor</code> to
      *                         which visits will be delegated.
      */
-    public ConcreteClassFileDownTraveler(ClassFileVisitor classFileVisitor)
+    public ConcreteClassDownTraveler(ClassVisitor classVisitor)
     {
-        this.classFileVisitor = classFileVisitor;
+        this.classVisitor = classVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Is this an abstract class or an interface?
-        if ((programClassFile.getAccessFlags() &
+        if ((programClass.getAccessFlags() &
              (ClassConstants.INTERNAL_ACC_INTERFACE |
               ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
         {
             // Travel down the hierarchy.
-            ClassFile[] subClasses = programClassFile.subClasses;
+            Clazz[] subClasses = programClass.subClasses;
             if (subClasses != null)
             {
-                for (int i = 0; i < subClasses.length; i++)
+                for (int index = 0; index < subClasses.length; index++)
                 {
-                    subClasses[i].accept(this);
+                    subClasses[index].accept(this);
                 }
             }
         }
         else
         {
-            // Visit the class file. Don't descend any further.
-            programClassFile.accept(classFileVisitor);
+            // Visit the class. Don't descend any further.
+            programClass.accept(classVisitor);
         }
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         // Is this an abstract class or interface?
-        if ((libraryClassFile.getAccessFlags() &
+        if ((libraryClass.getAccessFlags() &
              (ClassConstants.INTERNAL_ACC_INTERFACE |
               ClassConstants.INTERNAL_ACC_ABSTRACT)) != 0)
         {
             // Travel down the hierarchy.
-            ClassFile[] subClasses = libraryClassFile.subClasses;
+            Clazz[] subClasses = libraryClass.subClasses;
             if (subClasses != null)
             {
-                for (int i = 0; i < subClasses.length; i++)
+                for (int index = 0; index < subClasses.length; index++)
                 {
-                    subClasses[i].accept(this);
+                    subClasses[index].accept(this);
                 }
             }
         }
         else
         {
-            // Visit the class file. Don't descend any further.
-            libraryClassFile.accept(classFileVisitor);
+            // Visit the class. Don't descend any further.
+            libraryClass.accept(classVisitor);
         }
     }
 }
diff --git a/src/proguard/classfile/visitor/CpInfoVisitor.java b/src/proguard/classfile/visitor/CpInfoVisitor.java
deleted file mode 100644
index 43a7939..0000000
--- a/src/proguard/classfile/visitor/CpInfoVisitor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/* $Id: CpInfoVisitor.java,v 1.8.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This interface specifies the methods for a visitor of <code>CpInfo</code>
- * objects.
- *
- * @author Eric Lafortune
- */
-public interface CpInfoVisitor
-{
-    public void visitIntegerCpInfo(           ClassFile classFile, IntegerCpInfo            integerCpInfo);
-    public void visitLongCpInfo(              ClassFile classFile, LongCpInfo               longCpInfo);
-    public void visitFloatCpInfo(             ClassFile classFile, FloatCpInfo              floatCpInfo);
-    public void visitDoubleCpInfo(            ClassFile classFile, DoubleCpInfo             doubleCpInfo);
-    public void visitStringCpInfo(            ClassFile classFile, StringCpInfo             stringCpInfo);
-    public void visitUtf8CpInfo(              ClassFile classFile, Utf8CpInfo               utf8CpInfo);
-    public void visitFieldrefCpInfo(          ClassFile classFile, FieldrefCpInfo           fieldrefCpInfo);
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo);
-    public void visitMethodrefCpInfo(         ClassFile classFile, MethodrefCpInfo          methodrefCpInfo);
-    public void visitClassCpInfo(             ClassFile classFile, ClassCpInfo              classCpInfo);
-    public void visitNameAndTypeCpInfo(       ClassFile classFile, NameAndTypeCpInfo        nameAndTypeCpInfo);
-}
diff --git a/src/proguard/classfile/visitor/DotClassClassFileVisitor.java b/src/proguard/classfile/visitor/DotClassClassFileVisitor.java
deleted file mode 100644
index 087edd3..0000000
--- a/src/proguard/classfile/visitor/DotClassClassFileVisitor.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* $Id: DotClassClassFileVisitor.java,v 1.1.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.instruction.*;
-import proguard.classfile.util.*;
-
-/**
- * This InstructionVisitor lets a given <code>ClassFileVisitor</code> visit all
- * classes involved in any <code>.class</code> constructs that it visits.
- * <p>
- * Note that before JDK 1.5, <code>.class</code> constructs are actually
- * compiled differently, using <code>Class.forName</code> constructs.
- *
- * @see ClassForNameClassFileVisitor
- *
- * @author Eric Lafortune
- */
-public class DotClassClassFileVisitor
-  implements InstructionVisitor,
-             CpInfoVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new DotClassClassFileVisitor.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to which
-     *                         visits will be delegated.
-     */
-    public DotClassClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        byte opcode = cpInstruction.opcode;
-
-        // Could this instruction be a .class construct?
-        if (opcode == InstructionConstants.OP_LDC ||
-            opcode == InstructionConstants.OP_LDC_W)
-        {
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex,
-                                              this);
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        // Visit the referenced class from the .class construct.
-        classCpInfo.referencedClassAccept(classFileVisitor);
-    }
-}
diff --git a/src/proguard/classfile/visitor/DotClassClassVisitor.java b/src/proguard/classfile/visitor/DotClassClassVisitor.java
new file mode 100644
index 0000000..cd1cbf9
--- /dev/null
+++ b/src/proguard/classfile/visitor/DotClassClassVisitor.java
@@ -0,0 +1,91 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+
+/**
+ * This InstructionVisitor lets a given <code>ClassVisitor</code> visit all
+ * classes involved in any <code>.class</code> constructs that it visits.
+ * <p>
+ * Note that before JDK 1.5, <code>.class</code> constructs are actually
+ * compiled differently, using <code>Class.forName</code> constructs.
+ *
+ * @see ClassForNameClassVisitor
+ *
+ * @author Eric Lafortune
+ */
+public class DotClassClassVisitor
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor
+{
+    private final ClassVisitor classVisitor;
+
+
+    /**
+     * Creates a new ClassHierarchyTraveler.
+     * @param classVisitor the <code>ClassVisitor</code> to which visits will
+     *                     be delegated.
+     */
+    public DotClassClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    // 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)
+    {
+        byte opcode = constantInstruction.opcode;
+
+        // Could this instruction be a .class construct?
+        if (opcode == InstructionConstants.OP_LDC ||
+            opcode == InstructionConstants.OP_LDC_W)
+        {
+            clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
+                                          this);
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Visit the referenced class from the .class construct.
+        classConstant.referencedClassAccept(classVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/AllCpInfoVisitor.java b/src/proguard/classfile/visitor/ExceptionCounter.java
similarity index 55%
rename from src/proguard/classfile/visitor/AllCpInfoVisitor.java
rename to src/proguard/classfile/visitor/ExceptionCounter.java
index a8dafc5..578287d 100644
--- a/src/proguard/classfile/visitor/AllCpInfoVisitor.java
+++ b/src/proguard/classfile/visitor/ExceptionCounter.java
@@ -1,6 +1,6 @@
-/* $Id: AllCpInfoVisitor.java,v 1.3.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,32 +21,32 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.ExceptionInfoVisitor;
 
 /**
- * This ClassFileVisitor lets a given CpInfoVisitor visit all constant pool
- * entries of the program class files it visits.
+ * This ExceptionInfoVisitor counts the number of exceptions that has been visited.
  *
  * @author Eric Lafortune
  */
-public class AllCpInfoVisitor implements ClassFileVisitor
+public class ExceptionCounter implements ExceptionInfoVisitor
 {
-    private CpInfoVisitor cpInfoVisitor;
+    private int count;
 
 
-    public AllCpInfoVisitor(CpInfoVisitor cpInfoVisitor)
+    /**
+     * Returns the number of exceptions that has been visited so far.
+     */
+    public int getCount()
     {
-        this.cpInfoVisitor = cpInfoVisitor;
+        return count;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ExceptionInfoVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
-        programClassFile.constantPoolEntriesAccept(cpInfoVisitor);
+        count++;
     }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
 }
diff --git a/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java
new file mode 100644
index 0000000..2f3e461
--- /dev/null
+++ b/src/proguard/classfile/visitor/ExceptionExcludedOffsetFilter.java
@@ -0,0 +1,64 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+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
+ * does not cover the instruction at the given offset.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionExcludedOffsetFilter
+implements   ExceptionInfoVisitor
+{
+    private final int                  instructionOffset;
+    private final ExceptionInfoVisitor exceptionInfoVisitor;
+
+
+    /**
+     * Creates a new ExceptionExcludedOffsetFilter.
+     * @param instructionOffset    the instruction offset.
+     * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits
+     *                             will be delegated.
+     */
+    public ExceptionExcludedOffsetFilter(int                  instructionOffset,
+                                         ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        this.instructionOffset          = instructionOffset;
+        this.exceptionInfoVisitor = exceptionInfoVisitor;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        if (!exceptionInfo.isApplicable(instructionOffset))
+        {
+            exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ExceptionOffsetFilter.java b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java
new file mode 100644
index 0000000..461e3d7
--- /dev/null
+++ b/src/proguard/classfile/visitor/ExceptionOffsetFilter.java
@@ -0,0 +1,64 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+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
+ * covers the instruction at the given offset.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionOffsetFilter
+implements   ExceptionInfoVisitor
+{
+    private final int                  instructionOffset;
+    private final ExceptionInfoVisitor exceptionInfoVisitor;
+
+
+    /**
+     * Creates a new ExceptionOffsetFilter.
+     * @param instructionOffset    the instruction offset.
+     * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits
+     *                             will be delegated.
+     */
+    public ExceptionOffsetFilter(int                  instructionOffset,
+                                 ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        this.instructionOffset          = instructionOffset;
+        this.exceptionInfoVisitor = exceptionInfoVisitor;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        if (exceptionInfo.isApplicable(instructionOffset))
+        {
+            exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ExceptionRangeFilter.java b/src/proguard/classfile/visitor/ExceptionRangeFilter.java
new file mode 100644
index 0000000..e6ccddc
--- /dev/null
+++ b/src/proguard/classfile/visitor/ExceptionRangeFilter.java
@@ -0,0 +1,68 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+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.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionRangeFilter
+implements   ExceptionInfoVisitor
+{
+    private final int                  startOffset;
+    private final int                  endOffset;
+    private final ExceptionInfoVisitor exceptionInfoVisitor;
+
+
+    /**
+     * Creates a new ExceptionRangeFilter.
+     * @param startOffset          the start offset of the instruction range.
+     * @param endOffset            the end offset of the instruction range.
+     * @param exceptionInfoVisitor the ExceptionInfoVisitor to which visits
+     *                             will be delegated.
+     */
+    public ExceptionRangeFilter(int                  startOffset,
+                                int                  endOffset,
+                                ExceptionInfoVisitor exceptionInfoVisitor)
+    {
+        this.startOffset          = startOffset;
+        this.endOffset            = endOffset;
+        this.exceptionInfoVisitor = exceptionInfoVisitor;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        if (exceptionInfo.isApplicable(startOffset, endOffset))
+        {
+            exceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/LibraryClassFileFilter.java b/src/proguard/classfile/visitor/LibraryClassFileFilter.java
deleted file mode 100644
index 26e4fe1..0000000
--- a/src/proguard/classfile/visitor/LibraryClassFileFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* $Id: LibraryClassFileFilter.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> delegates its visits to another given
- * <code>ClassFileVisitor</code>, but only when visiting library class files.
- *
- * @author Eric Lafortune
- */
-public class LibraryClassFileFilter implements ClassFileVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new LibraryClassFileFilter.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
-     *                         will be delegated.
-     */
-    public LibraryClassFileFilter(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Don't delegate visits to program class files.
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        classFileVisitor.visitLibraryClassFile(libraryClassFile);
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/LibraryClassFilter.java
similarity index 51%
copy from src/proguard/classfile/visitor/ClassPoolFiller.java
copy to src/proguard/classfile/visitor/LibraryClassFilter.java
index ddde8ab..2c6d3d2 100644
--- a/src/proguard/classfile/visitor/ClassPoolFiller.java
+++ b/src/proguard/classfile/visitor/LibraryClassFilter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPoolFiller.java,v 1.9.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,39 +21,40 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.util.*;
 
 
 /**
- * This ClassFileVisitor collects all the class files it visits in a given
- * class pool.
+ * This <code>ClassVisitor</code> delegates its visits to another given
+ * <code>ClassVisitor</code>, but only when visiting library classes.
  *
  * @author Eric Lafortune
  */
-public class ClassPoolFiller implements ClassFileVisitor
+public class LibraryClassFilter implements ClassVisitor
 {
-    private ClassPool classPool;
+    private final ClassVisitor classVisitor;
 
 
     /**
-     * Creates a new ClassPoolFiller.
+     * Creates a new LibraryClassFilter.
+     * @param classVisitor     the <code>ClassVisitor</code> to which visits
+     *                         will be delegated.
      */
-    public ClassPoolFiller(ClassPool classPool)
+    public LibraryClassFilter(ClassVisitor classVisitor)
     {
-        this.classPool = classPool;
+        this.classVisitor = classVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        classPool.addClass(programClassFile);
+        // Don't delegate visits to program classes.
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        classPool.addClass(libraryClassFile);
+        classVisitor.visitLibraryClass(libraryClass);
     }
 }
diff --git a/src/proguard/classfile/visitor/LibraryMemberFilter.java b/src/proguard/classfile/visitor/LibraryMemberFilter.java
new file mode 100644
index 0000000..b8adfe4
--- /dev/null
+++ b/src/proguard/classfile/visitor/LibraryMemberFilter.java
@@ -0,0 +1,73 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when visiting members of library
+ * classes.
+ *
+ * @author Eric Lafortune
+ */
+public class LibraryMemberFilter implements MemberVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new ProgramMemberFilter.
+     * @param memberVisitor     the <code>MemberVisitor</code> to which
+     *                          visits will be delegated.
+     */
+    public LibraryMemberFilter(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Don't delegate visits to program members.
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Don't delegate visits to program members.
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        memberVisitor.visitLibraryField(libraryClass, libraryField);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+    }
+}
diff --git a/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java b/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java
deleted file mode 100644
index 3fa7e74..0000000
--- a/src/proguard/classfile/visitor/LibraryMemberInfoFilter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $Id: LibraryMemberInfoFilter.java,v 1.5.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when visiting members of library
- * class files.
- *
- * @author Eric Lafortune
- */
-public class LibraryMemberInfoFilter implements MemberInfoVisitor
-{
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new ProgramMemberInfoFilter.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
-     *                          visits will be delegated.
-     */
-    public LibraryMemberInfoFilter(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        // Don't delegate visits to program members.
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Don't delegate visits to program members.
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-    }
-}
diff --git a/src/proguard/classfile/visitor/MemberInfoAccessFilter.java b/src/proguard/classfile/visitor/MemberAccessFilter.java
similarity index 54%
rename from src/proguard/classfile/visitor/MemberInfoAccessFilter.java
rename to src/proguard/classfile/visitor/MemberAccessFilter.java
index 8fe7995..0ece68b 100644
--- a/src/proguard/classfile/visitor/MemberInfoAccessFilter.java
+++ b/src/proguard/classfile/visitor/MemberAccessFilter.java
@@ -1,6 +1,6 @@
-/* $Id: MemberInfoAccessFilter.java,v 1.7.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,9 +24,9 @@ import proguard.classfile.*;
 
 
 /**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when the visited member
- * has the proper access flags.
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member has the proper
+ * access flags.
  * <p>
  * If conflicting access flags (public/private/protected) are specified,
  * having one of them set will be considered sufficient.
@@ -35,8 +35,8 @@ import proguard.classfile.*;
  *
  * @author Eric Lafortune
  */
-public class MemberInfoAccessFilter
-  implements MemberInfoVisitor
+public class MemberAccessFilter
+implements   MemberVisitor
 {
     // A mask of conflicting access flags. These are interpreted in a special
     // way if more of them are required at the same time. In that case, one
@@ -46,66 +46,66 @@ public class MemberInfoAccessFilter
         ClassConstants.INTERNAL_ACC_PRIVATE |
         ClassConstants.INTERNAL_ACC_PROTECTED;
 
-    private int               requiredSetAccessFlags;
-    private int               requiredUnsetAccessFlags;
-    private int               requiredOneSetAccessFlags;
-    private MemberInfoVisitor memberInfoVisitor;
+    private final int           requiredSetAccessFlags;
+    private final int           requiredUnsetAccessFlags;
+    private final int           requiredOneSetAccessFlags;
+    private final MemberVisitor memberVisitor;
 
 
     /**
-     * Creates a new MemberInfoAccessFilter.
+     * Creates a new MemberAccessFilter.
      * @param requiredSetAccessFlags   the class access flags that should be
      *                                 set.
      * @param requiredUnsetAccessFlags the class access flags that should be
      *                                 unset.
-     * @param memberInfoVisitor        the <code>MemberInfoVisitor</code> to
+     * @param memberVisitor            the <code>MemberVisitor</code> to
      *                                 which visits will be delegated.
      */
-    public MemberInfoAccessFilter(int               requiredSetAccessFlags,
-                                  int               requiredUnsetAccessFlags,
-                                  MemberInfoVisitor memberInfoVisitor)
+    public MemberAccessFilter(int           requiredSetAccessFlags,
+                              int           requiredUnsetAccessFlags,
+                              MemberVisitor memberVisitor)
     {
         this.requiredSetAccessFlags    = requiredSetAccessFlags & ~ACCESS_MASK;
         this.requiredUnsetAccessFlags  = requiredUnsetAccessFlags;
         this.requiredOneSetAccessFlags = requiredSetAccessFlags &  ACCESS_MASK;
-        this.memberInfoVisitor         = memberInfoVisitor;
+        this.memberVisitor             = memberVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        if (accepted(programFieldInfo.getAccessFlags()))
+        if (accepted(programField.getAccessFlags()))
         {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+            memberVisitor.visitProgramField(programClass, programField);
         }
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        if (accepted(programMethodInfo.getAccessFlags()))
+        if (accepted(programMethod.getAccessFlags()))
         {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+            memberVisitor.visitProgramMethod(programClass, programMethod);
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
-        if (accepted(libraryFieldInfo.getAccessFlags()))
+        if (accepted(libraryField.getAccessFlags()))
         {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
         }
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        if (accepted(libraryMethodInfo.getAccessFlags()))
+        if (accepted(libraryMethod.getAccessFlags()))
         {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
         }
     }
 
diff --git a/src/proguard/classfile/visitor/MemberClassAccessFilter.java b/src/proguard/classfile/visitor/MemberClassAccessFilter.java
new file mode 100644
index 0000000..a2bd15a
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberClassAccessFilter.java
@@ -0,0 +1,106 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member is accessible
+ * from the given referencing class.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberClassAccessFilter
+implements   MemberVisitor
+{
+    private final Clazz         referencingClass;
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new MemberAccessFilter.
+     * @param referencingClass the class that is accessing the member.
+     * @param memberVisitor    the <code>MemberVisitor</code> to which visits
+     *                         will be delegated.
+     */
+    public MemberClassAccessFilter(Clazz         referencingClass,
+                                   MemberVisitor memberVisitor)
+    {
+        this.referencingClass = referencingClass;
+        this.memberVisitor    = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (accepted(programClass, programField.getAccessFlags()))
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (accepted(programClass, programMethod.getAccessFlags()))
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (accepted(libraryClass, libraryField.getAccessFlags()))
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (accepted(libraryClass, libraryMethod.getAccessFlags()))
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(Clazz clazz, int memberAccessFlags)
+    {
+        int accessLevel = AccessUtil.accessLevel(memberAccessFlags);
+
+        return
+            (accessLevel >= AccessUtil.PUBLIC                                                              ) ||
+            (accessLevel >= AccessUtil.PRIVATE         && referencingClass.equals(clazz)                   ) ||
+            (accessLevel >= AccessUtil.PACKAGE_VISIBLE && (ClassUtil.internalPackageName(referencingClass.getName()).equals(
+                                                           ClassUtil.internalPackageName(clazz.getName())))) ||
+            (accessLevel >= AccessUtil.PROTECTED       && (referencingClass.extends_(clazz)                  ||
+                                                           referencingClass.extendsOrImplements(clazz))            );
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberCounter.java b/src/proguard/classfile/visitor/MemberCounter.java
index d781200..501f249 100644
--- a/src/proguard/classfile/visitor/MemberCounter.java
+++ b/src/proguard/classfile/visitor/MemberCounter.java
@@ -1,6 +1,6 @@
-/* $Id: MemberCounter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,14 +21,13 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
 
 /**
- * This MemberInfoVisitor counts the number of class members that has been visited.
+ * This MemberVisitor counts the number of class members that has been visited.
  *
  * @author Eric Lafortune
  */
-public class MemberCounter implements MemberInfoVisitor
+public class MemberCounter implements MemberVisitor
 {
     private int count;
 
@@ -42,31 +41,31 @@ public class MemberCounter implements MemberInfoVisitor
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile,
-                                      LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass,
+                                  LibraryField libraryField)
     {
         count++;
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile,
-                                       LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass,
+                                   LibraryMethod libraryMethod)
     {
         count++;
     }
 
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile,
-                                      ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass,
+                                  ProgramField programField)
     {
         count++;
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile,
-                                       ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass,
+                                   ProgramMethod programMethod)
     {
         count++;
     }
diff --git a/src/proguard/classfile/visitor/MemberDescriptorFilter.java b/src/proguard/classfile/visitor/MemberDescriptorFilter.java
new file mode 100644
index 0000000..acaec2a
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberDescriptorFilter.java
@@ -0,0 +1,99 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member
+ * has a descriptor that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberDescriptorFilter implements MemberVisitor
+{
+    private final StringMatcher regularExpressionMatcher;
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new MemberDescriptorFilter.
+     * @param regularExpression the regular expression against which member
+     *                          descriptors will be matched.
+     * @param memberVisitor     the <code>MemberVisitor</code> to which visits
+     *                          will be delegated.
+     */
+    public MemberDescriptorFilter(String        regularExpression,
+                                  MemberVisitor memberVisitor)
+    {
+        this.regularExpressionMatcher = new ClassNameParser().parse(regularExpression);
+        this.memberVisitor            = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (accepted(programField.getDescriptor(programClass)))
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (accepted(programMethod.getDescriptor(programClass)))
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (accepted(libraryField.getDescriptor(libraryClass)))
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (accepted(libraryMethod.getDescriptor(libraryClass)))
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberInfoClassFileAccessFilter.java b/src/proguard/classfile/visitor/MemberInfoClassFileAccessFilter.java
deleted file mode 100644
index a7c6d5b..0000000
--- a/src/proguard/classfile/visitor/MemberInfoClassFileAccessFilter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/* $Id: MemberInfoClassFileAccessFilter.java,v 1.1.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java bytecode.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when the visited memberInfo is
- * accessible from the given referencing classFile.
- *
- * @author Eric Lafortune
- */
-public class MemberInfoClassFileAccessFilter
-implements   MemberInfoVisitor
-{
-    private ClassFile         referencingClassFile;
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new MemberInfoAccessFilter.
-     * @param referencingClassFile the classFile that is accessing the member.
-     * @param memberInfoVisitor    the <code>MemberInfoVisitor</code> to which
-     *                             visits will be delegated.
-     */
-    public MemberInfoClassFileAccessFilter(ClassFile         referencingClassFile,
-                                           MemberInfoVisitor memberInfoVisitor)
-    {
-        this.referencingClassFile = referencingClassFile;
-        this.memberInfoVisitor    = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (accepted(programClassFile, programFieldInfo.getAccessFlags()))
-        {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (accepted(programClassFile, programMethodInfo.getAccessFlags()))
-        {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (accepted(libraryClassFile, libraryFieldInfo.getAccessFlags()))
-        {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (accepted(libraryClassFile, libraryMethodInfo.getAccessFlags()))
-        {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-
-
-    // Small utility methodInfos.
-
-    private boolean accepted(ClassFile classFile, int memberInfoAccessFlags)
-    {
-        int accessLevel = AccessUtil.accessLevel(memberInfoAccessFlags);
-
-        return
-            (accessLevel >= AccessUtil.PUBLIC                                                              ) ||
-            (accessLevel >= AccessUtil.PRIVATE         && referencingClassFile.equals(classFile)                   ) ||
-            (accessLevel >= AccessUtil.PACKAGE_VISIBLE && (ClassUtil.internalPackageName(referencingClassFile.getName()).equals(
-                                                           ClassUtil.internalPackageName(classFile.getName())))) ||
-            (accessLevel >= AccessUtil.PROTECTED       && (referencingClassFile.extends_(classFile)                  ||
-                                                           referencingClassFile.implements_(classFile))            );
-    }
-}
diff --git a/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java b/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java
deleted file mode 100644
index 29ea0b0..0000000
--- a/src/proguard/classfile/visitor/MemberInfoDescriptorFilter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* $Id: MemberInfoDescriptorFilter.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.util.*;
-
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when the visited member
- * has a descriptor that matches a given regular expression.
- *
- * @author Eric Lafortune
- */
-public class MemberInfoDescriptorFilter implements MemberInfoVisitor
-{
-    private StringMatcher     regularExpressionMatcher;
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new MemberInfoDescriptorFilter.
-     * @param regularExpression the regular expression against which class names
-     *                          will be matched.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which visits
-     *                          will be delegated.
-     */
-    public MemberInfoDescriptorFilter(String            regularExpression,
-                                      MemberInfoVisitor memberInfoVisitor)
-    {
-        this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
-        this.memberInfoVisitor        = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (accepted(programFieldInfo.getDescriptor(programClassFile)))
-        {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (accepted(programMethodInfo.getDescriptor(programClassFile)))
-        {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (accepted(libraryFieldInfo.getDescriptor(libraryClassFile)))
-        {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (accepted(libraryMethodInfo.getDescriptor(libraryClassFile)))
-        {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-
-
-    // Small utility methods.
-
-    private boolean accepted(String name)
-    {
-        return regularExpressionMatcher.matches(name);
-    }
-}
diff --git a/src/proguard/classfile/visitor/MemberInfoNameFilter.java b/src/proguard/classfile/visitor/MemberInfoNameFilter.java
deleted file mode 100644
index 6b3cda2..0000000
--- a/src/proguard/classfile/visitor/MemberInfoNameFilter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/* $Id: MemberInfoNameFilter.java,v 1.10.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.util.*;
-
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when the visited member
- * has a name that matches a given regular expression.
- *
- * @author Eric Lafortune
- */
-public class MemberInfoNameFilter implements MemberInfoVisitor
-{
-    private StringMatcher     regularExpressionMatcher;
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new MemberInfoNameFilter.
-     * @param regularExpression the regular expression against which class names
-     *                          will be matched.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which visits
-     *                          will be delegated.
-     */
-    public MemberInfoNameFilter(String            regularExpression,
-                                MemberInfoVisitor memberInfoVisitor)
-    {
-        this.regularExpressionMatcher = new ClassNameMatcher(regularExpression);
-        this.memberInfoVisitor        = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (accepted(programFieldInfo.getName(programClassFile)))
-        {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (accepted(programMethodInfo.getName(programClassFile)))
-        {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (accepted(libraryFieldInfo.getName(libraryClassFile)))
-        {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (accepted(libraryMethodInfo.getName(libraryClassFile)))
-        {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-
-
-    // Small utility methods.
-
-    private boolean accepted(String name)
-    {
-        return regularExpressionMatcher.matches(name);
-    }
-}
diff --git a/src/proguard/classfile/visitor/MemberInfoVisitor.java b/src/proguard/classfile/visitor/MemberInfoVisitor.java
deleted file mode 100644
index f221b17..0000000
--- a/src/proguard/classfile/visitor/MemberInfoVisitor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/* $Id: MemberInfoVisitor.java,v 1.11.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This interface specifies the methods for a visitor of
- * <code>ProgramMemberInfo</code> objects and <code>LibraryMemberInfo</code>
- * objects.
- *
- * @author Eric Lafortune
- */
-public interface MemberInfoVisitor
-{
-    public void visitProgramFieldInfo( ProgramClassFile programClassFile, ProgramFieldInfo  programFieldInfo);
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo);
-
-    public void visitLibraryFieldInfo( LibraryClassFile libraryClassFile, LibraryFieldInfo  libraryFieldInfo);
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo);
-}
diff --git a/src/proguard/classfile/visitor/MemberNameFilter.java b/src/proguard/classfile/visitor/MemberNameFilter.java
new file mode 100644
index 0000000..c3cf35d
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberNameFilter.java
@@ -0,0 +1,99 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.util.*;
+
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member
+ * has a name that matches a given regular expression.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberNameFilter implements MemberVisitor
+{
+    private final StringMatcher regularExpressionMatcher;
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new MemberNameFilter.
+     * @param regularExpression the regular expression against which member
+     *                          names will be matched.
+     * @param memberVisitor     the <code>MemberVisitor</code> to which visits
+     *                          will be delegated.
+     */
+    public MemberNameFilter(String        regularExpression,
+                            MemberVisitor memberVisitor)
+    {
+        this.regularExpressionMatcher = new NameParser().parse(regularExpression);
+        this.memberVisitor            = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (accepted(programField.getName(programClass)))
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (accepted(programMethod.getName(programClass)))
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (accepted(libraryField.getName(libraryClass)))
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (accepted(libraryMethod.getName(libraryClass)))
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private boolean accepted(String name)
+    {
+        return regularExpressionMatcher.matches(name);
+    }
+}
diff --git a/src/proguard/classfile/visitor/MemberToClassVisitor.java b/src/proguard/classfile/visitor/MemberToClassVisitor.java
new file mode 100644
index 0000000..b332c89
--- /dev/null
+++ b/src/proguard/classfile/visitor/MemberToClassVisitor.java
@@ -0,0 +1,90 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberVisitor delegates all visits to a given ClassVisitor.
+ * The latter visits the class  of each visited class member, although
+ * never twice in a row.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberToClassVisitor implements MemberVisitor
+{
+    private final ClassVisitor classVisitor;
+
+    private Clazz lastVisitedClass;
+
+
+    public MemberToClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (!programClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitProgramClass(programClass);
+
+            lastVisitedClass = programClass;
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (!programClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitProgramClass(programClass);
+
+            lastVisitedClass = programClass;
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (!libraryClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+
+            lastVisitedClass = libraryClass;
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (!libraryClass.equals(lastVisitedClass))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+
+            lastVisitedClass = libraryClass;
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/ClassFileVisitor.java b/src/proguard/classfile/visitor/MemberVisitor.java
similarity index 61%
rename from src/proguard/classfile/visitor/ClassFileVisitor.java
rename to src/proguard/classfile/visitor/MemberVisitor.java
index 79aff22..6ea3209 100644
--- a/src/proguard/classfile/visitor/ClassFileVisitor.java
+++ b/src/proguard/classfile/visitor/MemberVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: ClassFileVisitor.java,v 1.8.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -25,12 +25,16 @@ import proguard.classfile.*;
 
 /**
  * This interface specifies the methods for a visitor of
- * <code>ClassFile</code> objects.
+ * <code>ProgramMember</code> objects and <code>LibraryMember</code>
+ * objects.
  *
  * @author Eric Lafortune
  */
-public interface ClassFileVisitor
+public interface MemberVisitor
 {
-    public void visitProgramClassFile(ProgramClassFile programClassFile);
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile);
+    public void visitProgramField( ProgramClass programClass, ProgramField  programField);
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod);
+
+    public void visitLibraryField( LibraryClass libraryClass, LibraryField  libraryField);
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod);
 }
diff --git a/src/proguard/classfile/visitor/MethodImplementationFilter.java b/src/proguard/classfile/visitor/MethodImplementationFilter.java
index 5a84909..7b6bb33 100644
--- a/src/proguard/classfile/visitor/MethodImplementationFilter.java
+++ b/src/proguard/classfile/visitor/MethodImplementationFilter.java
@@ -1,6 +1,6 @@
-/* $Id: MethodImplementationFilter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,55 +21,50 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This <code>MemberInfoVisitor</code> delegates its visits to methods to
- * another given <code>MemberInfoVisitor</code>, but only when the visited
+ * This <code>MemberVisitor</code> delegates its visits to methods to
+ * another given <code>MemberVisitor</code>, but only when the visited
  * method may have implementations.
  *
- * @see ClassFile#mayHaveImplementations(MethodInfo)
+ * @see Clazz#mayHaveImplementations(Method)
  * @author Eric Lafortune
  */
 public class MethodImplementationFilter
-  implements MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private MemberInfoVisitor memberInfoVisitor;
+    private final MemberVisitor memberVisitor;
 
 
     /**
      * Creates a new MethodImplementationFilter.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
+     * @param memberVisitor     the <code>MemberVisitor</code> to which
      *                          visits will be delegated.
      */
-    public MethodImplementationFilter(MemberInfoVisitor memberInfoVisitor)
+    public MethodImplementationFilter(MemberVisitor memberVisitor)
     {
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    // Implementations for MemberVisitor.
 
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        if (programClassFile.mayHaveImplementations(programMethodInfo))
+        if (programClass.mayHaveImplementations(programMethod))
         {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
+            memberVisitor.visitProgramMethod(programClass, programMethod);
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        if (libraryClassFile.mayHaveImplementations(libraryMethodInfo))
+        if (libraryClass.mayHaveImplementations(libraryMethod))
         {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
         }
     }
 }
diff --git a/src/proguard/classfile/visitor/MethodImplementationTraveler.java b/src/proguard/classfile/visitor/MethodImplementationTraveler.java
index dc5e6cc..2393c3b 100644
--- a/src/proguard/classfile/visitor/MethodImplementationTraveler.java
+++ b/src/proguard/classfile/visitor/MethodImplementationTraveler.java
@@ -1,6 +1,6 @@
-/* $Id: MethodImplementationTraveler.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,57 +21,52 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This <code>MemberInfoVisitor</code> lets a given
- * <code>MemberInfoVisitor</code> travel to all concrete implementations of
- * the visited methods in their class hierarchies.
+ * This <code>MemberVisitor</code> lets a given <code>MemberVisitor</code>
+ * travel to all concrete implementations of the visited methods in their class
+ * hierarchies.
  *
  * @author Eric Lafortune
  */
 public class MethodImplementationTraveler
-  implements MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private boolean           visitThisMethod;
-    private MemberInfoVisitor memberInfoVisitor;
+    private final boolean       visitThisMethod;
+    private final MemberVisitor memberVisitor;
 
 
     /**
      * Creates a new MethodImplementationTraveler.
      * @param visitThisMethod   specifies whether to visit the originally
      *                          visited methods.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
+     * @param memberVisitor     the <code>MemberVisitor</code> to which
      *                          visits will be delegated.
      */
-    public MethodImplementationTraveler(boolean           visitThisMethod,
-                                        MemberInfoVisitor memberInfoVisitor)
+    public MethodImplementationTraveler(boolean       visitThisMethod,
+                                        MemberVisitor memberVisitor)
     {
-        this.visitThisMethod   = visitThisMethod;
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.visitThisMethod = visitThisMethod;
+        this.memberVisitor   = memberVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    // Implementations for MemberVisitor.
 
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        programClassFile.methodImplementationsAccept(programMethodInfo,
-                                                     visitThisMethod,
-                                                     memberInfoVisitor);
+        programClass.methodImplementationsAccept(programMethod,
+                                                 visitThisMethod,
+                                                 memberVisitor);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        libraryClassFile.methodImplementationsAccept(libraryMethodInfo,
-                                                     visitThisMethod,
-                                                     memberInfoVisitor);
+        libraryClass.methodImplementationsAccept(libraryMethod,
+                                                 visitThisMethod,
+                                                 memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/MultiClassFileVisitor.java b/src/proguard/classfile/visitor/MultiClassFileVisitor.java
deleted file mode 100644
index 15c2674..0000000
--- a/src/proguard/classfile/visitor/MultiClassFileVisitor.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* $Id: MultiClassFileVisitor.java,v 1.9.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This ClassFileVisitor delegates all visits to each ClassFileVisitor
- * in a given list.
- *
- * @author Eric Lafortune
- */
-public class MultiClassFileVisitor implements ClassFileVisitor
-{
-    private static final int ARRAY_SIZE_INCREMENT = 5;
-
-    private ClassFileVisitor[] classFileVisitors;
-    private int                classFileVisitorCount;
-
-
-    public MultiClassFileVisitor()
-    {
-    }
-
-
-    public MultiClassFileVisitor(ClassFileVisitor[] classFileVisitors)
-    {
-        this.classFileVisitors     = classFileVisitors;
-        this.classFileVisitorCount = classFileVisitors.length;
-    }
-
-
-    public void addClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        ensureArraySize();
-
-        classFileVisitors[classFileVisitorCount++] = classFileVisitor;
-    }
-
-
-    private void ensureArraySize()
-    {
-        if (classFileVisitors == null)
-        {
-            classFileVisitors = new ClassFileVisitor[ARRAY_SIZE_INCREMENT];
-        }
-        else if (classFileVisitors.length == classFileVisitorCount)
-        {
-            ClassFileVisitor[] newClassFileVisitors =
-                new ClassFileVisitor[classFileVisitorCount +
-                                     ARRAY_SIZE_INCREMENT];
-            System.arraycopy(classFileVisitors, 0,
-                             newClassFileVisitors, 0,
-                             classFileVisitorCount);
-            classFileVisitors = newClassFileVisitors;
-        }
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        for (int i = 0; i < classFileVisitorCount; i++)
-        {
-            classFileVisitors[i].visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        for (int i = 0; i < classFileVisitorCount; i++)
-        {
-            classFileVisitors[i].visitLibraryClassFile(libraryClassFile);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
index dbc3696..bbe6e60 100644
--- a/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
+++ b/src/proguard/classfile/visitor/MultiClassPoolVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: MultiClassPoolVisitor.java,v 1.2 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.classfile.visitor;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassPool;
 
 
 /**
@@ -80,9 +80,9 @@ public class MultiClassPoolVisitor implements ClassPoolVisitor
 
     public void visitClassPool(ClassPool classPool)
     {
-        for (int i = 0; i < classPoolVisitorCount; i++)
+        for (int index = 0; index < classPoolVisitorCount; index++)
         {
-            classPoolVisitors[i].visitClassPool(classPool);
+            classPoolVisitors[index].visitClassPool(classPool);
         }
     }
 }
diff --git a/src/proguard/classfile/visitor/MultiClassVisitor.java b/src/proguard/classfile/visitor/MultiClassVisitor.java
new file mode 100644
index 0000000..a7792c8
--- /dev/null
+++ b/src/proguard/classfile/visitor/MultiClassVisitor.java
@@ -0,0 +1,97 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassVisitor delegates all visits to each ClassVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiClassVisitor implements ClassVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private ClassVisitor[] classVisitors;
+    private int            classVisitorCount;
+
+
+    public MultiClassVisitor()
+    {
+    }
+
+
+    public MultiClassVisitor(ClassVisitor[] classVisitors)
+    {
+        this.classVisitors     = classVisitors;
+        this.classVisitorCount = classVisitors.length;
+    }
+
+
+    public void addClassVisitor(ClassVisitor classVisitor)
+    {
+        ensureArraySize();
+
+        classVisitors[classVisitorCount++] = classVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (classVisitors == null)
+        {
+            classVisitors = new ClassVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (classVisitors.length == classVisitorCount)
+        {
+            ClassVisitor[] newClassVisitors =
+                new ClassVisitor[classVisitorCount +
+                                     ARRAY_SIZE_INCREMENT];
+            System.arraycopy(classVisitors, 0,
+                             newClassVisitors, 0,
+                             classVisitorCount);
+            classVisitors = newClassVisitors;
+        }
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        for (int index = 0; index < classVisitorCount; index++)
+        {
+            classVisitors[index].visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        for (int index = 0; index < classVisitorCount; index++)
+        {
+            classVisitors[index].visitLibraryClass(libraryClass);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java b/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java
deleted file mode 100644
index ff7a938..0000000
--- a/src/proguard/classfile/visitor/MultiMemberInfoVisitor.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/* $Id: MultiMemberInfoVisitor.java,v 1.12.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This MemberInfoVisitor delegates all visits to each MemberInfoVisitor
- * in a given list.
- *
- * @author Eric Lafortune
- */
-public class MultiMemberInfoVisitor implements MemberInfoVisitor
-{
-    private static final int ARRAY_SIZE_INCREMENT = 5;
-
-    private MemberInfoVisitor[] memberInfoVisitors;
-    private int                 memberInfoVisitorCount;
-
-
-    public MultiMemberInfoVisitor()
-    {
-    }
-
-
-    public MultiMemberInfoVisitor(MemberInfoVisitor[] memberInfoVisitors)
-    {
-        this.memberInfoVisitors     = memberInfoVisitors;
-        this.memberInfoVisitorCount = memberInfoVisitors.length;
-    }
-
-
-    public void addMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
-    {
-        ensureArraySize();
-
-        memberInfoVisitors[memberInfoVisitorCount++] = memberInfoVisitor;
-    }
-
-
-    private void ensureArraySize()
-    {
-        if (memberInfoVisitors == null)
-        {
-            memberInfoVisitors = new MemberInfoVisitor[ARRAY_SIZE_INCREMENT];
-        }
-        else if (memberInfoVisitors.length == memberInfoVisitorCount)
-        {
-            MemberInfoVisitor[] newMemberInfoVisitors =
-                new MemberInfoVisitor[memberInfoVisitorCount +
-                                         ARRAY_SIZE_INCREMENT];
-            System.arraycopy(memberInfoVisitors, 0,
-                             newMemberInfoVisitors, 0,
-                             memberInfoVisitorCount);
-            memberInfoVisitors = newMemberInfoVisitors;
-        }
-    }
-
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        for (int i = 0; i < memberInfoVisitorCount; i++)
-        {
-            memberInfoVisitors[i].visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        for (int i = 0; i < memberInfoVisitorCount; i++)
-        {
-            memberInfoVisitors[i].visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        for (int i = 0; i < memberInfoVisitorCount; i++)
-        {
-            memberInfoVisitors[i].visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        for (int i = 0; i < memberInfoVisitorCount; i++)
-        {
-            memberInfoVisitors[i].visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/MultiMemberVisitor.java b/src/proguard/classfile/visitor/MultiMemberVisitor.java
new file mode 100644
index 0000000..a23326c
--- /dev/null
+++ b/src/proguard/classfile/visitor/MultiMemberVisitor.java
@@ -0,0 +1,113 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberVisitor delegates all visits to each MemberVisitor
+ * in a given list.
+ *
+ * @author Eric Lafortune
+ */
+public class MultiMemberVisitor implements MemberVisitor
+{
+    private static final int ARRAY_SIZE_INCREMENT = 5;
+
+    private MemberVisitor[] memberVisitors;
+    private int             memberVisitorCount;
+
+
+    public MultiMemberVisitor()
+    {
+    }
+
+
+    public MultiMemberVisitor(MemberVisitor[] memberVisitors)
+    {
+        this.memberVisitors     = memberVisitors;
+        this.memberVisitorCount = memberVisitors.length;
+    }
+
+
+    public void addMemberVisitor(MemberVisitor memberVisitor)
+    {
+        ensureArraySize();
+
+        memberVisitors[memberVisitorCount++] = memberVisitor;
+    }
+
+
+    private void ensureArraySize()
+    {
+        if (memberVisitors == null)
+        {
+            memberVisitors = new MemberVisitor[ARRAY_SIZE_INCREMENT];
+        }
+        else if (memberVisitors.length == memberVisitorCount)
+        {
+            MemberVisitor[] newMemberVisitors =
+                new MemberVisitor[memberVisitorCount +
+                                         ARRAY_SIZE_INCREMENT];
+            System.arraycopy(memberVisitors, 0,
+                             newMemberVisitors, 0,
+                             memberVisitorCount);
+            memberVisitors = newMemberVisitors;
+        }
+    }
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        for (int index = 0; index < memberVisitorCount; index++)
+        {
+            memberVisitors[index].visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        for (int index = 0; index < memberVisitorCount; index++)
+        {
+            memberVisitors[index].visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        for (int index = 0; index < memberVisitorCount; index++)
+        {
+            memberVisitors[index].visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        for (int index = 0; index < memberVisitorCount; index++)
+        {
+            memberVisitors[index].visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/AllClassFileVisitor.java b/src/proguard/classfile/visitor/NamedClassVisitor.java
similarity index 62%
rename from src/proguard/classfile/visitor/AllClassFileVisitor.java
rename to src/proguard/classfile/visitor/NamedClassVisitor.java
index 5e690fa..0628e41 100644
--- a/src/proguard/classfile/visitor/AllClassFileVisitor.java
+++ b/src/proguard/classfile/visitor/NamedClassVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: AllClassFileVisitor.java,v 1.11.2.2 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,28 +20,30 @@
  */
 package proguard.classfile.visitor;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassPool;
 
 
 /**
- * This ClassPoolVisitor lets a given ClassFileVisitor visit all ClassFile
- * objects of the class pools it visits.
+ * This class visits Clazz objects with the given name.
  *
  * @author Eric Lafortune
  */
-public class AllClassFileVisitor implements ClassPoolVisitor
+public class NamedClassVisitor implements ClassPoolVisitor
 {
-    private ClassFileVisitor classFileVisitor;
+    private final ClassVisitor classVisitor;
+    private final String       name;
 
 
-    public AllClassFileVisitor(ClassFileVisitor classFileVisitor)
+    public NamedClassVisitor(ClassVisitor classVisitor,
+                             String       name)
     {
-        this.classFileVisitor = classFileVisitor;
+        this.classVisitor = classVisitor;
+        this.name         = name;
     }
 
 
     public void visitClassPool(ClassPool classPool)
     {
-        classPool.classFilesAccept(classFileVisitor);
+        classPool.classAccept(name, classVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/NamedFieldVisitor.java b/src/proguard/classfile/visitor/NamedFieldVisitor.java
index 35ca576..5c654b5 100644
--- a/src/proguard/classfile/visitor/NamedFieldVisitor.java
+++ b/src/proguard/classfile/visitor/NamedFieldVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: NamedFieldVisitor.java,v 1.12.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,36 +24,36 @@ import proguard.classfile.*;
 
 
 /**
- * This class visits ProgramMemberInfo objects referring to fields, identified by
+ * This class visits ProgramMember objects referring to fields, identified by
  * a name and descriptor pair.
  *
  * @author Eric Lafortune
  */
-public class NamedFieldVisitor implements ClassFileVisitor
+public class NamedFieldVisitor implements ClassVisitor
 {
-    private String            name;
-    private String            descriptor;
-    private MemberInfoVisitor memberInfoVisitor;
+    private final String        name;
+    private final String        descriptor;
+    private final MemberVisitor memberVisitor;
 
 
-    public NamedFieldVisitor(String            name,
-                             String            descriptor,
-                             MemberInfoVisitor memberInfoVisitor)
+    public NamedFieldVisitor(String        name,
+                             String        descriptor,
+                             MemberVisitor memberVisitor)
     {
-        this.name              = name;
-        this.descriptor        = descriptor;
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.name          = name;
+        this.descriptor    = descriptor;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        programClassFile.fieldAccept(name, descriptor, memberInfoVisitor);
+        programClass.fieldAccept(name, descriptor, memberVisitor);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        libraryClassFile.fieldAccept(name, descriptor, memberInfoVisitor);
+        libraryClass.fieldAccept(name, descriptor, memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/NamedMethodVisitor.java b/src/proguard/classfile/visitor/NamedMethodVisitor.java
index 2fd496e..bb7671c 100644
--- a/src/proguard/classfile/visitor/NamedMethodVisitor.java
+++ b/src/proguard/classfile/visitor/NamedMethodVisitor.java
@@ -1,6 +1,6 @@
-/* $Id: NamedMethodVisitor.java,v 1.12.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,36 +24,36 @@ import proguard.classfile.*;
 
 
 /**
- * This class visits ProgramMemberInfo objects referring to methods, identified by
+ * This class visits ProgramMember objects referring to methods, identified by
  * a name and descriptor pair.
  *
  * @author Eric Lafortune
  */
-public class NamedMethodVisitor implements ClassFileVisitor
+public class NamedMethodVisitor implements ClassVisitor
 {
-    private String            name;
-    private String            descriptor;
-    private MemberInfoVisitor memberInfoVisitor;
+    private final String        name;
+    private final String        descriptor;
+    private final MemberVisitor memberVisitor;
 
 
-    public NamedMethodVisitor(String            name,
-                              String            descriptor,
-                              MemberInfoVisitor memberInfoVisitor)
+    public NamedMethodVisitor(String        name,
+                              String        descriptor,
+                              MemberVisitor memberVisitor)
     {
-        this.name              = name;
-        this.descriptor        = descriptor;
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.name          = name;
+        this.descriptor    = descriptor;
+        this.memberVisitor = memberVisitor;
     }
 
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        programClassFile.methodAccept(name, descriptor, memberInfoVisitor);
+        programClass.methodAccept(name, descriptor, memberVisitor);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        libraryClassFile.methodAccept(name, descriptor, memberInfoVisitor);
+        libraryClass.methodAccept(name, descriptor, memberVisitor);
     }
 }
diff --git a/src/proguard/classfile/visitor/ProgramClassFileFilter.java b/src/proguard/classfile/visitor/ProgramClassFileFilter.java
deleted file mode 100644
index e1b81a0..0000000
--- a/src/proguard/classfile/visitor/ProgramClassFileFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/* $Id: ProgramClassFileFilter.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> delegates its visits to another given
- * <code>ClassFileVisitor</code>, but only when visiting program class files.
- *
- * @author Eric Lafortune
- */
-public class ProgramClassFileFilter implements ClassFileVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new ProgramClassFileFilter.
-     * @param classFileVisitor the <code>ClassFileVisitor</code> to which visits
-     *                         will be delegated.
-     */
-    public ProgramClassFileFilter(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        classFileVisitor.visitProgramClassFile(programClassFile);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Don't delegate visits to library class files.
-    }
-}
diff --git a/src/proguard/classfile/visitor/ClassPoolFiller.java b/src/proguard/classfile/visitor/ProgramClassFilter.java
similarity index 51%
copy from src/proguard/classfile/visitor/ClassPoolFiller.java
copy to src/proguard/classfile/visitor/ProgramClassFilter.java
index ddde8ab..6722800 100644
--- a/src/proguard/classfile/visitor/ClassPoolFiller.java
+++ b/src/proguard/classfile/visitor/ProgramClassFilter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassPoolFiller.java,v 1.9.2.4 2007/01/18 21:31:51 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,39 +21,40 @@
 package proguard.classfile.visitor;
 
 import proguard.classfile.*;
-import proguard.classfile.util.*;
 
 
 /**
- * This ClassFileVisitor collects all the class files it visits in a given
- * class pool.
+ * This <code>ClassVisitor</code> delegates its visits to another given
+ * <code>ClassVisitor</code>, but only when visiting program classes.
  *
  * @author Eric Lafortune
  */
-public class ClassPoolFiller implements ClassFileVisitor
+public class ProgramClassFilter implements ClassVisitor
 {
-    private ClassPool classPool;
+    private final ClassVisitor classVisitor;
 
 
     /**
-     * Creates a new ClassPoolFiller.
+     * Creates a new ProgramClassFilter.
+     * @param classVisitor     the <code>ClassVisitor</code> to which visits
+     *                         will be delegated.
      */
-    public ClassPoolFiller(ClassPool classPool)
+    public ProgramClassFilter(ClassVisitor classVisitor)
     {
-        this.classPool = classPool;
+        this.classVisitor = classVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        classPool.addClass(programClassFile);
+        classVisitor.visitProgramClass(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        classPool.addClass(libraryClassFile);
+        // Don't delegate visits to library classes.
     }
 }
diff --git a/src/proguard/classfile/visitor/ProgramMemberFilter.java b/src/proguard/classfile/visitor/ProgramMemberFilter.java
new file mode 100644
index 0000000..7c72abc
--- /dev/null
+++ b/src/proguard/classfile/visitor/ProgramMemberFilter.java
@@ -0,0 +1,73 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when visiting members of program
+ * classes.
+ *
+ * @author Eric Lafortune
+ */
+public class ProgramMemberFilter implements MemberVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new ProgramMemberFilter.
+     * @param memberVisitor     the <code>MemberVisitor</code> to which
+     *                          visits will be delegated.
+     */
+    public ProgramMemberFilter(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        memberVisitor.visitProgramField(programClass, programField);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        memberVisitor.visitProgramMethod(programClass, programMethod);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        // Don't delegate visits to library members.
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        // Don't delegate visits to library members.
+    }
+}
diff --git a/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java b/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java
deleted file mode 100644
index 1903838..0000000
--- a/src/proguard/classfile/visitor/ProgramMemberInfoFilter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/* $Id: ProgramMemberInfoFilter.java,v 1.5.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when visiting members of program
- * class files.
- *
- * @author Eric Lafortune
- */
-public class ProgramMemberInfoFilter implements MemberInfoVisitor
-{
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new ProgramMemberInfoFilter.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
-     *                          visits will be delegated.
-     */
-    public ProgramMemberInfoFilter(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Don't delegate visits to library members.
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Don't delegate visits to library members.
-    }
-}
diff --git a/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java b/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java
deleted file mode 100644
index 69e472d..0000000
--- a/src/proguard/classfile/visitor/ReferencedClassFileVisitor.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/* $Id: ReferencedClassFileVisitor.java,v 1.6.2.4 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassFileVisitor, MmemberInfoVisitor, CpInfoVisitor, AttrInfoVisitor,
- * etc. lets a given ClassFileVisitor visit all the referenced classes of the
- * elements that it visits.
- *
- * @author Eric Lafortune
- */
-public class ReferencedClassFileVisitor
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor,
-             LocalVariableInfoVisitor,
-             LocalVariableTypeInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    public ReferencedClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Visit the constant pool entries.
-        programClassFile.constantPoolEntriesAccept(this);
-
-        // Visit the fields and methods.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        // Visit the attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Visit the fields and methods.
-        libraryClassFile.fieldsAccept(this);
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        // Let the visitor visit the classes referenced in the descriptor string.
-        programFieldInfo.referencedClassesAccept(classFileVisitor);
-
-        // Visit the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Let the visitor visit the classes referenced in the descriptor string.
-        programMethodInfo.referencedClassesAccept(classFileVisitor);
-
-        // Visit the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Let the visitor visit the classes referenced in the descriptor string.
-        libraryFieldInfo.referencedClassesAccept(classFileVisitor);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Let the visitor visit the classes referenced in the descriptor string.
-        libraryMethodInfo.referencedClassesAccept(classFileVisitor);
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
-    {
-        visitReferencedClassFile(stringCpInfo.referencedClassFile);
-    }
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        visitReferencedClassFile(fieldrefCpInfo.referencedClassFile);
-    }
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        visitReferencedClassFile(interfaceMethodrefCpInfo.referencedClassFile);
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        visitReferencedClassFile(methodrefCpInfo.referencedClassFile);
-    }
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        visitReferencedClassFile(classCpInfo.referencedClassFile);
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Visit the attributes of the code attribute.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        // Let the visitor visit the class of the enclosing method.
-        visitReferencedClassFile(enclosingMethodAttrInfo.referencedClassFile);
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        // Visit the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        // Visit the local variable types.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        // Let the visitor visit the classes referenced in the signature string.
-        signatureAttrInfo.referencedClassesAccept(classFileVisitor);
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        // Visit the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        // Visit the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        // Visit the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        // Visit the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, MethodInfo methodInfo, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        // Visit the default element value.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
-    }
-
-
-    // Implementations for LocalVariableInfoVisitor.
-
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
-    {
-        // Let the visitor visit the class referenced in the local variable.
-        visitReferencedClassFile(localVariableInfo.referencedClassFile);
-    }
-
-
-    // Implementations for LocalVariableTypeInfoVisitor.
-
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
-    {
-        // Let the visitor visit the classes referenced in the local variable type.
-        visitReferencedClassFiles(localVariableTypeInfo.referencedClassFiles);
-    }
-
-
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
-    {
-        // Let the visitor visit the classes referenced in the annotation.
-        visitReferencedClassFiles(annotation.referencedClassFiles);
-
-        // Visit the element values.
-        annotation.elementValuesAccept(classFile, this);
-    }
-
-
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue) {}
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue) {}
-
-
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        // Let the visitor visit the classes referenced in the constant element value.
-        visitReferencedClassFiles(enumConstantElementValue.referencedClassFiles);
-    }
-
-
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
-    {
-        // Let the visitor visit the classes referenced in the class element value.
-        visitReferencedClassFiles(classElementValue.referencedClassFiles);
-    }
-
-
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        // Visit the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-    }
-
-
-    // Small utility methods.
-
-    private void visitReferencedClassFiles(ClassFile[] referencedClassFiles)
-    {
-        if (referencedClassFiles != null)
-        {
-            for (int index = 0; index < referencedClassFiles.length; index++)
-            {
-                visitReferencedClassFile(referencedClassFiles[index]);
-            }
-        }
-    }
-
-
-    private void visitReferencedClassFile(ClassFile referencedClassFile)
-    {
-        if (referencedClassFile != null)
-        {
-            referencedClassFile.accept(classFileVisitor);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/ReferencedClassVisitor.java b/src/proguard/classfile/visitor/ReferencedClassVisitor.java
new file mode 100644
index 0000000..a2dede2
--- /dev/null
+++ b/src/proguard/classfile/visitor/ReferencedClassVisitor.java
@@ -0,0 +1,243 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+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.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferencedClassVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor,
+             AttributeVisitor,
+             LocalVariableInfoVisitor,
+             LocalVariableTypeInfoVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private final ClassVisitor classVisitor;
+
+
+    public ReferencedClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Visit the constant pool entries.
+        programClass.constantPoolEntriesAccept(this);
+
+        // Visit the fields and methods.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+
+        // Visit the attributes.
+        programClass.attributesAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Visit the fields and methods.
+        libraryClass.fieldsAccept(this);
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        // Let the visitor visit the classes referenced in the descriptor string.
+        programMember.referencedClassesAccept(classVisitor);
+
+        // Visit the attributes.
+        programMember.attributesAccept(programClass, this);
+    }
+
+
+    public void visitLibraryMember(LibraryClass programClass, LibraryMember libraryMember)
+    {
+        // Let the visitor visit the classes referenced in the descriptor string.
+        libraryMember.referencedClassesAccept(classVisitor);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Let the visitor visit the class referenced in the string constant.
+        stringConstant.referencedClassAccept(classVisitor);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        // Let the visitor visit the class referenced in the reference constant.
+        refConstant.referencedClassAccept(classVisitor);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Let the visitor visit the class referenced in the class constant.
+        classConstant.referencedClassAccept(classVisitor);
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        // Let the visitor visit the class of the enclosing method.
+        enclosingMethodAttribute.referencedClassAccept(classVisitor);
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Visit the attributes of the code attribute.
+        codeAttribute.attributesAccept(clazz, method, this);
+    }
+
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+    {
+        // Visit the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+    {
+        // Visit the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+    }
+
+
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
+    {
+        // Let the visitor visit the classes referenced in the signature string.
+        signatureAttribute.referencedClassesAccept(classVisitor);
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Visit the annotations.
+        annotationsAttribute.annotationsAccept(clazz, this);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Visit the parameter annotations.
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // Visit the default element value.
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+    }
+
+
+    // Implementations for LocalVariableInfoVisitor.
+
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        // Let the visitor visit the class referenced in the local variable.
+        localVariableInfo.referencedClassAccept(classVisitor);
+    }
+
+
+    // Implementations for LocalVariableTypeInfoVisitor.
+
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        // Let the visitor visit the classes referenced in the local variable type.
+        localVariableTypeInfo.referencedClassesAccept(classVisitor);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        // Let the visitor visit the classes referenced in the annotation.
+        annotation.referencedClassesAccept(classVisitor);
+
+        // Visit the element values.
+        annotation.elementValuesAccept(clazz, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {}
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        // Let the visitor visit the classes referenced in the constant element value.
+        enumConstantElementValue.referencedClassesAccept(classVisitor);
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        // Let the visitor visit the classes referenced in the class element value.
+        classElementValue.referencedClassesAccept(classVisitor);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        // Visit the element values.
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+    }
+}
diff --git a/src/proguard/classfile/visitor/ReferencedMemberVisitor.java b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java
new file mode 100644
index 0000000..dcbb4b0
--- /dev/null
+++ b/src/proguard/classfile/visitor/ReferencedMemberVisitor.java
@@ -0,0 +1,73 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This ConstantVisitor and ElementValueVisitor lets a given MemberVisitor
+ * visit all the referenced class members of the elements that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferencedMemberVisitor
+extends      SimplifiedVisitor
+implements   ConstantVisitor,
+             ElementValueVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+
+    public ReferencedMemberVisitor(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        stringConstant.referencedMemberAccept(memberVisitor);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        refConstant.referencedMemberAccept(memberVisitor);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
+    {
+        elementValue.referencedMethodAccept(memberVisitor);
+    }
+}
diff --git a/src/proguard/classfile/visitor/SimpleClassFilePrinter.java b/src/proguard/classfile/visitor/SimpleClassFilePrinter.java
deleted file mode 100644
index ba79ec9..0000000
--- a/src/proguard/classfile/visitor/SimpleClassFilePrinter.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/* $Id: SimpleClassFilePrinter.java,v 1.18.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.optimize.*;
-import proguard.optimize.SideEffectMethodMarker;
-
-import java.io.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
- * prints out the class names of the class files it visits, and the full class
- * member descriptions of the class members it visits. The names are printed
- * in a readable, Java-like format. The access modifiers can be included or not.
- *
- * @author Eric Lafortune
- */
-public class SimpleClassFilePrinter
-  implements ClassFileVisitor,
-             MemberInfoVisitor
-{
-    private boolean     printAccessModifiers;
-    private PrintStream ps;
-
-
-    /**
-     * Creates a new SimpleClassFilePrinter that prints to
-     * <code>System.out</code>, including the access modifiers.
-     */
-    public SimpleClassFilePrinter()
-    {
-        this(true);
-    }
-
-    /**
-     * Creates a new SimpleClassFilePrinter that prints to
-     * <code>System.out</code>, with or without the access modifiers.
-     */
-    public SimpleClassFilePrinter(boolean printAccessModifiers)
-    {
-        this(printAccessModifiers, System.out);
-    }
-
-    /**
-     * Creates a new SimpleClassFilePrinter that prints to the given
-     * <code>PrintStream</code>, with or without the access modifiers.
-     */
-    public SimpleClassFilePrinter(boolean     printAccessModifiers,
-                                  PrintStream printStream)
-    {
-        this.printAccessModifiers = printAccessModifiers;
-        this.ps                   = printStream;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           programClassFile.getAccessFlags() :
-                           0,
-                       programClassFile.getName()));
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           libraryClassFile.getAccessFlags() :
-                           0,
-                       libraryClassFile.getName()));
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           programClassFile.getAccessFlags() :
-                           0,
-                       programClassFile.getName()) +
-                   ": " +
-                   ClassUtil.externalFullFieldDescription(
-                       printAccessModifiers ?
-                           programFieldInfo.getAccessFlags() :
-                           0,
-                       programFieldInfo.getName(programClassFile),
-                       programFieldInfo.getDescriptor(programClassFile)));
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           programClassFile.getAccessFlags() :
-                           0,
-                       programClassFile.getName()) +
-                   ": " +
-                   ClassUtil.externalFullMethodDescription(
-                       programClassFile.getName(),
-                       printAccessModifiers ?
-                           programMethodInfo.getAccessFlags() :
-                           0,
-                       programMethodInfo.getName(programClassFile),
-                       programMethodInfo.getDescriptor(programClassFile)));
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           libraryClassFile.getAccessFlags() :
-                           0,
-                       libraryClassFile.getName()) +
-                   ": " +
-                   ClassUtil.externalFullFieldDescription(
-                       printAccessModifiers ?
-                           libraryFieldInfo.getAccessFlags() :
-                           0,
-                       libraryFieldInfo.getName(libraryClassFile),
-                       libraryFieldInfo.getDescriptor(libraryClassFile)));
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        ps.println(ClassUtil.externalFullClassDescription(
-                       printAccessModifiers ?
-                           libraryClassFile.getAccessFlags() :
-                           0,
-                       libraryClassFile.getName()) +
-                   ": " +
-                   ClassUtil.externalFullMethodDescription(
-                       libraryClassFile.getName(),
-                       printAccessModifiers ?
-                           libraryMethodInfo.getAccessFlags() :
-                           0,
-                       libraryMethodInfo.getName(libraryClassFile),
-                       libraryMethodInfo.getDescriptor(libraryClassFile)));
-    }
-}
diff --git a/src/proguard/classfile/visitor/SimpleClassPrinter.java b/src/proguard/classfile/visitor/SimpleClassPrinter.java
new file mode 100644
index 0000000..73d41c5
--- /dev/null
+++ b/src/proguard/classfile/visitor/SimpleClassPrinter.java
@@ -0,0 +1,167 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+import java.io.PrintStream;
+
+
+/**
+ * This <code>ClassVisitor</code> and <code>MemberVisitor</code>
+ * prints out the class names of the classes it visits, and the full class
+ * member descriptions of the class members it visits. The names are printed
+ * in a readable, Java-like format. The access modifiers can be included or not.
+ *
+ * @author Eric Lafortune
+ */
+public class SimpleClassPrinter
+implements   ClassVisitor,
+             MemberVisitor
+{
+    private final boolean     printAccessModifiers;
+    private final PrintStream ps;
+
+
+    /**
+     * Creates a new SimpleClassPrinter that prints to
+     * <code>System.out</code>, including the access modifiers.
+     */
+    public SimpleClassPrinter()
+    {
+        this(true);
+    }
+
+    /**
+     * Creates a new SimpleClassPrinter that prints to
+     * <code>System.out</code>, with or without the access modifiers.
+     */
+    public SimpleClassPrinter(boolean printAccessModifiers)
+    {
+        this(printAccessModifiers, System.out);
+    }
+
+    /**
+     * Creates a new SimpleClassPrinter that prints to the given
+     * <code>PrintStream</code>, with or without the access modifiers.
+     */
+    public SimpleClassPrinter(boolean     printAccessModifiers,
+                              PrintStream printStream)
+    {
+        this.printAccessModifiers = printAccessModifiers;
+        this.ps                   = printStream;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClass.getAccessFlags() :
+                           0,
+                       programClass.getName()));
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClass.getAccessFlags() :
+                           0,
+                       libraryClass.getName()));
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClass.getAccessFlags() :
+                           0,
+                       programClass.getName()) +
+                   ": " +
+                   ClassUtil.externalFullFieldDescription(
+                       printAccessModifiers ?
+                           programField.getAccessFlags() :
+                           0,
+                       programField.getName(programClass),
+                       programField.getDescriptor(programClass)));
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           programClass.getAccessFlags() :
+                           0,
+                       programClass.getName()) +
+                   ": " +
+                   ClassUtil.externalFullMethodDescription(
+                       programClass.getName(),
+                       printAccessModifiers ?
+                           programMethod.getAccessFlags() :
+                           0,
+                       programMethod.getName(programClass),
+                       programMethod.getDescriptor(programClass)));
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClass.getAccessFlags() :
+                           0,
+                       libraryClass.getName()) +
+                   ": " +
+                   ClassUtil.externalFullFieldDescription(
+                       printAccessModifiers ?
+                           libraryField.getAccessFlags() :
+                           0,
+                       libraryField.getName(libraryClass),
+                       libraryField.getDescriptor(libraryClass)));
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        ps.println(ClassUtil.externalFullClassDescription(
+                       printAccessModifiers ?
+                           libraryClass.getAccessFlags() :
+                           0,
+                       libraryClass.getName()) +
+                   ": " +
+                   ClassUtil.externalFullMethodDescription(
+                       libraryClass.getName(),
+                       printAccessModifiers ?
+                           libraryMethod.getAccessFlags() :
+                           0,
+                       libraryMethod.getName(libraryClass),
+                       libraryMethod.getDescriptor(libraryClass)));
+    }
+}
diff --git a/src/proguard/classfile/visitor/VariableClassFileVisitor.java b/src/proguard/classfile/visitor/VariableClassFileVisitor.java
deleted file mode 100644
index 3829c63..0000000
--- a/src/proguard/classfile/visitor/VariableClassFileVisitor.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/* $Id: VariableClassFileVisitor.java,v 1.8.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This ClassFileVisitor delegates all method calls to a ClassFileVisitor
- * that can be changed at any time.
- *
- * @author Eric Lafortune
- */
-public class VariableClassFileVisitor implements ClassFileVisitor
-{
-    private ClassFileVisitor classFileVisitor;
-
-
-    public VariableClassFileVisitor()
-    {
-        this(null);
-    }
-
-
-    public VariableClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    public void setClassFileVisitor(ClassFileVisitor classFileVisitor)
-    {
-        this.classFileVisitor = classFileVisitor;
-    }
-
-    public ClassFileVisitor getClassFileVisitor()
-    {
-        return classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        if (classFileVisitor != null)
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        if (classFileVisitor != null)
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/VariableClassVisitor.java b/src/proguard/classfile/visitor/VariableClassVisitor.java
new file mode 100644
index 0000000..52d484e
--- /dev/null
+++ b/src/proguard/classfile/visitor/VariableClassVisitor.java
@@ -0,0 +1,78 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This ClassVisitor delegates all method calls to a ClassVisitor
+ * that can be changed at any time.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableClassVisitor implements ClassVisitor
+{
+    private ClassVisitor classVisitor;
+
+
+    public VariableClassVisitor()
+    {
+        this(null);
+    }
+
+
+    public VariableClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+
+    public void setClassVisitor(ClassVisitor classVisitor)
+    {
+        this.classVisitor = classVisitor;
+    }
+
+    public ClassVisitor getClassVisitor()
+    {
+        return classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (classVisitor != null)
+        {
+            classVisitor.visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        if (classVisitor != null)
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+        }
+    }
+}
diff --git a/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java b/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java
deleted file mode 100644
index 087f338..0000000
--- a/src/proguard/classfile/visitor/VariableMemberInfoVisitor.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $Id: VariableMemberInfoVisitor.java,v 1.12.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.classfile.visitor;
-
-import proguard.classfile.*;
-
-
-/**
- * This MemberInfoVisitor delegates all method calls to a MemberInfoVisitor
- * that can be changed at any time.
- *
- * @author Eric Lafortune
- */
-public class VariableMemberInfoVisitor implements MemberInfoVisitor
-{
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    public VariableMemberInfoVisitor()
-    {
-        this(null);
-    }
-
-
-    public VariableMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-
-    public void setMemberInfoVisitor(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-    public MemberInfoVisitor getMemberInfoVisitor()
-    {
-        return memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (memberInfoVisitor != null)
-        {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (memberInfoVisitor != null)
-        {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (memberInfoVisitor != null)
-        {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (memberInfoVisitor != null)
-        {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-}
diff --git a/src/proguard/classfile/visitor/VariableMemberVisitor.java b/src/proguard/classfile/visitor/VariableMemberVisitor.java
new file mode 100644
index 0000000..042c059
--- /dev/null
+++ b/src/proguard/classfile/visitor/VariableMemberVisitor.java
@@ -0,0 +1,96 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.classfile.visitor;
+
+import proguard.classfile.*;
+
+
+/**
+ * This MemberVisitor     delegates all method calls to a MemberVisitor
+ * that can be changed at any time.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableMemberVisitor implements MemberVisitor
+{
+    private MemberVisitor memberVisitor;
+
+
+    public VariableMemberVisitor()
+    {
+        this(null);
+    }
+
+
+    public VariableMemberVisitor(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    public void setMemberVisitor(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+    public MemberVisitor getMemberVisitor()
+    {
+        return memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (memberVisitor != null)
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (memberVisitor != null)
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (memberVisitor != null)
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (memberVisitor != null)
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+}
diff --git a/src/proguard/evaluation/BasicBranchUnit.java b/src/proguard/evaluation/BasicBranchUnit.java
new file mode 100644
index 0000000..a0fc96a
--- /dev/null
+++ b/src/proguard/evaluation/BasicBranchUnit.java
@@ -0,0 +1,126 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation;
+
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.evaluation.value.InstructionOffsetValue;
+
+/**
+ * This BranchUnit remembers the branch unit commands that are invoked on it.
+ * I doesn't consider conditions when branching.
+ *
+ * @author Eric Lafortune
+ */
+public class BasicBranchUnit
+implements   BranchUnit
+{
+    private boolean                wasCalled;
+    private InstructionOffsetValue traceBranchTargets;
+
+
+    /**
+     * Resets the flag that tells whether any of the branch unit commands was
+     * called.
+     */
+    public void resetCalled()
+    {
+        wasCalled = false;
+    }
+
+    /**
+     * Sets the flag that tells whether any of the branch unit commands was
+     * called.
+     */
+    protected void setCalled()
+    {
+        wasCalled = true;
+    }
+
+    /**
+     * Returns whether any of the branch unit commands was called.
+     */
+    public boolean wasCalled()
+    {
+        return wasCalled;
+    }
+
+
+    /**
+     * Sets the initial branch targets, which will be updated as the branch
+     * methods of the branch unit are called.
+     */
+    public void setTraceBranchTargets(InstructionOffsetValue branchTargets)
+    {
+        this.traceBranchTargets = branchTargets;
+    }
+
+    public InstructionOffsetValue getTraceBranchTargets()
+    {
+        return traceBranchTargets;
+    }
+
+
+    // Implementations for BranchUnit.
+
+    public void branch(Clazz         clazz,
+                       CodeAttribute codeAttribute,
+                       int           offset,
+                       int           branchTarget)
+    {
+        // Override the branch targets.
+        traceBranchTargets = new InstructionOffsetValue(branchTarget);
+
+        wasCalled = true;
+    }
+
+
+    public void branchConditionally(Clazz         clazz,
+                                    CodeAttribute codeAttribute,
+                                    int           offset,
+                                    int           branchTarget,
+                                    int           conditional)
+    {
+        // Accumulate the branch targets.
+        traceBranchTargets =
+            traceBranchTargets.generalize(new InstructionOffsetValue(branchTarget)).instructionOffsetValue();
+
+        wasCalled = true;
+    }
+
+
+    public void returnFromMethod()
+    {
+        // Stop processing this block.
+        traceBranchTargets = InstructionOffsetValue.EMPTY_VALUE;
+
+        wasCalled = true;
+    }
+
+
+    public void throwException()
+    {
+        // Stop processing this block.
+        traceBranchTargets = InstructionOffsetValue.EMPTY_VALUE;
+
+        wasCalled = true;
+    }
+}
diff --git a/src/proguard/evaluation/BasicInvocationUnit.java b/src/proguard/evaluation/BasicInvocationUnit.java
new file mode 100644
index 0000000..5c5270a
--- /dev/null
+++ b/src/proguard/evaluation/BasicInvocationUnit.java
@@ -0,0 +1,360 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.evaluation.value.*;
+
+/**
+ * This InvocationUnit sets up the variables for entering a method,
+ * and it updates the stack for the invocation of a class member,
+ * using simple values.
+ *
+ * @author Eric Lafortune
+ */
+public class BasicInvocationUnit
+extends      SimplifiedVisitor
+implements   InvocationUnit,
+             ConstantVisitor,
+             MemberVisitor
+{
+    private final ValueFactory valueFactory = new ValueFactory();
+
+    // Fields acting as parameters between the visitor methods.
+    private boolean isStatic;
+    private boolean isLoad;
+    private Stack   stack;
+    private Clazz   returnTypeClass;
+
+
+    /**
+     * Sets up the given variables for entering the given method.
+     */
+    public void enterMethod(Clazz clazz, Method method, Variables variables)
+    {
+        String descriptor = method.getDescriptor(clazz);
+
+        // Initialize the parameters.
+        boolean isStatic =
+            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
+
+        // Count the number of parameters, taking into account their categories.
+        int parameterSize = ClassUtil.internalMethodParameterSize(descriptor);
+        if (!isStatic)
+        {
+            parameterSize++;
+        }
+
+        // Reuse the existing parameters object, ensuring the right size.
+        variables.reset(parameterSize);
+
+        // Go over the parameters again.
+        InternalTypeEnumeration internalTypeEnumeration =
+            new InternalTypeEnumeration(descriptor);
+
+        int parameterIndex = 0;
+        int variableIndex  = 0;
+
+        // Put the 'this' reference in variable 0.
+        if (!isStatic)
+        {
+            // Get the reference value.
+            Value value = getMethodParameterValue(clazz,
+                                                  method,
+                                                  parameterIndex++,
+                                                  ClassUtil.internalTypeFromClassName(clazz.getName()),
+                                                  clazz);
+
+            // Store the value in variable 0.
+            variables.store(variableIndex++, value);
+        }
+
+        Clazz[] referencedClasses = ((ProgramMethod)method).referencedClasses;
+        int referencedClassIndex = 0;
+
+        // Set up the variables corresponding to the parameter types and values.
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            String type = internalTypeEnumeration.nextType();
+
+            Clazz referencedClass = referencedClasses != null &&
+                                    ClassUtil.isInternalClassType(type) ?
+                referencedClasses[referencedClassIndex++] :
+                null;
+
+            // Get the parameter value.
+            Value value = getMethodParameterValue(clazz,
+                                                  method,
+                                                  parameterIndex++,
+                                                  type,
+                                                  referencedClass);
+
+            // Store the value in the corresponding variable.
+            variables.store(variableIndex++, value);
+
+            // Increment the variable index again for Category 2 values.
+            if (value.isCategory2())
+            {
+                variableIndex++;
+            }
+        }
+    }
+
+
+    /**
+     * Exits the given method with the given return value.
+     */
+    public void exitMethod(Clazz clazz, Method method, Value returnValue)
+    {
+        setMethodReturnValue(clazz, method, returnValue);
+    }
+
+
+    /**
+     * Updates the given stack corresponding to the execution of the given
+     * field or method reference instruction.
+     */
+    public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack)
+    {
+        int constantIndex = constantInstruction.constantIndex;
+
+        switch (constantInstruction.opcode)
+        {
+            case InstructionConstants.OP_GETSTATIC:
+                isStatic = true;
+                isLoad   = true;
+                break;
+
+            case InstructionConstants.OP_PUTSTATIC:
+                isStatic = true;
+                isLoad   = false;
+                break;
+
+            case InstructionConstants.OP_GETFIELD:
+                isStatic = false;
+                isLoad   = true;
+                break;
+
+            case InstructionConstants.OP_PUTFIELD:
+                isStatic = false;
+                isLoad   = false;
+                break;
+
+            case InstructionConstants.OP_INVOKESTATIC:
+                isStatic = true;
+                break;
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                isStatic = false;
+                break;
+        }
+
+        // Pop the parameters and push the return value.
+        this.stack = stack;
+        clazz.constantPoolEntryAccept(constantIndex, this);
+        this.stack = null;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        // Pop the field value, if applicable.
+        if (!isLoad)
+        {
+            setFieldValue(clazz, fieldrefConstant, stack.pop());
+        }
+
+        // Pop the reference value, if applicable.
+        if (!isStatic)
+        {
+            setFieldClassValue(clazz, fieldrefConstant, stack.apop());
+        }
+
+        // Push the field value, if applicable.
+        if (isLoad)
+        {
+            String type = fieldrefConstant.getType(clazz);
+
+            stack.push(getFieldValue(clazz, fieldrefConstant, type));
+        }
+    }
+
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant)
+    {
+        String type = methodrefConstant.getType(clazz);
+
+        // Count the number of parameters.
+        int parameterCount = ClassUtil.internalMethodParameterCount(type);
+        if (!isStatic)
+        {
+            parameterCount++;
+        }
+
+        // Pop the parameters and the class reference, in reverse order.
+        for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--)
+        {
+            setMethodParameterValue(clazz, methodrefConstant, parameterIndex, stack.pop());
+        }
+
+        // Pop the reference value, if applicable.
+        // Push the return value, if applicable.
+        String returnType = ClassUtil.internalMethodReturnType(type);
+        if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID)
+        {
+            stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType));
+        }
+    }
+
+
+    protected void setFieldClassValue(Clazz          clazz,
+                                      RefConstant    refConstant,
+                                      ReferenceValue value)
+    {
+        // We don't care about the new value.
+    }
+
+
+    protected Value getFieldClassValue(Clazz       clazz,
+                                       RefConstant refConstant,
+                                       String      type)
+    {
+        // Try to figure out the class of the return type.
+        returnTypeClass = null;
+        refConstant.referencedMemberAccept(this);
+
+        return valueFactory.createValue(type,
+                                        returnTypeClass,
+                                        true);
+    }
+
+
+    protected void setFieldValue(Clazz       clazz,
+                                 RefConstant refConstant,
+                                 Value       value)
+    {
+        // We don't care about the new field value.
+    }
+
+
+    protected Value getFieldValue(Clazz       clazz,
+                                  RefConstant refConstant,
+                                  String      type)
+    {
+        // Try to figure out the class of the return type.
+        returnTypeClass = null;
+        refConstant.referencedMemberAccept(this);
+
+        return valueFactory.createValue(type,
+                                        returnTypeClass,
+                                        true);
+    }
+
+
+    protected void setMethodParameterValue(Clazz       clazz,
+                                           RefConstant refConstant,
+                                           int         parameterIndex,
+                                           Value       value)
+    {
+        // We don't care about the parameter value.
+    }
+
+
+    protected Value getMethodParameterValue(Clazz  clazz,
+                                            Method method,
+                                            int    parameterIndex,
+                                            String type,
+                                            Clazz  referencedClass)
+    {
+        return valueFactory.createValue(type, referencedClass, true);
+    }
+
+
+    protected void setMethodReturnValue(Clazz  clazz,
+                                        Method method,
+                                        Value  value)
+    {
+        // We don't care about the return value.
+    }
+
+
+    protected Value getMethodReturnValue(Clazz       clazz,
+                                         RefConstant refConstant,
+                                         String      type)
+    {
+        // Try to figure out the class of the return type.
+        returnTypeClass = null;
+        refConstant.referencedMemberAccept(this);
+
+        return valueFactory.createValue(type,
+                                        returnTypeClass,
+                                        true);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        returnTypeClass = programField.referencedClass;
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        Clazz[] referencedClasses = programMethod.referencedClasses;
+        if (referencedClasses != null)
+        {
+            returnTypeClass = referencedClasses[referencedClasses.length - 1];
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass programClass, LibraryField programField)
+    {
+        returnTypeClass = programField.referencedClass;
+    }
+
+
+    public void visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod)
+    {
+        Clazz[] referencedClasses = programMethod.referencedClasses;
+        if (referencedClasses != null)
+        {
+            returnTypeClass = referencedClasses[referencedClasses.length - 1];
+        }
+    }
+
+
+//    public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)
+//    {
+//    }
+}
diff --git a/src/proguard/optimize/evaluation/BranchUnit.java b/src/proguard/evaluation/BranchUnit.java
similarity index 60%
rename from src/proguard/optimize/evaluation/BranchUnit.java
rename to src/proguard/evaluation/BranchUnit.java
index 2d47ec9..73c775b 100644
--- a/src/proguard/optimize/evaluation/BranchUnit.java
+++ b/src/proguard/evaluation/BranchUnit.java
@@ -1,6 +1,6 @@
-/* $Id: BranchUnit.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,11 +18,10 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation;
+package proguard.evaluation;
 
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.optimize.evaluation.value.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.CodeAttribute;
 
 /**
  * This InstructionVisitor evaluates the instructions that it visits.
@@ -34,27 +33,27 @@ public interface BranchUnit
     /**
      * Sets the new instruction offset.
      */
-    public void branch(ClassFile    classFile,
-                       CodeAttrInfo codeAttrInfo,
-                       int          offset,
-                       int          branchTarget);
+    public void branch(Clazz         clazz,
+                       CodeAttribute codeAttribute,
+                       int           offset,
+                       int           branchTarget);
 
 
     /**
      * Sets the new instruction offset, depending on the certainty of the
      * conditional branch.
      */
-    public void branchConditionally(ClassFile    classFile,
-                                    CodeAttrInfo codeAttrInfo,
-                                    int          offset,
-                                    int          branchTarget,
-                                    int          conditional);
+    public void branchConditionally(Clazz         clazz,
+                                    CodeAttribute codeAttribute,
+                                    int           offset,
+                                    int           branchTarget,
+                                    int           conditional);
 
 
     /**
      * Returns from the method with the given value.
      */
-    public void returnFromMethod(Value returnValue);
+    public void returnFromMethod();
 
 
     /**
diff --git a/src/proguard/evaluation/InvocationUnit.java b/src/proguard/evaluation/InvocationUnit.java
new file mode 100644
index 0000000..47a4a97
--- /dev/null
+++ b/src/proguard/evaluation/InvocationUnit.java
@@ -0,0 +1,62 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.ConstantInstruction;
+import proguard.evaluation.value.Value;
+
+/**
+ * This interface sets up the variables for entering a method,
+ * and it updates the stack for the invocation of a class member.
+ *
+ * @author Eric Lafortune
+ */
+public interface InvocationUnit
+{
+    /**
+     * Sets up the given variables for entering the given method.
+     */
+    public void enterMethod(Clazz     clazz,
+                            Method    method,
+                            Variables variables);
+
+
+    /**
+     * Exits the given method with the given return value.
+     */
+    public void exitMethod(Clazz  clazz,
+                           Method method,
+                           Value  returnValue);
+
+
+    /**
+     * Updates the given stack corresponding to the execution of the given
+     * field or method reference instruction.
+     */
+    public void invokeMember(Clazz               clazz,
+                             Method              method,
+                             CodeAttribute       codeAttribute,
+                             int                 offset,
+                             ConstantInstruction constantInstruction,
+                             Stack               stack);
+}
diff --git a/src/proguard/evaluation/MutableValue.java b/src/proguard/evaluation/MutableValue.java
new file mode 100644
index 0000000..c6e12d6
--- /dev/null
+++ b/src/proguard/evaluation/MutableValue.java
@@ -0,0 +1,149 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation;
+
+import proguard.evaluation.value.*;
+
+/**
+ * This Value is a mutable wrapper for other Value instances.
+ * Its generalization method affects the contained Value as a side-effect.
+ */
+class MutableValue extends Category1Value
+{
+    private Value containedValue;
+
+
+    /**
+     * Generalizes the contained value with the given value.
+     * @param otherContainedValue the other value.
+     */
+    public void generalizeContainedValue(Value otherContainedValue)
+    {
+        MutableValue lastMutableValue  = lastMutableValue();
+        Value        lastContainedValue= lastMutableValue.containedValue;
+
+        lastMutableValue.containedValue =
+            lastContainedValue == null ? otherContainedValue :
+                                         otherContainedValue.generalize(lastContainedValue);
+    }
+
+
+    /**
+     * Sets the contained value.
+     */
+    public void setContainedValue(Value containedValue)
+    {
+        lastMutableValue().containedValue = containedValue;
+    }
+
+
+    /**
+     * Returns the contained value.
+     */
+    public Value getContainedValue()
+    {
+        return lastMutableValue().containedValue;
+    }
+
+
+    // Implementations for Value.
+
+    public Value generalize(Value other)
+    {
+        MutableValue otherMutableValue = (MutableValue)other;
+
+        MutableValue thisLastMutableValue  = this.lastMutableValue();
+        MutableValue otherLastMutableValue = otherMutableValue.lastMutableValue();
+
+        Value thisLastContainedValue  = thisLastMutableValue.containedValue;
+        Value otherLastContainedValue = otherLastMutableValue.containedValue;
+
+        if (thisLastMutableValue != otherLastMutableValue)
+        {
+            otherLastMutableValue.containedValue = thisLastMutableValue;
+        }
+
+        thisLastMutableValue.containedValue =
+            thisLastContainedValue  == null ? otherLastContainedValue :
+            otherLastContainedValue == null ? thisLastContainedValue  :
+                                              thisLastContainedValue.generalize(otherLastContainedValue);
+        return thisLastMutableValue;
+    }
+
+
+    public int computationalType()
+    {
+        return 0;
+    }
+
+    public final String internalType()
+    {
+        return null;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        MutableValue other = (MutableValue)object;
+        Value thisContainedValue  = this.getContainedValue();
+        Value otherContainedValue = other.getContainedValue();
+        return thisContainedValue == null ?
+            otherContainedValue == null :
+            thisContainedValue.equals(otherContainedValue);
+    }
+
+
+    public int hashCode()
+    {
+        Value containedValue  = getContainedValue();
+        return this.getClass().hashCode() ^
+               (containedValue == null ? 0 : containedValue.hashCode());
+    }
+
+
+    public String toString()
+    {
+        return containedValue == null ? "none" : containedValue.toString();
+    }
+
+
+    // Small utility methods.
+
+    public MutableValue lastMutableValue()
+    {
+        MutableValue mutableValue = this;
+
+        while (mutableValue.containedValue instanceof MutableValue)
+        {
+            mutableValue = (MutableValue)mutableValue.containedValue;
+        }
+
+        return mutableValue;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/Processor.java b/src/proguard/evaluation/Processor.java
similarity index 64%
rename from src/proguard/optimize/evaluation/Processor.java
rename to src/proguard/evaluation/Processor.java
index 9670157..6a27dec 100644
--- a/src/proguard/optimize/evaluation/Processor.java
+++ b/src/proguard/evaluation/Processor.java
@@ -1,6 +1,6 @@
-/* $Id: Processor.java,v 1.13.2.5 2007/04/05 21:40:07 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,14 +18,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation;
+package proguard.evaluation;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.instruction.*;
-import proguard.classfile.util.ClassUtil;
-import proguard.classfile.visitor.*;
-import proguard.optimize.evaluation.value.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.evaluation.value.*;
 
 /**
  * This InstructionVisitor executes the instructions that it visits on a given
@@ -34,39 +36,45 @@ import proguard.optimize.evaluation.value.*;
  * @author Eric Lafortune
  */
 public class Processor
+extends      SimplifiedVisitor
 implements   InstructionVisitor,
-             CpInfoVisitor
+             ConstantVisitor
 {
-    private Variables  variables;
-    private Stack      stack;
-    private BranchUnit branchUnit;
+    private final Variables      variables;
+    private final Stack          stack;
+    private final ValueFactory   valueFactory;
+    private final BranchUnit     branchUnit;
+    private final InvocationUnit invocationUnit;
 
-    // Fields acting as return parameters for the CpInfoVisitor methods.
-    private int       parameterCount;
-    private Value     cpValue;
-    private ClassFile referencedClassFile;
-    private int       referencedTypeDimensionCount;
+    // Fields acting as parameters for the ConstantVisitor methods.
+    private boolean handleClassConstantAsClassValue;
+    private Value   cpValue;
 
 
     /**
      * Creates a new processor that operates on the given environment.
-     * @param variables  the local variable frame.
-     * @param stack      the local stack.
-     * @param branchUnit the class that can affect the program counter.
+     * @param variables      the local variable frame.
+     * @param stack          the local stack.
+     * @param branchUnit     the class that can affect the program counter.
+     * @param invocationUnit the class that can access other program members.
      */
-    public Processor(Variables  variables,
-                     Stack      stack,
-                     BranchUnit branchUnit)
+    public Processor(Variables      variables,
+                     Stack          stack,
+                     ValueFactory   valueFactory,
+                     BranchUnit     branchUnit,
+                     InvocationUnit invocationUnit)
     {
-        this.variables  = variables;
-        this.stack      = stack;
-        this.branchUnit = branchUnit;
+        this.variables      = variables;
+        this.stack          = stack;
+        this.valueFactory   = valueFactory;
+        this.branchUnit     = branchUnit;
+        this.invocationUnit = invocationUnit;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         switch (simpleInstruction.opcode)
         {
@@ -74,7 +82,7 @@ implements   InstructionVisitor,
                 break;
 
             case InstructionConstants.OP_ACONST_NULL:
-                stack.push(ReferenceValueFactory.createNull());
+                stack.push(valueFactory.createReferenceValueNull());
                 break;
 
             case InstructionConstants.OP_ICONST_M1:
@@ -86,23 +94,23 @@ implements   InstructionVisitor,
             case InstructionConstants.OP_ICONST_5:
             case InstructionConstants.OP_BIPUSH:
             case InstructionConstants.OP_SIPUSH:
-                stack.push(IntegerValueFactory.create(simpleInstruction.constant));
+                stack.push(valueFactory.createIntegerValue(simpleInstruction.constant));
                 break;
 
             case InstructionConstants.OP_LCONST_0:
             case InstructionConstants.OP_LCONST_1:
-                stack.push(LongValueFactory.create(simpleInstruction.constant));
+                stack.push(valueFactory.createLongValue(simpleInstruction.constant));
                 break;
 
             case InstructionConstants.OP_FCONST_0:
             case InstructionConstants.OP_FCONST_1:
             case InstructionConstants.OP_FCONST_2:
-                stack.push(FloatValueFactory.create((float)simpleInstruction.constant));
+                stack.push(valueFactory.createFloatValue((float)simpleInstruction.constant));
                 break;
 
             case InstructionConstants.OP_DCONST_0:
             case InstructionConstants.OP_DCONST_1:
-                stack.push(DoubleValueFactory.create((double)simpleInstruction.constant));
+                stack.push(valueFactory.createDoubleValue((double)simpleInstruction.constant));
                 break;
 
             case InstructionConstants.OP_IALOAD:
@@ -111,32 +119,34 @@ implements   InstructionVisitor,
             case InstructionConstants.OP_SALOAD:
                 stack.ipop();
                 stack.apop();
-                stack.push(IntegerValueFactory.create());
+                stack.push(valueFactory.createIntegerValue());
                 break;
 
             case InstructionConstants.OP_LALOAD:
                 stack.ipop();
                 stack.apop();
-                stack.push(LongValueFactory.create());
+                stack.push(valueFactory.createLongValue());
                 break;
 
             case InstructionConstants.OP_FALOAD:
                 stack.ipop();
                 stack.apop();
-                stack.push(FloatValueFactory.create());
+                stack.push(valueFactory.createFloatValue());
                 break;
 
             case InstructionConstants.OP_DALOAD:
                 stack.ipop();
                 stack.apop();
-                stack.push(DoubleValueFactory.create());
+                stack.push(valueFactory.createDoubleValue());
                 break;
 
             case InstructionConstants.OP_AALOAD:
-                stack.ipop();
-                stack.apop();
-                stack.push(ReferenceValueFactory.create(true));
+            {
+                IntegerValue   arrayIndex     = stack.ipop();
+                ReferenceValue arrayReference = stack.apop();
+                stack.push(arrayReference.arrayLoad(arrayIndex, valueFactory));
                 break;
+            }
 
             case InstructionConstants.OP_IASTORE:
             case InstructionConstants.OP_BASTORE:
@@ -262,10 +272,10 @@ implements   InstructionVisitor,
                 }
                 catch (ArithmeticException ex)
                 {
-                    stack.push(IntegerValueFactory.create());
+                    stack.push(valueFactory.createIntegerValue());
                     // TODO: Forward ArithmeticExceptions.
                     //stack.clear();
-                    //stack.push(ReferenceValueFactory.create(false));
+                    //stack.push(valueFactory.createReference(false));
                     //branchUnit.throwException();
                 }
                 break;
@@ -273,11 +283,11 @@ implements   InstructionVisitor,
             case InstructionConstants.OP_LDIV:
                 try
                 {
-                    stack.push(stack.lpop().remainderOf(stack.lpop()));
+                    stack.push(stack.lpop().divideOf(stack.lpop()));
                 }
                 catch (ArithmeticException ex)
                 {
-                    stack.push(LongValueFactory.create());
+                    stack.push(valueFactory.createLongValue());
                     // TODO: Forward ArithmeticExceptions.
                     //stack.clear();
                     //stack.push(valueFactory.createReference(false));
@@ -300,10 +310,10 @@ implements   InstructionVisitor,
                 }
                 catch (ArithmeticException ex)
                 {
-                    stack.push(IntegerValueFactory.create());
+                    stack.push(valueFactory.createIntegerValue());
                     // TODO: Forward ArithmeticExceptions.
                     //stack.clear();
-                    //stack.push(ReferenceValueFactory.create(false));
+                    //stack.push(valueFactory.createReference(false));
                     //branchUnit.throwException();
                 }
                 break;
@@ -315,7 +325,7 @@ implements   InstructionVisitor,
                 }
                 catch (ArithmeticException ex)
                 {
-                    stack.push(LongValueFactory.create());
+                    stack.push(valueFactory.createLongValue());
                     // TODO: Forward ArithmeticExceptions.
                     //stack.clear();
                     //stack.push(valueFactory.createReference(false));
@@ -396,121 +406,132 @@ implements   InstructionVisitor,
                 break;
 
             case InstructionConstants.OP_I2L:
-                stack.push(stack.ipop().convertToLong());
+                stack.push(stack.ipop().convertToLong(valueFactory));
                 break;
 
             case InstructionConstants.OP_I2F:
-                stack.push(stack.ipop().convertToFloat());
+                stack.push(stack.ipop().convertToFloat(valueFactory));
                 break;
 
             case InstructionConstants.OP_I2D:
-                stack.push(stack.ipop().convertToDouble());
+                stack.push(stack.ipop().convertToDouble(valueFactory));
                 break;
 
             case InstructionConstants.OP_L2I:
-                stack.push(stack.lpop().convertToInteger());
+                stack.push(stack.lpop().convertToInteger(valueFactory));
                 break;
 
             case InstructionConstants.OP_L2F:
-                stack.push(stack.lpop().convertToFloat());
+                stack.push(stack.lpop().convertToFloat(valueFactory));
                 break;
 
             case InstructionConstants.OP_L2D:
-                stack.push(stack.lpop().convertToDouble());
+                stack.push(stack.lpop().convertToDouble(valueFactory));
                 break;
 
             case InstructionConstants.OP_F2I:
-                stack.push(stack.fpop().convertToInteger());
+                stack.push(stack.fpop().convertToInteger(valueFactory));
                 break;
 
             case InstructionConstants.OP_F2L:
-                stack.push(stack.fpop().convertToLong());
+                stack.push(stack.fpop().convertToLong(valueFactory));
                 break;
 
             case InstructionConstants.OP_F2D:
-                stack.push(stack.fpop().convertToDouble());
+                stack.push(stack.fpop().convertToDouble(valueFactory));
                 break;
 
             case InstructionConstants.OP_D2I:
-                stack.push(stack.dpop().convertToInteger());
+                stack.push(stack.dpop().convertToInteger(valueFactory));
                 break;
 
             case InstructionConstants.OP_D2L:
-                stack.push(stack.dpop().convertToLong());
+                stack.push(stack.dpop().convertToLong(valueFactory));
                 break;
 
             case InstructionConstants.OP_D2F:
-                stack.push(stack.dpop().convertToFloat());
+                stack.push(stack.dpop().convertToFloat(valueFactory));
                 break;
 
             case InstructionConstants.OP_I2B:
-                stack.push(stack.ipop().convertToByte());
+                stack.push(stack.ipop().convertToByte(valueFactory));
                 break;
 
             case InstructionConstants.OP_I2C:
-                stack.push(stack.ipop().convertToCharacter());
+                stack.push(stack.ipop().convertToCharacter(valueFactory));
                 break;
 
             case InstructionConstants.OP_I2S:
-                stack.push(stack.ipop().convertToShort());
+                stack.push(stack.ipop().convertToShort(valueFactory));
                 break;
 
             case InstructionConstants.OP_LCMP:
-                stack.push(stack.lpop().compareReverse(stack.lpop()));
+//                stack.push(stack.lpop().compareReverse(stack.lpop()));
+
+                LongValue longValue1 = stack.lpop();
+                LongValue longValue2 = stack.lpop();
+                stack.push(longValue2.compare(longValue1, valueFactory));
                 break;
 
             case InstructionConstants.OP_FCMPL:
                 FloatValue floatValue1 = stack.fpop();
                 FloatValue floatValue2 = stack.fpop();
-                stack.push(floatValue2.compare(floatValue1));
+                stack.push(floatValue2.compare(floatValue1, valueFactory));
                 break;
 
             case InstructionConstants.OP_FCMPG:
-                stack.push(stack.fpop().compareReverse(stack.fpop()));
+                stack.push(stack.fpop().compareReverse(stack.fpop(), valueFactory));
                 break;
 
             case InstructionConstants.OP_DCMPL:
                 DoubleValue doubleValue1 = stack.dpop();
                 DoubleValue doubleValue2 = stack.dpop();
-                stack.push(doubleValue2.compare(doubleValue1));
+                stack.push(doubleValue2.compare(doubleValue1, valueFactory));
                 break;
 
             case InstructionConstants.OP_DCMPG:
-                stack.push(stack.dpop().compareReverse(stack.dpop()));
+                stack.push(stack.dpop().compareReverse(stack.dpop(), valueFactory));
                 break;
 
             case InstructionConstants.OP_IRETURN:
-                branchUnit.returnFromMethod(stack.ipop());
+                invocationUnit.exitMethod(clazz, method, stack.ipop());
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_LRETURN:
-                branchUnit.returnFromMethod(stack.lpop());
+                invocationUnit.exitMethod(clazz, method, stack.lpop());
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_FRETURN:
-                branchUnit.returnFromMethod(stack.fpop());
+                invocationUnit.exitMethod(clazz, method, stack.fpop());
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_DRETURN:
-                branchUnit.returnFromMethod(stack.dpop());
+                invocationUnit.exitMethod(clazz, method, stack.dpop());
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_ARETURN:
-                branchUnit.returnFromMethod(stack.apop());
+                invocationUnit.exitMethod(clazz, method, stack.apop());
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_RETURN:
-                branchUnit.returnFromMethod(null);
+                branchUnit.returnFromMethod();
                 break;
 
             case InstructionConstants.OP_NEWARRAY:
-                stack.ipop();
-                stack.push(ReferenceValueFactory.create(false));
+                IntegerValue arrayLength = stack.ipop();
+                stack.push(valueFactory.createArrayReferenceValue(String.valueOf(InstructionUtil.internalTypeFromArrayType((byte)simpleInstruction.constant)),
+                                                                  null,
+                                                                  arrayLength));
                 break;
 
             case InstructionConstants.OP_ARRAYLENGTH:
                 stack.apop();
-                stack.push(IntegerValueFactory.create());
+                stack.push(valueFactory.createIntegerValue());
                 break;
 
             case InstructionConstants.OP_ATHROW:
@@ -531,103 +552,86 @@ implements   InstructionVisitor,
     }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
-        int cpIndex = cpInstruction.cpIndex;
+        int constantIndex = constantInstruction.constantIndex;
 
-        switch (cpInstruction.opcode)
+        switch (constantInstruction.opcode)
         {
             case InstructionConstants.OP_LDC:
             case InstructionConstants.OP_LDC_W:
             case InstructionConstants.OP_LDC2_W:
-                Value cpValue = cpValue(classFile, cpIndex);
-                // The constant can be a class constant, which actually loads
-                // the java.lang.Class type.
-                stack.push(cpValue.computationalType() == Value.TYPE_REFERENCE ?
-                    ReferenceValueFactory.create(false) :
-                    cpValue);
+                stack.push(cpValue(clazz, constantIndex, true));
                 break;
 
             case InstructionConstants.OP_GETSTATIC:
-                stack.push(cpValue(classFile, cpIndex));
-                break;
-
             case InstructionConstants.OP_PUTSTATIC:
-                stack.pop();
-                break;
-
             case InstructionConstants.OP_GETFIELD:
-                stack.apop();
-                stack.push(cpValue(classFile, cpIndex));
-                break;
-
             case InstructionConstants.OP_PUTFIELD:
-                stack.pop();
-                stack.apop();
-                break;
-
             case InstructionConstants.OP_INVOKEVIRTUAL:
             case InstructionConstants.OP_INVOKESPECIAL:
             case InstructionConstants.OP_INVOKESTATIC:
             case InstructionConstants.OP_INVOKEINTERFACE:
-                cpValue = cpValue(classFile, cpIndex);
-                int parameterCount = parameterCount(classFile, cpIndex);
-
-                for (int counter = 0; counter < parameterCount; counter++)
-                {
-                    stack.pop();
-                }
-                if (cpInstruction.opcode != InstructionConstants.OP_INVOKESTATIC)
-                {
-                    stack.apop();
-                }
-                if (cpValue != null)
-                {
-                    stack.push(cpValue);
-                }
+                invocationUnit.invokeMember(clazz, method, codeAttribute, offset, constantInstruction, stack);
                 break;
 
             case InstructionConstants.OP_NEW:
-                stack.push(cpValue(classFile, cpIndex));
+                stack.push(cpValue(clazz, constantIndex).referenceValue());
                 break;
 
             case InstructionConstants.OP_ANEWARRAY:
-                stack.ipop();
-                stack.push(ReferenceValueFactory.create(referencedClassFile(classFile, cpIndex),
-                                                        1,
-                                                        false));
+            {
+                ReferenceValue referenceValue = cpValue(clazz, constantIndex).referenceValue();
+
+                stack.push(valueFactory.createArrayReferenceValue(referenceValue.internalType(),
+                                                                  referenceValue.getReferencedClass(),
+                                                                  stack.ipop()));
                 break;
+            }
 
             case InstructionConstants.OP_CHECKCAST:
                 // TODO: Check cast.
-                stack.push(stack.apop());
+                ReferenceValue castValue = stack.apop();
+                ReferenceValue castResultValue =
+                    castValue.isNull() == Value.ALWAYS ? castValue :
+                    castValue.isNull() == Value.NEVER  ? cpValue(clazz, constantIndex).referenceValue() :
+                                                         cpValue(clazz, constantIndex).referenceValue().generalize(valueFactory.createReferenceValueNull());
+                stack.push(castResultValue);
                 break;
 
             case InstructionConstants.OP_INSTANCEOF:
-                int instanceOf = stack.apop().instanceOf(referencedClassFile(classFile, cpIndex),
-                                                         referencedTypeDimensionCount(classFile, cpIndex));
+            {
+                ReferenceValue referenceValue = cpValue(clazz, constantIndex).referenceValue();
+
+                int instanceOf = stack.apop().instanceOf(referenceValue.getType(),
+                                                         referenceValue.getReferencedClass());
 
-                stack.push(instanceOf == Value.NEVER  ? IntegerValueFactory.create(0) :
-                           instanceOf == Value.ALWAYS ? IntegerValueFactory.create(1) :
-                                                        IntegerValueFactory.create());
+                stack.push(instanceOf == Value.NEVER  ? valueFactory.createIntegerValue(0) :
+                           instanceOf == Value.ALWAYS ? valueFactory.createIntegerValue(1) :
+                                                        valueFactory.createIntegerValue());
                 break;
+            }
 
             case InstructionConstants.OP_MULTIANEWARRAY:
-                int dimensionCount = cpInstruction.constant;
+            {
+                int dimensionCount = constantInstruction.constant;
                 for (int dimension = 0; dimension < dimensionCount; dimension++)
                 {
-                    stack.ipop();
+                    // TODO: Use array lengths.
+                    IntegerValue arrayLength = stack.ipop();
                 }
-                stack.push(cpValue(classFile, cpIndex));
+
+                stack.push(cpValue(clazz, constantIndex).referenceValue());
                 break;
+            }
 
             default:
-                throw new IllegalArgumentException("Unknown constant pool instruction ["+cpInstruction.opcode+"]");
+                throw new IllegalArgumentException("Unknown constant pool instruction ["+constantInstruction.opcode+"]");
         }
     }
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         int variableIndex = variableInstruction.variableIndex;
 
@@ -719,7 +723,7 @@ implements   InstructionVisitor,
             case InstructionConstants.OP_IINC:
                 variables.store(variableIndex,
                                 variables.iload(variableIndex).add(
-                                IntegerValueFactory.create(variableInstruction.constant)));
+                                valueFactory.createIntegerValue(variableInstruction.constant)));
                 break;
 
             case InstructionConstants.OP_RET:
@@ -727,8 +731,8 @@ implements   InstructionVisitor,
                 // given instruction offset variable (even though there may
                 // be other offsets).
                 InstructionOffsetValue instructionOffsetValue = variables.oload(variableIndex);
-                branchUnit.branch(classFile,
-                                  codeAttrInfo,
+                branchUnit.branch(clazz,
+                                  codeAttribute,
                                   offset,
                                   instructionOffsetValue.instructionOffset(instructionOffsetValue.instructionOffsetCount()-1));
                 break;
@@ -739,108 +743,107 @@ implements   InstructionVisitor,
     }
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         int branchTarget = offset + branchInstruction.branchOffset;
 
         switch (branchInstruction.opcode)
         {
             case InstructionConstants.OP_IFEQ:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().equal(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().equal(valueFactory.createIntegerValue(0)));
                 break;
 
             case InstructionConstants.OP_IFNE:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().notEqual(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().notEqual(valueFactory.createIntegerValue(0)));
                 break;
 
             case InstructionConstants.OP_IFLT:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().lessThan(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().lessThan(valueFactory.createIntegerValue(0)));
                 break;
 
             case InstructionConstants.OP_IFGE:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().greaterThanOrEqual(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().greaterThanOrEqual(valueFactory.createIntegerValue(0)));
                 break;
 
             case InstructionConstants.OP_IFGT:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().greaterThan(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().greaterThan(valueFactory.createIntegerValue(0)));
                 break;
 
             case InstructionConstants.OP_IFLE:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
-                    stack.ipop().lessThanOrEqual(IntegerValueFactory.create(0)));
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
+                    stack.ipop().lessThanOrEqual(valueFactory.createIntegerValue(0)));
                 break;
 
 
             case InstructionConstants.OP_IFICMPEQ:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().equal(stack.ipop()));
                 break;
 
             case InstructionConstants.OP_IFICMPNE:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().notEqual(stack.ipop()));
                 break;
 
-
             case InstructionConstants.OP_IFICMPLT:
                 // Note that the stack entries are popped in reverse order.
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().greaterThan(stack.ipop()));
                 break;
 
             case InstructionConstants.OP_IFICMPGE:
                 // Note that the stack entries are popped in reverse order.
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().lessThanOrEqual(stack.ipop()));
                 break;
 
             case InstructionConstants.OP_IFICMPGT:
                 // Note that the stack entries are popped in reverse order.
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().lessThan(stack.ipop()));
                 break;
 
             case InstructionConstants.OP_IFICMPLE:
                 // Note that the stack entries are popped in reverse order.
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.ipop().greaterThanOrEqual(stack.ipop()));
                 break;
 
             case InstructionConstants.OP_IFACMPEQ:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.apop().equal(stack.apop()));
                 break;
 
             case InstructionConstants.OP_IFACMPNE:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.apop().notEqual(stack.apop()));
                 break;
 
             case InstructionConstants.OP_GOTO:
             case InstructionConstants.OP_GOTO_W:
-                branchUnit.branch(classFile, codeAttrInfo, offset, branchTarget);
+                branchUnit.branch(clazz, codeAttribute, offset, branchTarget);
                 break;
 
 
             case InstructionConstants.OP_JSR:
             case InstructionConstants.OP_JSR_W:
-                stack.push(InstructionOffsetValueFactory.create(offset +
-                                                                branchInstruction.length(offset)));
-                branchUnit.branch(classFile, codeAttrInfo, offset, branchTarget);
+                stack.push(new InstructionOffsetValue(offset +
+                                                      branchInstruction.length(offset)));
+                branchUnit.branch(clazz, codeAttribute, offset, branchTarget);
                 break;
 
             case InstructionConstants.OP_IFNULL:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.apop().isNull());
                 break;
 
             case InstructionConstants.OP_IFNONNULL:
-                branchUnit.branchConditionally(classFile, codeAttrInfo, offset, branchTarget,
+                branchUnit.branchConditionally(clazz, codeAttribute, offset, branchTarget,
                     stack.apop().isNotNull());
                 break;
 
@@ -850,21 +853,21 @@ implements   InstructionVisitor,
     }
 
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
     {
         IntegerValue indexValue = stack.ipop();
 
         // If there is no definite branch in any of the cases below,
         // branch to the default offset.
-        branchUnit.branch(classFile, codeAttrInfo,
+        branchUnit.branch(clazz, codeAttribute,
                           offset,
                           offset + tableSwitchInstruction.defaultOffset);
 
-        for (int index = 0; index < tableSwitchInstruction.jumpOffsetCount; index++)
+        for (int index = 0; index < tableSwitchInstruction.jumpOffsets.length; index++)
         {
-            int conditional = indexValue.equal(IntegerValueFactory.create(
+            int conditional = indexValue.equal(valueFactory.createIntegerValue(
                 tableSwitchInstruction.lowCase + index));
-            branchUnit.branchConditionally(classFile, codeAttrInfo,
+            branchUnit.branchConditionally(clazz, codeAttribute,
                                            offset,
                                            offset + tableSwitchInstruction.jumpOffsets[index],
                                            conditional);
@@ -878,21 +881,21 @@ implements   InstructionVisitor,
     }
 
 
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
     {
         IntegerValue indexValue = stack.ipop();
 
         // If there is no definite branch in any of the cases below,
         // branch to the default offset.
-        branchUnit.branch(classFile, codeAttrInfo,
+        branchUnit.branch(clazz, codeAttribute,
                           offset,
                           offset + lookUpSwitchInstruction.defaultOffset);
 
-        for (int index = 0; index < lookUpSwitchInstruction.jumpOffsetCount; index++)
+        for (int index = 0; index < lookUpSwitchInstruction.jumpOffsets.length; index++)
         {
-            int conditional = indexValue.equal(IntegerValueFactory.create(
+            int conditional = indexValue.equal(valueFactory.createIntegerValue(
                 lookUpSwitchInstruction.cases[index]));
-            branchUnit.branchConditionally(classFile, codeAttrInfo,
+            branchUnit.branchConditionally(clazz, codeAttribute,
                                            offset,
                                            offset + lookUpSwitchInstruction.jumpOffsets[index],
                                            conditional);
@@ -906,70 +909,44 @@ implements   InstructionVisitor,
     }
 
 
-    // Implementations for CpInfoVisitor.
-
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
-    {
-        cpValue = IntegerValueFactory.create(integerCpInfo.getValue());
-    }
-
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
-    {
-        cpValue = LongValueFactory.create(longCpInfo.getValue());
-    }
+    // Implementations for ConstantVisitor.
 
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
     {
-        cpValue = FloatValueFactory.create(floatCpInfo.getValue());
+        cpValue = valueFactory.createIntegerValue(integerConstant.getValue());
     }
 
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
     {
-        cpValue = DoubleValueFactory.create(doubleCpInfo.getValue());
+        cpValue = valueFactory.createLongValue(longConstant.getValue());
     }
 
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
     {
-        cpValue = ReferenceValueFactory.create(false);
+        cpValue = valueFactory.createFloatValue(floatConstant.getValue());
     }
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
     {
-        cpValue = ValueFactory.create(fieldrefCpInfo.getType(classFile));
+        cpValue = valueFactory.createDoubleValue(doubleConstant.getValue());
     }
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+        cpValue = valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING,
+                                                    stringConstant.javaLangStringClass, 
+                                                    false);
     }
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        visitRefCpInfo(classFile, methodrefCpInfo);
-    }
-
-    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
-    {
-        String type = methodrefCpInfo.getType(classFile);
-
-        parameterCount = ClassUtil.internalMethodParameterCount(type);
-        cpValue        = ValueFactory.create(ClassUtil.internalMethodReturnType(type));
-    }
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        String className = classCpInfo.getName(classFile);
-
-        referencedClassFile          = classCpInfo.referencedClassFile;
-        referencedTypeDimensionCount = ClassUtil.internalArrayTypeDimensionCount(className);
-
-        cpValue = ReferenceValueFactory.create(referencedClassFile,
-                                               referencedTypeDimensionCount,
-                                               false);
+        cpValue = handleClassConstantAsClassValue ?
+            valueFactory.createReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS,
+                                              classConstant.javaLangClassClass,
+                                              false) :
+            valueFactory.createReferenceValue(classConstant.getName(clazz),
+                                              classConstant.referencedClass,
+                                              false);
     }
 
 
@@ -977,55 +954,26 @@ implements   InstructionVisitor,
 
     /**
      * Returns the Value of the constant pool element at the given index.
-     * The element can be a constant, a field, a method,...
      */
-    private Value cpValue(ClassFile classFile, int cpIndex)
+    private Value cpValue(Clazz clazz,
+                          int   constantIndex)
     {
-        // Visit the constant pool entry to get its return value.
-        classFile.constantPoolEntryAccept(cpIndex, this);
-
-        return cpValue;
-    }
-
-
-    /**
-     * Returns the class file referenced by the class constant pool entry at the
-     * given index.
-     */
-    private ClassFile referencedClassFile(ClassFile classFile, int cpIndex)
-    {
-        // Visit the constant pool entry to get its referenced class file.
-        classFile.constantPoolEntryAccept(cpIndex, this);
-
-        return referencedClassFile;
+        return cpValue(clazz, constantIndex, false);
     }
 
 
     /**
-     * Returns the dimensionality of the class constant pool entry at the given
-     * index.
+     * Returns the Value of the constant pool element at the given index.
      */
-    private int referencedTypeDimensionCount(ClassFile classFile, int cpIndex)
+    private Value cpValue(Clazz   clazz,
+                          int     constantIndex,
+                          boolean handleClassConstantAsClassValue)
     {
-        // Visit the constant pool entry to get its referenced class file.
-        //classFile.constantPoolEntryAccept(this, cpIndex);
-
-        // We'll return the value that was just computed.
-        return referencedTypeDimensionCount;
-    }
+        this.handleClassConstantAsClassValue = handleClassConstantAsClassValue;
 
+        // Visit the constant pool entry to get its return value.
+        clazz.constantPoolEntryAccept(constantIndex, this);
 
-    /**
-     * Returns the number of parameters of the method reference at the given
-     * constant pool index. This method must be invoked right after the
-     * cpValue(ClassFile,int) method.
-     */
-    private int parameterCount(ClassFile classFile, int methodRefCpIndex)
-    {
-        // Visit the method ref constant pool entry to get its parameter count.
-        //classFile.constantPoolEntryAccept(this, methodRefCpIndex);
-
-        // We'll return the value that was just computed.
-        return parameterCount;
+        return cpValue;
     }
 }
diff --git a/src/proguard/optimize/evaluation/Stack.java b/src/proguard/evaluation/Stack.java
similarity index 91%
rename from src/proguard/optimize/evaluation/Stack.java
rename to src/proguard/evaluation/Stack.java
index b4da1f0..048b389 100644
--- a/src/proguard/optimize/evaluation/Stack.java
+++ b/src/proguard/evaluation/Stack.java
@@ -1,6 +1,6 @@
-/* $Id: Stack.java,v 1.8.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,9 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation;
+package proguard.evaluation;
 
-import proguard.optimize.evaluation.value.*;
+import proguard.evaluation.value.*;
 
 /**
  * This class represents an operand stack that contains <code>Value</code>
@@ -28,8 +28,11 @@ import proguard.optimize.evaluation.value.*;
  *
  * @author Eric Lafortune
  */
-class Stack
+public class Stack
 {
+    private static final TopValue TOP_VALUE = new TopValue();
+
+
     protected Value[] values;
     protected int     currentSize;
     protected int     actualMaxSize;
@@ -125,20 +128,22 @@ class Stack
         // Generalize the stack values.
         for (int index = 0; index < currentSize; index++)
         {
-            Value otherValue = other.values[index];
+            Value thisValue  = this.values[index];
 
-            if (otherValue != null)
+            if (thisValue != null)
             {
-                Value thisValue  = this.values[index];
+                Value newValue = null;
 
-                if (thisValue != null)
-                {
-                    otherValue = thisValue.generalize(otherValue);
+                Value otherValue = other.values[index];
 
-                    changed = changed || !otherValue.equals(thisValue);
+                if (otherValue != null)
+                {
+                    newValue = thisValue.generalize(otherValue);
                 }
 
-                values[index] = otherValue;
+                changed = changed || !thisValue.equals(newValue);
+
+                values[index] = newValue;
             }
         }
 
@@ -169,7 +174,7 @@ class Stack
 
     /**
      * Returns the number of elements currently on the stack, accounting for the
-     * double space required by Category 2 values..
+     * double space required by Category 2 values.
      */
     public int size()
     {
@@ -226,6 +231,20 @@ class Stack
 
 
     /**
+     * Removes the specified Value from the stack.
+     * @param index the index of the stack element, counting from the top
+     *              of the stack.
+     */
+    public void removeTop(int index)
+    {
+        System.arraycopy(values, currentSize - index,
+                         values, currentSize - index - 1,
+                         index);
+        currentSize--;
+    }
+
+
+    /**
      * Pushes the given Value onto the stack.
      */
     public void push(Value value)
@@ -233,7 +252,7 @@ class Stack
         // Account for the extra space required by Category 2 values.
         if (value.isCategory2())
         {
-            currentSize++;
+            values[currentSize++] = TOP_VALUE;
         }
 
         // Push the value.
@@ -480,7 +499,8 @@ class Stack
 
     public boolean equals(Object object)
     {
-        if (this.getClass() != object.getClass())
+        if (object == null ||
+            this.getClass() != object.getClass())
         {
             return false;
         }
diff --git a/src/proguard/optimize/evaluation/TracedStack.java b/src/proguard/evaluation/TracedStack.java
similarity index 76%
rename from src/proguard/optimize/evaluation/TracedStack.java
rename to src/proguard/evaluation/TracedStack.java
index 20fde4d..7bc5320 100644
--- a/src/proguard/optimize/evaluation/TracedStack.java
+++ b/src/proguard/evaluation/TracedStack.java
@@ -1,6 +1,6 @@
-/* $Id: TracedStack.java,v 1.10.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,13 +18,13 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation;
+package proguard.evaluation;
 
-import proguard.optimize.evaluation.value.*;
+import proguard.evaluation.value.Value;
 
 /**
  * This Stack saves additional information with stack elements, to keep track
- * of their origins and destionations.
+ * of their origins and destinations.
  * <p>
  * The stack stores a given producer Value along with each Value it stores.
  * It then generalizes a given collected Value with the producer Value
@@ -38,12 +38,12 @@ import proguard.optimize.evaluation.value.*;
  *
  * @author Eric Lafortune
  */
-class TracedStack extends Stack
+public class TracedStack extends Stack
 {
     private Value producerValue;
     private Value collectedProducerValue;
-    private Stack producerStack;
-    private Stack consumerStack;
+    private final Stack producerStack;
+    private final Stack consumerStack;
 
 
     public TracedStack(int maxSize)
@@ -208,6 +208,14 @@ class TracedStack extends Stack
         consumerStack.clear();
     }
 
+    public void removeTop(int index)
+    {
+        super.removeTop(index);
+
+        producerStack.removeTop(index);
+        consumerStack.removeTop(index);
+    }
+
     public void push(Value value)
     {
         super.push(value);
@@ -361,7 +369,8 @@ class TracedStack extends Stack
 
     public boolean equals(Object object)
     {
-        if (this.getClass() != object.getClass())
+        if (object == null ||
+            this.getClass() != object.getClass())
         {
             return false;
         }
@@ -389,8 +398,8 @@ class TracedStack extends Stack
         for (int index = 0; index < this.size(); index++)
         {
             Value value         = this.values[index];
-            Value producerValue = producerStack.values[index];
-            Value consumerValue = consumerStack.values[index];
+            Value producerValue = producerStack.getBottom(index);
+            Value consumerValue = consumerStack.getBottom(index);
             buffer = buffer.append('[')
                            .append(producerValue == null ? "empty" : producerValue.toString())
                            .append('>')
@@ -460,104 +469,4 @@ class TracedStack extends Stack
             collectedProducerValue = collectedProducerValue.generalize(producerValue);
         }
     }
-
-
-    /**
-     * This Value is a mutable wrapper for other Value instances.
-     * Its generalization method affects the Value itself as a side-effect.
-     */
-    private static class MutableValue extends Category1Value
-    {
-        Value containedValue;
-
-
-        public void generalizeContainedValue(Value containedValue)
-        {
-            MutableValue lastMutableValue  = lastMutableValue();
-            Value        lastContainedValue= lastMutableValue.containedValue;
-
-            lastMutableValue.containedValue =
-                lastContainedValue == null ? containedValue :
-                                             containedValue.generalize(lastContainedValue);
-        }
-
-
-        public void setContainedValue(Value value)
-        {
-            lastMutableValue().containedValue = value;
-        }
-
-
-        public Value getContainedValue()
-        {
-            return lastMutableValue().containedValue;
-        }
-
-
-        // Implementations for Value.
-
-        public Value generalize(Value other)
-        {
-            MutableValue otherMutableValue = (MutableValue)other;
-
-            MutableValue thisLastMutableValue  = this.lastMutableValue();
-            MutableValue otherLastMutableValue = otherMutableValue.lastMutableValue();
-
-            Value thisLastContainedValue  = thisLastMutableValue.containedValue;
-            Value otherLastContainedValue = otherLastMutableValue.containedValue;
-
-            if (thisLastMutableValue != otherLastMutableValue)
-            {
-                otherLastMutableValue.containedValue = thisLastMutableValue;
-            }
-
-            thisLastMutableValue.containedValue =
-                thisLastContainedValue  == null ? otherLastContainedValue :
-                otherLastContainedValue == null ? thisLastContainedValue  :
-                                                  thisLastContainedValue.generalize(otherLastContainedValue);
-            return thisLastMutableValue;
-        }
-
-
-        public int computationalType()
-        {
-            return 0;
-        }
-
-
-        // Implementations for Object.
-
-//        public boolean equals(Object other)
-//        {
-//            return this.getClass() == other.getClass() &&
-//                   this.lastMutableValue() == ((MutableValue)other).lastMutableValue();
-//        }
-//
-//
-//        public int hashCode()
-//        {
-//            return lastMutableValue().containedValue.hashCode();
-//        }
-
-
-        public String toString()
-        {
-            return containedValue == null ? "none" : containedValue.toString();
-        }
-
-
-        // Small utility methods.
-
-        public MutableValue lastMutableValue()
-        {
-            MutableValue mutableValue = this;
-
-            while (mutableValue.containedValue instanceof MutableValue)
-            {
-                mutableValue = (MutableValue)mutableValue.containedValue;
-            }
-
-            return mutableValue;
-        }
-    }
 }
diff --git a/src/proguard/evaluation/TracedVariables.java b/src/proguard/evaluation/TracedVariables.java
new file mode 100644
index 0000000..b921d2a
--- /dev/null
+++ b/src/proguard/evaluation/TracedVariables.java
@@ -0,0 +1,292 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation;
+
+import proguard.evaluation.value.Value;
+
+/**
+ * This Variables class saves additional information with variables, to keep track
+ * of their origins.
+ * <p>
+ * The Variables class stores a given producer Value along with each Value it
+ * stores. It then generalizes a given collected Value with the producer Value
+ * of each Value it loads. The producer Value and the initial collected Value
+ * can be set; the generalized collected Value can be retrieved.
+ * <p>
+ * In addition, an initialization index can be reset and retrieved, pointing
+ * to the most recent variable that has been initialized by a store operation.
+ *
+ * @author Eric Lafortune
+ */
+public class TracedVariables extends Variables
+{
+    public static final int NONE = -1;
+
+
+    private Value     producerValue;
+    private Value     collectedProducerValue;
+    private final Variables producerVariables;
+    private final Variables consumerVariables;
+    private int       initializationIndex;
+
+
+    public TracedVariables(int size)
+    {
+        super(size);
+
+        producerVariables = new Variables(size);
+        consumerVariables = new Variables(size);
+    }
+
+
+    public TracedVariables(TracedVariables tracedVariables)
+    {
+        super(tracedVariables);
+
+        producerVariables = new Variables(tracedVariables.producerVariables);
+        consumerVariables = new Variables(tracedVariables.consumerVariables);
+    }
+
+
+    /**
+     * Sets the Value that will be stored along with all store instructions.
+     */
+    public void setProducerValue(Value producerValue)
+    {
+        this.producerValue = producerValue;
+    }
+
+
+    /**
+     * Sets the initial Value with which all values stored along with load
+     * instructions will be generalized.
+     */
+    public void setCollectedProducerValue(Value collectedProducerValue)
+    {
+        this.collectedProducerValue = collectedProducerValue;
+    }
+
+    public Value getCollectedProducerValue()
+    {
+        return collectedProducerValue;
+    }
+
+
+    /**
+     * Resets the initialization index.
+     */
+    public void resetInitialization()
+    {
+        initializationIndex = NONE;
+    }
+
+    public int getInitializationIndex()
+    {
+        return initializationIndex;
+    }
+
+
+    /**
+     * Gets the producer Value for the specified variable, without disturbing it.
+     * @param index the variable index.
+     * @return the producer value of the given variable.
+     */
+    public Value getProducerValue(int index)
+    {
+        return producerVariables.getValue(index);
+    }
+
+
+    /**
+     * Sets the given producer Value for the specified variable, without
+     * disturbing it.
+     * @param index the variable index.
+     * @param value the producer value to set.
+     */
+    public void setProducerValue(int index, Value value)
+    {
+        producerVariables.store(index, value);
+    }
+
+
+    /**
+     * Gets the consumer Value for the specified variable, without disturbing it.
+     * @param index the variable index.
+     * @return the producer value of the given variable.
+     */
+    public Value getConsumerValue(int index)
+    {
+        return ((MutableValue)consumerVariables.getValue(index)).getContainedValue();
+    }
+
+
+    /**
+     * Sets the specified consumer Value for the given variable, without
+     * disturbing it.
+     * @param index the variable index.
+     * @param value the consumer value to set.
+     */
+    public void setConsumerValue(int index, Value value)
+    {
+        ((MutableValue)consumerVariables.getValue(index)).setContainedValue(value);
+        consumerVariables.store(index, new MutableValue());
+    }
+
+
+    // Implementations for Variables.
+
+    public void reset(int size)
+    {
+        super.reset(size);
+
+        producerVariables.reset(size);
+        consumerVariables.reset(size);
+    }
+
+    public void initialize(TracedVariables other)
+    {
+        super.initialize(other);
+
+        producerVariables.initialize(other.producerVariables);
+        consumerVariables.initialize(other.consumerVariables);
+    }
+
+    public boolean generalize(TracedVariables other,
+                              boolean         clearConflictingOtherVariables)
+    {
+        boolean variablesChanged = super.generalize(other, clearConflictingOtherVariables);
+        boolean producersChanged = producerVariables.generalize(other.producerVariables, clearConflictingOtherVariables);
+        /* consumerVariables.generalize(other.consumerVariables)*/
+
+        // Clear any traces if a variable has become null.
+        if (variablesChanged)
+        {
+            for (int index = 0; index < size; index++)
+            {
+                if (values[index] == null)
+                {
+                    producerVariables.values[index] = null;
+                    consumerVariables.values[index] = null;
+
+                    if (clearConflictingOtherVariables)
+                    {
+                        other.producerVariables.values[index] = null;
+                        other.consumerVariables.values[index] = null;
+                    }
+                }
+            }
+        }
+
+        return variablesChanged || producersChanged;
+    }
+
+
+    public void store(int index, Value value)
+    {
+        // Is this store operation an initialization of the variable?
+        Value previousValue = super.load(index);
+        if (previousValue == null ||
+            previousValue.computationalType() != value.computationalType())
+        {
+            initializationIndex = index;
+        }
+
+        // Store the value itself in the variable.
+        super.store(index, value);
+
+        // Store the producer value in its producer variable.
+        producerVariables.store(index, producerValue);
+
+        // Reserve a space for the consumer value.
+        MutableValue mutableValue = new MutableValue();
+        consumerVariables.store(index, mutableValue);
+
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            producerVariables.store(index+1, producerValue);
+            consumerVariables.store(index+1, mutableValue);
+        }
+    }
+
+    public Value load(int index)
+    {
+        // Load and accumulate the producer value of the variable.
+        if (collectedProducerValue != null)
+        {
+            collectedProducerValue = collectedProducerValue.generalize(producerVariables.load(index));
+        }
+
+        // Generalize the consumer value of the variable.
+        ((MutableValue)consumerVariables.load(index)).generalizeContainedValue(producerValue);
+
+        // Return the value itself.
+        return super.load(index);
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        TracedVariables other = (TracedVariables)object;
+
+        return super.equals(object) &&
+               this.producerVariables.equals(other.producerVariables) /*&&
+               this.consumerVariables.equals(other.consumerVariables)*/;
+    }
+
+
+    public int hashCode()
+    {
+        return super.hashCode() ^
+               producerVariables.hashCode() /*^
+               consumerVariables.hashCode()*/;
+    }
+
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        for (int index = 0; index < this.size(); index++)
+        {
+            Value value         = this.values[index];
+            Value producerValue = producerVariables.getValue(index);
+            Value consumerValue = consumerVariables.getValue(index);
+            buffer = buffer.append('[')
+                           .append(producerValue == null ? "empty" : producerValue.toString())
+                           .append('>')
+                           .append(value         == null ? "empty" : value.toString())
+                           .append('>')
+                           .append(consumerValue == null ? "empty" : consumerValue.toString())
+                           .append(']');
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/src/proguard/optimize/evaluation/Variables.java b/src/proguard/evaluation/Variables.java
similarity index 76%
rename from src/proguard/optimize/evaluation/Variables.java
rename to src/proguard/evaluation/Variables.java
index 062cf40..2a78260 100644
--- a/src/proguard/optimize/evaluation/Variables.java
+++ b/src/proguard/evaluation/Variables.java
@@ -1,6 +1,6 @@
-/* $Id: Variables.java,v 1.7.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,9 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation;
+package proguard.evaluation;
 
-import proguard.optimize.evaluation.value.*;
+import proguard.evaluation.value.*;
 
 /**
  * This class represents a local variable frame that contains <code>Value</code>
@@ -29,8 +29,11 @@ import proguard.optimize.evaluation.value.*;
  *
  * @author Eric Lafortune
  */
-class Variables
+public class Variables
 {
+    private static final TopValue TOP_VALUE = new TopValue();
+
+
     protected Value[] values;
     protected int     size;
 
@@ -101,11 +104,14 @@ class Variables
 
     /**
      * Generalizes the values of this Variables object with the values of the
-     * given Variables object. In case of conflicts, the other Variables
-     * object gets precedence.
+     * given Variables object.
+     * @param clearConflictingOtherVariables specifies whether the other
+     *                                       variables should be cleared too,
+     *                                       in case of conflicts.
      * @return whether the generalization has made any difference.
      */
-    public boolean generalize(Variables other)
+    public boolean generalize(Variables other,
+                              boolean   clearConflictingOtherVariables)
     {
         if (this.size != other.size)
         {
@@ -116,26 +122,34 @@ class Variables
 
         for (int index = 0; index < size; index++)
         {
+            Value thisValue  = this.values[index];
             Value otherValue = other.values[index];
 
-            if (otherValue != null)
+            // Occasionally, two values of different types might be present
+            // in the same variable in a variable frame (corresponding to
+            // two local variables that share the same index), at some point
+            // outside of their scopes. Don't generalize the variable then,
+            // but let it clear instead.
+            if (thisValue  != null &&
+                otherValue != null &&
+                thisValue.computationalType() == otherValue.computationalType())
             {
-                Value thisValue  = this.values[index];
-
-                // Occasionally, two values of different types might be
-                // present in the same variable in a variable frame
-                // (corresponding to two local variables that share the
-                // same index), at some point outside of their scopes.
-                // The new value gets precedence.
-                if (thisValue != null &&
-                    thisValue.computationalType() == otherValue.computationalType())
-                {
-                    otherValue = thisValue.generalize(otherValue);
+                Value newValue = thisValue.generalize(otherValue);
 
-                    changed = changed || !otherValue.equals(thisValue);
-                }
+                changed = changed || !thisValue.equals(newValue);
 
-                values[index] = otherValue;
+                this.values[index] = newValue;
+            }
+            else
+            {
+                changed = changed || thisValue != null;
+
+                this.values[index] = null;
+
+                if (clearConflictingOtherVariables)
+                {
+                    other.values[index] = null;
+                }
             }
         }
 
@@ -153,6 +167,21 @@ class Variables
 
 
     /**
+     * Gets the Value of the variable with the given index, without disturbing it.
+     */
+    public Value getValue(int index)
+    {
+        if (index < 0 ||
+            index >= size)
+        {
+            throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
+        }
+
+        return values[index];
+    }
+
+
+    /**
      * Stores the given Value at the given variable index.
      */
     public void store(int index, Value value)
@@ -165,6 +194,12 @@ class Variables
 
         // Store the value.
         values[index] = value;
+
+        // Account for the extra space required by Category 2 values.
+        if (value.isCategory2())
+        {
+            values[index + 1] = TOP_VALUE;
+        }
     }
 
 
@@ -243,7 +278,8 @@ class Variables
 
     public boolean equals(Object object)
     {
-        if (this.getClass() != object.getClass())
+        if (object == null ||
+            this.getClass() != object.getClass())
         {
             return false;
         }
diff --git a/src/proguard/optimize/evaluation/value/Category1Value.java b/src/proguard/evaluation/value/Category1Value.java
similarity index 85%
rename from src/proguard/optimize/evaluation/value/Category1Value.java
rename to src/proguard/evaluation/value/Category1Value.java
index 78a152f..74aa421 100644
--- a/src/proguard/optimize/evaluation/value/Category1Value.java
+++ b/src/proguard/evaluation/value/Category1Value.java
@@ -1,6 +1,6 @@
-/* $Id: Category1Value.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This abstract class represents a partially evaluated Category 1 value.
diff --git a/src/proguard/optimize/evaluation/value/Category2Value.java b/src/proguard/evaluation/value/Category2Value.java
similarity index 85%
rename from src/proguard/optimize/evaluation/value/Category2Value.java
rename to src/proguard/evaluation/value/Category2Value.java
index c1c4678..fba6c29 100644
--- a/src/proguard/optimize/evaluation/value/Category2Value.java
+++ b/src/proguard/evaluation/value/Category2Value.java
@@ -1,6 +1,6 @@
-/* $Id: Category2Value.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This abstract class represents a partially evaluated Category 2 value.
diff --git a/src/proguard/optimize/evaluation/value/DoubleValue.java b/src/proguard/evaluation/value/DoubleValue.java
similarity index 84%
rename from src/proguard/optimize/evaluation/value/DoubleValue.java
rename to src/proguard/evaluation/value/DoubleValue.java
index f05bb61..b55f492 100644
--- a/src/proguard/optimize/evaluation/value/DoubleValue.java
+++ b/src/proguard/evaluation/value/DoubleValue.java
@@ -1,6 +1,6 @@
-/* $Id: DoubleValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
+
+import proguard.classfile.ClassConstants;
 
 /**
  * This class represents a partially evaluated double value.
@@ -116,9 +118,9 @@ public class DoubleValue extends Category2Value
      * Returns an IntegerValue with value -1, 0, or 1, if this DoubleValue is
      * less than, equal to, or greater than the given DoubleValue, respectively.
      */
-    public IntegerValue compare(DoubleValue other)
+    public IntegerValue compare(DoubleValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -128,9 +130,9 @@ public class DoubleValue extends Category2Value
      * Returns an IntegerValue with value 1, 0, or -1, if this DoubleValue is
      * less than, equal to, or greater than the given DoubleValue, respectively.
      */
-    public final IntegerValue compareReverse(DoubleValue other)
+    public final IntegerValue compareReverse(DoubleValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -147,25 +149,25 @@ public class DoubleValue extends Category2Value
     /**
      * Converts this DoubleValue to an IntegerValue.
      */
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
     /**
      * Converts this DoubleValue to a LongValue.
      */
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create();
+        return valueFactory.createLongValue();
     }
 
     /**
      * Converts this DoubleValue to a FloatValue.
      */
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create();
+        return valueFactory.createFloatValue();
     }
 
 
@@ -249,13 +251,13 @@ public class DoubleValue extends Category2Value
     }
 
     /**
-     * Returns an IntegerValue with value -1, 0, or  1, if this DoubleValue is
+     * Returns an IntegerValue with value -1, 0, or 1, if this DoubleValue is
      * less than, equal to, or greater than the given SpecificDoubleValue,
      * respectively.
      */
-    public IntegerValue compare(SpecificDoubleValue other)
+    public IntegerValue compare(SpecificDoubleValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -266,9 +268,9 @@ public class DoubleValue extends Category2Value
      * less than, equal to, or greater than the given SpecificDoubleValue,
      * respectively.
      */
-    public final IntegerValue compareReverse(SpecificDoubleValue other)
+    public final IntegerValue compareReverse(SpecificDoubleValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -289,6 +291,11 @@ public class DoubleValue extends Category2Value
         return TYPE_DOUBLE;
     }
 
+    public final String internalType()
+    {
+        return String.valueOf(ClassConstants.INTERNAL_TYPE_DOUBLE);
+    }
+
 
     // Implementations for Object.
 
diff --git a/src/proguard/optimize/evaluation/value/FloatValue.java b/src/proguard/evaluation/value/FloatValue.java
similarity index 83%
rename from src/proguard/optimize/evaluation/value/FloatValue.java
rename to src/proguard/evaluation/value/FloatValue.java
index 922c17a..5a9e87b 100644
--- a/src/proguard/optimize/evaluation/value/FloatValue.java
+++ b/src/proguard/evaluation/value/FloatValue.java
@@ -1,6 +1,6 @@
-/* $Id: FloatValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
+
+import proguard.classfile.ClassConstants;
 
 /**
  * This class represents a partially evaluated float value.
@@ -116,9 +118,9 @@ public class FloatValue extends Category1Value
      * Returns an IntegerValue with value -1, 0, or 1, if this FloatValue is
      * less than, equal to, or greater than the given FloatValue, respectively.
      */
-    public IntegerValue compare(FloatValue other)
+    public IntegerValue compare(FloatValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -128,9 +130,9 @@ public class FloatValue extends Category1Value
      * Returns an IntegerValue with value 1, 0, or -1, if this FloatValue is
      * less than, equal to, or greater than the given FloatValue, respectively.
      */
-    public final IntegerValue compareReverse(FloatValue other)
+    public final IntegerValue compareReverse(FloatValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -147,25 +149,25 @@ public class FloatValue extends Category1Value
     /**
      * Converts this FloatValue to an IntegerValue.
      */
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
     /**
      * Converts this FloatValue to a LongValue.
      */
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create();
+        return valueFactory.createLongValue();
     }
 
     /**
      * Converts this FloatValue to a DoubleValue.
      */
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create();
+        return valueFactory.createDoubleValue();
     }
 
 
@@ -249,13 +251,13 @@ public class FloatValue extends Category1Value
     }
 
     /**
-     * Returns an IntegerValue with value -1, 0, or  1, if this FloatValue is
+     * Returns an IntegerValue with value -1, 0, or 1, if this FloatValue is
      * less than, equal to, or greater than the given SpecificFloatValue,
      * respectively.
      */
-    public IntegerValue compare(SpecificFloatValue other)
+    public IntegerValue compare(SpecificFloatValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -266,9 +268,9 @@ public class FloatValue extends Category1Value
      * less than, equal to, or greater than the given SpecificFloatValue,
      * respectively.
      */
-    public final IntegerValue compareReverse(SpecificFloatValue other)
+    public final IntegerValue compareReverse(SpecificFloatValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -289,6 +291,11 @@ public class FloatValue extends Category1Value
         return TYPE_FLOAT;
     }
 
+    public final String internalType()
+    {
+        return String.valueOf(ClassConstants.INTERNAL_TYPE_FLOAT);
+    }
+
 
     // Implementations for Object.
 
diff --git a/src/proguard/optimize/evaluation/value/InstructionOffsetValue.java b/src/proguard/evaluation/value/InstructionOffsetValue.java
similarity index 77%
rename from src/proguard/optimize/evaluation/value/InstructionOffsetValue.java
rename to src/proguard/evaluation/value/InstructionOffsetValue.java
index b759f32..3dcbfc2 100644
--- a/src/proguard/optimize/evaluation/value/InstructionOffsetValue.java
+++ b/src/proguard/evaluation/value/InstructionOffsetValue.java
@@ -1,6 +1,6 @@
-/* $Id: InstructionOffsetValue.java,v 1.7.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
+
+import proguard.classfile.ClassConstants;
 
 /**
  * This class represents a partially evaluated instruction offset. It can
@@ -28,10 +30,13 @@ package proguard.optimize.evaluation.value;
  */
 public class InstructionOffsetValue extends Category1Value
 {
+    public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue();
+
+
     private int[] values;
 
 
-    public InstructionOffsetValue()
+    private InstructionOffsetValue()
     {
     }
 
@@ -82,6 +87,56 @@ public class InstructionOffsetValue extends Category1Value
 
 
     /**
+     * Returns the minimum value from this list of instruction offsets.
+     * Returns <code>Integer.MAX_VALUE</code> if the list is empty.
+     */
+    public int minimumValue()
+    {
+        int minimumValue = Integer.MAX_VALUE;
+
+        if (values != null)
+        {
+            for (int index = 0; index < values.length; index++)
+            {
+                int value = values[index];
+
+                if (minimumValue > value)
+                {
+                    minimumValue = value;
+                }
+            }
+        }
+
+        return minimumValue;
+    }
+
+
+    /**
+     * Returns the maximum value from this list of instruction offsets.
+     * Returns <code>Integer.MIN_VALUE</code> if the list is empty.
+     */
+    public int maximumValue()
+    {
+        int maximumValue = Integer.MIN_VALUE;
+
+        if (values != null)
+        {
+            for (int index = 0; index < values.length; index++)
+            {
+                int value = values[index];
+
+                if (maximumValue < value)
+                {
+                    maximumValue = value;
+                }
+            }
+        }
+
+        return maximumValue;
+    }
+
+
+    /**
      * Returns the generalization of this InstructionOffsetValue and the given
      * other InstructionOffsetValue. The values of the other InstructionOffsetValue
      * are guaranteed to remain at the end of the list, in the same order.
@@ -143,7 +198,7 @@ public class InstructionOffsetValue extends Category1Value
             newValues[newIndex++] = other.values[index];
         }
 
-        return InstructionOffsetValueFactory.create(newValues);
+        return new InstructionOffsetValue(newValues);
     }
 
 
@@ -164,6 +219,11 @@ public class InstructionOffsetValue extends Category1Value
         return TYPE_INSTRUCTION_OFFSET;
     }
 
+    public final String internalType()
+    {
+        return String.valueOf(ClassConstants.INTERNAL_TYPE_INT);
+    }
+
 
     // Implementations for Object.
 
diff --git a/src/proguard/optimize/evaluation/value/IntegerValue.java b/src/proguard/evaluation/value/IntegerValue.java
similarity index 92%
rename from src/proguard/optimize/evaluation/value/IntegerValue.java
rename to src/proguard/evaluation/value/IntegerValue.java
index fa57725..d434abe 100644
--- a/src/proguard/optimize/evaluation/value/IntegerValue.java
+++ b/src/proguard/evaluation/value/IntegerValue.java
@@ -1,6 +1,6 @@
-/* $Id: IntegerValue.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
+
+import proguard.classfile.ClassConstants;
 
 /**
  * This class represents a partially evaluated integer value.
@@ -174,7 +176,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue shiftLeftOf(LongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -182,7 +184,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue shiftRightOf(LongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -190,7 +192,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue unsignedShiftRightOf(LongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -292,7 +294,7 @@ public class IntegerValue extends Category1Value
     /**
      * Converts this IntegerValue to a byte IntegerValue.
      */
-    public IntegerValue convertToByte()
+    public IntegerValue convertToByte(ValueFactory valueFactory)
     {
         return this;
     }
@@ -300,7 +302,7 @@ public class IntegerValue extends Category1Value
     /**
      * Converts this IntegerValue to a character IntegerValue.
      */
-    public IntegerValue convertToCharacter()
+    public IntegerValue convertToCharacter(ValueFactory valueFactory)
     {
         return this;
     }
@@ -308,7 +310,7 @@ public class IntegerValue extends Category1Value
     /**
      * Converts this IntegerValue to a short IntegerValue.
      */
-    public IntegerValue convertToShort()
+    public IntegerValue convertToShort(ValueFactory valueFactory)
     {
         return this;
     }
@@ -316,25 +318,25 @@ public class IntegerValue extends Category1Value
     /**
      * Converts this IntegerValue to a LongValue.
      */
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create();
+        return valueFactory.createLongValue();
     }
 
     /**
      * Converts this IntegerValue to a FloatValue.
      */
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create();
+        return valueFactory.createFloatValue();
     }
 
     /**
      * Converts this IntegerValue to a DoubleValue.
      */
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create();
+        return valueFactory.createDoubleValue();
     }
 
 
@@ -473,7 +475,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue shiftLeftOf(SpecificLongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -481,7 +483,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue shiftRightOf(SpecificLongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -490,7 +492,7 @@ public class IntegerValue extends Category1Value
      */
     public LongValue unsignedShiftRightOf(SpecificLongValue other)
     {
-        return LongValueFactory.create();
+        return ValueFactory.LONG_VALUE;
     }
 
     /**
@@ -599,6 +601,11 @@ public class IntegerValue extends Category1Value
         return TYPE_INTEGER;
     }
 
+    public final String internalType()
+    {
+        return String.valueOf(ClassConstants.INTERNAL_TYPE_INT);
+    }
+
 
     // Implementations for Object.
 
diff --git a/src/proguard/optimize/evaluation/value/LongValue.java b/src/proguard/evaluation/value/LongValue.java
similarity index 82%
rename from src/proguard/optimize/evaluation/value/LongValue.java
rename to src/proguard/evaluation/value/LongValue.java
index a745aa4..86c442f 100644
--- a/src/proguard/optimize/evaluation/value/LongValue.java
+++ b/src/proguard/evaluation/value/LongValue.java
@@ -1,6 +1,6 @@
-/* $Id: LongValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,9 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
+
+import proguard.classfile.ClassConstants;
 
 /**
  * This class represents a partially evaluated long value.
@@ -168,9 +170,9 @@ public class LongValue extends Category2Value
      * Returns an IntegerValue with value -1, 0, or 1, if this LongValue is
      * less than, equal to, or greater than the given LongValue, respectively.
      */
-    public IntegerValue compare(LongValue other)
+    public IntegerValue compare(LongValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -180,9 +182,9 @@ public class LongValue extends Category2Value
      * Returns an IntegerValue with value 1, 0, or -1, if this LongValue is
      * less than, equal to, or greater than the given LongValue, respectively.
      */
-    public final IntegerValue compareReverse(LongValue other)
+    public final IntegerValue compareReverse(LongValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -199,25 +201,25 @@ public class LongValue extends Category2Value
     /**
      * Converts this LongValue to an IntegerValue.
      */
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
     /**
      * Converts this LongValue to a FloatValue.
      */
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create();
+        return valueFactory.createFloatValue();
     }
 
     /**
      * Converts this LongValue to a DoubleValue.
      */
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create();
+        return valueFactory.createDoubleValue();
     }
 
 
@@ -300,6 +302,31 @@ public class LongValue extends Category2Value
     }
 
     /**
+     * Returns this LongValue, shifted left by the given SpecificIntegerValue.
+     */
+    public LongValue shiftLeft(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this LongValue, shifted right by the given SpecificIntegerValue.
+     */
+    public LongValue shiftRight(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
+     * Returns this unsigned LongValue, shifted left by the given
+     * SpecificIntegerValue.
+     */
+    public LongValue unsignedShiftRight(SpecificIntegerValue other)
+    {
+        return this;
+    }
+
+    /**
      * Returns the logical <i>and</i> of this LongValue and the given
      * SpecificLongValue.
      */
@@ -331,9 +358,9 @@ public class LongValue extends Category2Value
      * less than, equal to, or greater than the given SpecificLongValue,
      * respectively.
      */
-    public IntegerValue compare(SpecificLongValue other)
+    public IntegerValue compare(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create();
+        return valueFactory.createIntegerValue();
     }
 
 
@@ -344,9 +371,9 @@ public class LongValue extends Category2Value
      * less than, equal to, or greater than the given SpecificLongValue,
      * respectively.
      */
-    public final IntegerValue compareReverse(SpecificLongValue other)
+    public final IntegerValue compareReverse(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return compare(other).negate();
+        return compare(other, valueFactory).negate();
     }
 
 
@@ -367,6 +394,11 @@ public class LongValue extends Category2Value
         return TYPE_LONG;
     }
 
+    public final String internalType()
+    {
+        return String.valueOf(ClassConstants.INTERNAL_TYPE_LONG);
+    }
+
 
     // Implementations for Object.
 
diff --git a/src/proguard/evaluation/value/ReferenceValue.java b/src/proguard/evaluation/value/ReferenceValue.java
new file mode 100644
index 0000000..64829c1
--- /dev/null
+++ b/src/proguard/evaluation/value/ReferenceValue.java
@@ -0,0 +1,515 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+import proguard.classfile.visitor.ClassCollector;
+
+import java.util.*;
+
+/**
+ * This class represents a partially evaluated reference value. It has a type
+ * and a flag that indicates whether the value could be <code>null</code>. If
+ * the type is <code>null</code>, the value is <code>null</code>.
+ *
+ * @author Eric Lafortune
+ */
+public class ReferenceValue extends Category1Value
+{
+    private static final boolean DEBUG = false;
+
+
+    private final String  type;
+    private final Clazz   referencedClass;
+    private final boolean mayBeNull;
+
+
+    /**
+     * Creates a new ReferenceValue.
+     */
+    public ReferenceValue(String  type,
+                          Clazz   referencedClass,
+                          boolean mayBeNull)
+    {
+        this.type            = type;
+        this.referencedClass = referencedClass;
+        this.mayBeNull       = mayBeNull;
+    }
+
+
+    /**
+     * Returns the type.
+     */
+    public String getType()
+    {
+        return type;
+    }
+
+
+    /**
+     * Returns the class that is referenced by the type.
+     */
+    public Clazz getReferencedClass()
+    {
+        return referencedClass;
+    }
+
+
+    // Basic unary methods.
+
+    /**
+     * Returns whether the type is <code>null</code>.
+     */
+    public int isNull()
+    {
+        return type == null ? ALWAYS :
+               mayBeNull    ? MAYBE  :
+                              NEVER;
+    }
+
+
+    /**
+     * Returns whether the type is an instance of the given type.
+     */
+    public int instanceOf(String otherType, Clazz otherReferencedClass)
+    {
+        String thisType = this.type;
+
+        // If this type is null, it is never an instance of any class.
+        if (thisType == null)
+        {
+            return NEVER;
+        }
+
+        // Start taking into account the type dimensions.
+        int thisDimensionCount   = ClassUtil.internalArrayTypeDimensionCount(thisType);
+        int otherDimensionCount  = ClassUtil.internalArrayTypeDimensionCount(otherType);
+        int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
+
+        // Strip any common array prefixes.
+        thisType  = thisType.substring(commonDimensionCount);
+        otherType = otherType.substring(commonDimensionCount);
+
+        // If either stripped type is a primitive type, we can tell right away.
+        if (commonDimensionCount > 0 &&
+            (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) ||
+             ClassUtil.isInternalPrimitiveType(otherType.charAt(0))))
+        {
+            return !thisType.equals(otherType) ? NEVER :
+                   mayBeNull                   ? MAYBE :
+                                                 ALWAYS;
+        }
+
+        // Strip the class type prefix and suffix of this type, if any.
+        if (thisDimensionCount == commonDimensionCount)
+        {
+            thisType = ClassUtil.internalClassNameFromClassType(thisType);
+        }
+
+        // Strip the class type prefix and suffix of the other type, if any.
+        if (otherDimensionCount == commonDimensionCount)
+        {
+            otherType = ClassUtil.internalClassNameFromClassType(otherType);
+        }
+
+        // If this type is an array type, and the other type is not
+        // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
+        // this type can never be an instance.
+        if (thisDimensionCount > otherDimensionCount &&
+            !ClassUtil.isInternalArrayInterfaceName(otherType))
+        {
+            return NEVER;
+        }
+
+        // If the other type is an array type, and this type is not
+        // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
+        // this type can never be an instance.
+        if (thisDimensionCount < otherDimensionCount &&
+            !ClassUtil.isInternalArrayInterfaceName(thisType))
+        {
+            return NEVER;
+        }
+
+        // If this type may be null, it might not be an instance of any class.
+        if (mayBeNull)
+        {
+            return MAYBE;
+        }
+
+        // If this type is equal to the other type, or if the other type is
+        // java.lang.Object, this type is always an instance.
+        if (thisType.equals(otherType) ||
+            ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(otherType))
+        {
+            return ALWAYS;
+        }
+
+        // If this type is an array type, it's ok.
+        if (thisDimensionCount > otherDimensionCount)
+        {
+            return ALWAYS;
+        }
+
+        // If the other type is an array type, it might be ok.
+        if (thisDimensionCount < otherDimensionCount)
+        {
+            return MAYBE;
+        }
+
+        // If the value extends the type, we're sure.
+        return referencedClass      != null &&
+               otherReferencedClass != null &&
+               referencedClass.extendsOrImplements(otherReferencedClass) ?
+                   ALWAYS :
+                   MAYBE;
+    }
+
+
+    /**
+     * Returns the length of the array, assuming this type is an array.
+     */
+    public IntegerValue arrayLength(ValueFactory valueFactory)
+    {
+        return valueFactory.createIntegerValue();
+    }
+
+
+    /**
+     * Returns the value of the array at the given index, assuming this type
+     * is an array.
+     */
+    public Value arrayLoad(IntegerValue integerValue, ValueFactory valueFactory)
+    {
+        return
+            type == null                         ? ValueFactory.REFERENCE_VALUE_NULL                        :
+            !ClassUtil.isInternalArrayType(type) ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
+                                                   valueFactory.createValue(type.substring(1),
+                                                                            referencedClass,
+                                                                            true);
+    }
+
+
+    // Basic binary methods.
+
+    /**
+     * Returns the generalization of this ReferenceValue and the given other
+     * ReferenceValue.
+     */
+    public ReferenceValue generalize(ReferenceValue other)
+    {
+        String thisType  = this.type;
+        String otherType = other.type;
+
+        // If both types are nul, the generalization is null too.
+        if (thisType == null && otherType == null)
+        {
+            return ValueFactory.REFERENCE_VALUE_NULL;
+        }
+
+        // If this type is null, the generalization is the other type, maybe null.
+        if (thisType == null)
+        {
+            return other.generalizeMayBeNull(true);
+        }
+
+        // If the other type is null, the generalization is this type, maybe null.
+        if (otherType == null)
+        {
+            return this.generalizeMayBeNull(true);
+        }
+
+        boolean mayBeNull = this.mayBeNull || other.mayBeNull;
+
+        // If the two types are equal, the generalization remains the same, maybe null.
+        if (thisType.equals(otherType))
+        {
+            return this.generalizeMayBeNull(mayBeNull);
+        }
+
+        // Start taking into account the type dimensions.
+        int thisDimensionCount   = ClassUtil.internalArrayTypeDimensionCount(thisType);
+        int otherDimensionCount  = ClassUtil.internalArrayTypeDimensionCount(otherType);
+        int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
+
+        if (thisDimensionCount == otherDimensionCount)
+        {
+            // See if we can take into account the referenced classes.
+            Clazz thisReferencedClass  = this.referencedClass;
+            Clazz otherReferencedClass = other.referencedClass;
+
+            if (thisReferencedClass  != null &&
+                otherReferencedClass != null)
+            {
+                if (thisReferencedClass.extendsOrImplements(otherReferencedClass))
+                {
+                    return other.generalizeMayBeNull(mayBeNull);
+                }
+
+                if (otherReferencedClass.extendsOrImplements(thisReferencedClass))
+                {
+                    return this.generalizeMayBeNull(mayBeNull);
+                }
+
+                // Collect the superclasses and interfaces of this class.
+                Set thisSuperClasses = new HashSet();
+                thisReferencedClass.hierarchyAccept(false, true, true, false,
+                                                    new ClassCollector(thisSuperClasses));
+
+                // Collect the superclasses and interfaces of the other class.
+                Set otherSuperClasses = new HashSet();
+                otherReferencedClass.hierarchyAccept(false, true, true, false,
+                                                     new ClassCollector(otherSuperClasses));
+
+                if (DEBUG)
+                {
+                    System.out.println("ReferenceValue.generalize this ["+thisReferencedClass.getName()+"] with other ["+otherReferencedClass.getName()+"]");
+                    System.out.println("  This super classes:  "+thisSuperClasses);
+                    System.out.println("  Other super classes: "+otherSuperClasses);
+                }
+
+                // Find the common superclasses.
+                thisSuperClasses.retainAll(otherSuperClasses);
+
+                if (DEBUG)
+                {
+                    System.out.println("  Common super classes: "+thisSuperClasses);
+                }
+
+                // Find a class that is a subclass of all common superclasses,
+                // or that at least has the maximum number of common superclasses.
+                Clazz commonClazz = null;
+
+                int maximumSuperClassCount = -1;
+
+                // Go over all common superclasses to find it. In case of
+                // multiple subclasses, keep the lowest one alphabetically,
+                // in order to ensure that the choice is deterministic.
+                Iterator commonSuperClasses = thisSuperClasses.iterator();
+                while (commonSuperClasses.hasNext())
+                {
+                    Clazz commonSuperClass = (Clazz)commonSuperClasses.next();
+
+                    int superClassCount = superClassCount(commonSuperClass, thisSuperClasses);
+                    if (maximumSuperClassCount < superClassCount ||
+                        (maximumSuperClassCount == superClassCount &&
+                         commonClazz != null                       &&
+                         commonClazz.getName().compareTo(commonSuperClass.getName()) > 0))
+                    {
+                        commonClazz            = commonSuperClass;
+                        maximumSuperClassCount = superClassCount;
+                    }
+                }
+
+                if (commonClazz == null)
+                {
+                    throw new IllegalArgumentException("Can't find common super class of ["+thisType+"] and ["+otherType+"]");
+                }
+
+                if (DEBUG)
+                {
+                    System.out.println("  Best common class: ["+commonClazz.getName()+"]");
+                }
+
+                // TODO: Handle more difficult cases, with multiple global subclasses.
+
+                return new ReferenceValue(commonDimensionCount == 0 ?
+                                              commonClazz.getName() :
+                                              ClassUtil.internalArrayTypeFromClassName(commonClazz.getName(),
+                                                                                       commonDimensionCount),
+                                          commonClazz,
+                                          mayBeNull);
+            }
+        }
+        else if (thisDimensionCount > otherDimensionCount)
+        {
+            // See if the other type is an interface type of arrays.
+            if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(otherType)))
+            {
+                return other.generalizeMayBeNull(mayBeNull);
+            }
+        }
+        else if (thisDimensionCount < otherDimensionCount)
+        {
+            // See if this type is an interface type of arrays.
+            if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(thisType)))
+            {
+                return this.generalizeMayBeNull(mayBeNull);
+            }
+        }
+
+        // Reduce the common dimension count if either type is an array of
+        // primitives type of this dimension.
+        if (commonDimensionCount > 0 &&
+            (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) ||
+             ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount)))
+        {
+            commonDimensionCount--;
+        }
+
+        // Fall back on a basic Object or array of Objects type.
+        return commonDimensionCount == 0 ?
+            mayBeNull ?
+                ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
+                ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL   :
+            new ReferenceValue(ClassUtil.internalArrayTypeFromClassName(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT,
+                                                                        commonDimensionCount),
+                               null,
+                               mayBeNull);
+    }
+
+
+    /**
+     * Returns if the number of superclasses of the given class in the given
+     * set of classes.
+     */
+    private int superClassCount(Clazz subClass, Set classes)
+    {
+        int count = 0;
+
+        Iterator iterator = classes.iterator();
+
+        while (iterator.hasNext())
+        {
+            Clazz clazz = (Clazz)iterator.next();
+            if (subClass.extendsOrImplements(clazz))
+            {
+                count++;
+            }
+        }
+
+        //System.out.println("ReferenceValue.superClassCount: ["+subClass.getName()+"]: "+count);
+
+        return count;
+    }
+
+
+    /**
+     * Returns whether this ReferenceValue is equal to the given other
+     * ReferenceValue.
+     * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public int equal(ReferenceValue other)
+    {
+        return this.type  == null && other.type == null ? ALWAYS : MAYBE;
+    }
+
+
+    // Derived unary methods.
+
+    /**
+     * Returns whether this ReferenceValue is not <code>null</code>.
+     * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int isNotNull()
+    {
+        return -isNull();
+    }
+
+
+    /**
+     * Returns the generalization of this ReferenceValue and the given other
+     * ReferenceValue.
+     */
+    private ReferenceValue generalizeMayBeNull(boolean mayBeNull)
+    {
+        return this.mayBeNull || !mayBeNull ?
+            this :
+            new ReferenceValue(this.type, this.referencedClass, true);
+    }
+
+
+    // Derived binary methods.
+
+    /**
+     * Returns whether this ReferenceValue and the given ReferenceValue are different.
+     * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+     */
+    public final int notEqual(ReferenceValue other)
+    {
+        return -equal(other);
+    }
+
+
+    // Implementations for Value.
+
+    public final ReferenceValue referenceValue()
+    {
+        return this;
+    }
+
+    public final Value generalize(Value other)
+    {
+        return this.generalize(other.referenceValue());
+    }
+
+    public boolean isSpecific()
+    {
+        return type == null;
+    }
+
+    public final int computationalType()
+    {
+        return TYPE_REFERENCE;
+    }
+
+    public final String internalType()
+    {
+        return
+            type == null                        ? ClassConstants.INTERNAL_TYPE_JAVA_LANG_OBJECT :
+            ClassUtil.isInternalArrayType(type) ? type                                          :
+                                                  ClassConstants.INTERNAL_TYPE_CLASS_START +
+                                                  type +
+                                                  ClassConstants.INTERNAL_TYPE_CLASS_END;
+    }
+
+
+    // Implementations for Object.
+
+    public boolean equals(Object object)
+    {
+        if (object == null ||
+            this.getClass() != object.getClass())
+        {
+            return false;
+        }
+
+        ReferenceValue other = (ReferenceValue)object;
+        return this.type == null ? other.type == null :
+                                   (this.mayBeNull == other.mayBeNull &&
+                                    this.type.equals(other.type));
+    }
+
+
+    public int hashCode()
+    {
+        return this.getClass().hashCode() ^
+               (type == null ? 0 : type.hashCode() ^ (mayBeNull ? 0 : 1));
+    }
+
+
+    public String toString()
+    {
+        return "a:" + (type == null ?
+            "null" :
+            type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!"));
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/SpecificDoubleValue.java b/src/proguard/evaluation/value/SpecificDoubleValue.java
similarity index 68%
rename from src/proguard/optimize/evaluation/value/SpecificDoubleValue.java
rename to src/proguard/evaluation/value/SpecificDoubleValue.java
index 9f79951..7076ce5 100644
--- a/src/proguard/optimize/evaluation/value/SpecificDoubleValue.java
+++ b/src/proguard/evaluation/value/SpecificDoubleValue.java
@@ -1,6 +1,6 @@
-/* $Id: SpecificDoubleValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,16 +18,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This DoubleValue represents a specific double value.
  *
  * @author Eric Lafortune
  */
-class SpecificDoubleValue extends DoubleValue
+final class SpecificDoubleValue extends DoubleValue
 {
-    private double value;
+    private final double value;
 
 
     /**
@@ -97,9 +97,9 @@ class SpecificDoubleValue extends DoubleValue
         return other.remainder(this);
     }
 
-    public IntegerValue compare(DoubleValue other)
+    public IntegerValue compare(DoubleValue other, ValueFactory valueFactory)
     {
-        return other.compareReverse(this);
+        return other.compareReverse(this, valueFactory);
     }
 
 
@@ -107,22 +107,22 @@ class SpecificDoubleValue extends DoubleValue
 
     public DoubleValue negate()
     {
-        return DoubleValueFactory.create(-value);
+        return new SpecificDoubleValue(-value);
     }
 
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create((int)value);
+        return new SpecificIntegerValue((int)value);
     }
 
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create((long)value);
+        return new SpecificLongValue((long)value);
     }
 
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create((float)value);
+        return new SpecificFloatValue((float)value);
     }
 
 
@@ -131,54 +131,54 @@ class SpecificDoubleValue extends DoubleValue
 
     public DoubleValue generalize(SpecificDoubleValue other)
     {
-        return this.value == other.value ? this : DoubleValueFactory.create();
+        return this.value == other.value ? this : ValueFactory.DOUBLE_VALUE;
     }
 
     public DoubleValue add(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(this.value + other.value);
+        return new SpecificDoubleValue(this.value + other.value);
     }
 
     public DoubleValue subtract(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(this.value - other.value);
+        return new SpecificDoubleValue(this.value - other.value);
     }
 
     public DoubleValue subtractFrom(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(other.value - this.value);
+        return new SpecificDoubleValue(other.value - this.value);
     }
 
     public DoubleValue multiply(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(this.value * other.value);
+        return new SpecificDoubleValue(this.value * other.value);
     }
 
     public DoubleValue divide(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(this.value / other.value);
+        return new SpecificDoubleValue(this.value / other.value);
     }
 
     public DoubleValue divideOf(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(other.value / this.value);
+        return new SpecificDoubleValue(other.value / this.value);
     }
 
     public DoubleValue remainder(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(this.value % other.value);
+        return new SpecificDoubleValue(this.value % other.value);
     }
 
     public DoubleValue remainderOf(SpecificDoubleValue other)
     {
-        return DoubleValueFactory.create(other.value % this.value);
+        return new SpecificDoubleValue(other.value % this.value);
     }
 
-    public IntegerValue compare(SpecificDoubleValue other)
+    public IntegerValue compare(SpecificDoubleValue other, ValueFactory valueFactory)
     {
-        return this.value <  other.value ? IntegerValueFactory.create(-1) :
-               this.value == other.value ? IntegerValueFactory.create(0) :
-                                           IntegerValueFactory.create(1);
+        return this.value <  other.value ? SpecificValueFactory.INTEGER_VALUE_M1 :
+               this.value == other.value ? SpecificValueFactory.INTEGER_VALUE_0  :
+                                           SpecificValueFactory.INTEGER_VALUE_1;
     }
 
 
diff --git a/src/proguard/optimize/evaluation/value/SpecificFloatValue.java b/src/proguard/evaluation/value/SpecificFloatValue.java
similarity index 68%
rename from src/proguard/optimize/evaluation/value/SpecificFloatValue.java
rename to src/proguard/evaluation/value/SpecificFloatValue.java
index 205a6aa..b0e37d3 100644
--- a/src/proguard/optimize/evaluation/value/SpecificFloatValue.java
+++ b/src/proguard/evaluation/value/SpecificFloatValue.java
@@ -1,6 +1,6 @@
-/* $Id: SpecificFloatValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,16 +18,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This FloatValue represents a specific float value.
  *
  * @author Eric Lafortune
  */
-class SpecificFloatValue extends FloatValue
+final class SpecificFloatValue extends FloatValue
 {
-    private float value;
+    private final float value;
 
 
     /**
@@ -97,9 +97,9 @@ class SpecificFloatValue extends FloatValue
         return other.remainder(this);
     }
 
-    public IntegerValue compare(FloatValue other)
+    public IntegerValue compare(FloatValue other, ValueFactory valueFactory)
     {
-        return other.compareReverse(this);
+        return other.compareReverse(this, valueFactory);
     }
 
 
@@ -107,22 +107,22 @@ class SpecificFloatValue extends FloatValue
 
     public FloatValue negate()
     {
-        return FloatValueFactory.create(-value);
+        return new SpecificFloatValue(-value);
     }
 
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create((int)value);
+        return valueFactory.createIntegerValue((int)value);
     }
 
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create((long)value);
+        return valueFactory.createLongValue((long)value);
     }
 
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create((double)value);
+        return valueFactory.createDoubleValue((double)value);
     }
 
 
@@ -131,54 +131,54 @@ class SpecificFloatValue extends FloatValue
 
     public FloatValue generalize(SpecificFloatValue other)
     {
-        return this.value == other.value ? this : FloatValueFactory.create();
+        return this.value == other.value ? this : ValueFactory.FLOAT_VALUE;
     }
 
     public FloatValue add(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(this.value + other.value);
+        return new SpecificFloatValue(this.value + other.value);
     }
 
     public FloatValue subtract(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(this.value - other.value);
+        return new SpecificFloatValue(this.value - other.value);
     }
 
     public FloatValue subtractFrom(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(other.value - this.value);
+        return new SpecificFloatValue(other.value - this.value);
     }
 
     public FloatValue multiply(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(this.value * other.value);
+        return new SpecificFloatValue(this.value * other.value);
     }
 
     public FloatValue divide(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(this.value / other.value);
+        return new SpecificFloatValue(this.value / other.value);
     }
 
     public FloatValue divideOf(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(other.value / this.value);
+        return new SpecificFloatValue(other.value / this.value);
     }
 
     public FloatValue remainder(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(this.value % other.value);
+        return new SpecificFloatValue(this.value % other.value);
     }
 
     public FloatValue remainderOf(SpecificFloatValue other)
     {
-        return FloatValueFactory.create(other.value % this.value);
+        return new SpecificFloatValue(other.value % this.value);
     }
 
-    public IntegerValue compare(SpecificFloatValue other)
+    public IntegerValue compare(SpecificFloatValue other, ValueFactory valueFactory)
     {
-        return this.value <  other.value ? IntegerValueFactory.create(-1) :
-               this.value == other.value ? IntegerValueFactory.create(0) :
-                                           IntegerValueFactory.create(1);
+        return this.value <  other.value ? valueFactory.createIntegerValue(-1) :
+               this.value == other.value ? valueFactory.createIntegerValue(0) :
+                                           valueFactory.createIntegerValue(1);
     }
 
 
diff --git a/src/proguard/optimize/evaluation/value/SpecificIntegerValue.java b/src/proguard/evaluation/value/SpecificIntegerValue.java
similarity index 72%
rename from src/proguard/optimize/evaluation/value/SpecificIntegerValue.java
rename to src/proguard/evaluation/value/SpecificIntegerValue.java
index 354fa18..6ec5dec 100644
--- a/src/proguard/optimize/evaluation/value/SpecificIntegerValue.java
+++ b/src/proguard/evaluation/value/SpecificIntegerValue.java
@@ -1,6 +1,6 @@
-/* $Id: SpecificIntegerValue.java,v 1.4.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,16 +18,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This IntegerValue represents a specific integer value.
  *
  * @author Eric Lafortune
  */
-class SpecificIntegerValue extends IntegerValue
+final class SpecificIntegerValue extends IntegerValue
 {
-    private int value;
+    private final int value;
 
 
     public SpecificIntegerValue(int value)
@@ -178,54 +178,54 @@ class SpecificIntegerValue extends IntegerValue
 
     public IntegerValue negate()
     {
-        return IntegerValueFactory.create(-value);
+        return new SpecificIntegerValue(-value);
     }
 
-    public IntegerValue convertToByte()
+    public IntegerValue convertToByte(ValueFactory valueFactory)
     {
         int byteValue = (byte)value;
 
         return byteValue == value ?
             this :
-            IntegerValueFactory.create(byteValue);
+            new SpecificIntegerValue(byteValue);
     }
 
-    public IntegerValue convertToCharacter()
+    public IntegerValue convertToCharacter(ValueFactory valueFactory)
     {
         int charValue = (char)value;
 
         return charValue == value ?
             this :
-            IntegerValueFactory.create(charValue);
+            new SpecificIntegerValue(charValue);
     }
 
-    public IntegerValue convertToShort()
+    public IntegerValue convertToShort(ValueFactory valueFactory)
     {
         int shortValue = (short)value;
 
         return shortValue == value ?
             this :
-            IntegerValueFactory.create(shortValue);
+            new SpecificIntegerValue(shortValue);
     }
 
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
         return this;
     }
 
-    public LongValue convertToLong()
+    public LongValue convertToLong(ValueFactory valueFactory)
     {
-        return LongValueFactory.create((long)value);
+        return valueFactory.createLongValue((long)value);
     }
 
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create((float)value);
+        return valueFactory.createFloatValue((float)value);
     }
 
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create((double)value);
+        return valueFactory.createDoubleValue((double)value);
     }
 
 
@@ -234,111 +234,111 @@ class SpecificIntegerValue extends IntegerValue
 
     public IntegerValue generalize(SpecificIntegerValue other)
     {
-        return this.value == other.value ? this : IntegerValueFactory.create();
+        return this.value == other.value ? this : ValueFactory.INTEGER_VALUE;
     }
 
     public IntegerValue add(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value + other.value);
+        return new SpecificIntegerValue(this.value + other.value);
     }
 
     public IntegerValue subtract(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value - other.value);
+        return new SpecificIntegerValue(this.value - other.value);
     }
 
     public IntegerValue subtractFrom(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(other.value - this.value);
+        return new SpecificIntegerValue(other.value - this.value);
     }
 
     public IntegerValue multiply(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value * other.value);
+        return new SpecificIntegerValue(this.value * other.value);
     }
 
     public IntegerValue divide(SpecificIntegerValue other)
     throws ArithmeticException
     {
-        return IntegerValueFactory.create(this.value / other.value);
+        return new SpecificIntegerValue(this.value / other.value);
     }
 
     public IntegerValue divideOf(SpecificIntegerValue other)
     throws ArithmeticException
     {
-        return IntegerValueFactory.create(other.value / this.value);
+        return new SpecificIntegerValue(other.value / this.value);
     }
 
     public IntegerValue remainder(SpecificIntegerValue other)
     throws ArithmeticException
     {
-        return IntegerValueFactory.create(this.value % other.value);
+        return new SpecificIntegerValue(this.value % other.value);
     }
 
     public IntegerValue remainderOf(SpecificIntegerValue other)
     throws ArithmeticException
     {
-        return IntegerValueFactory.create(other.value % this.value);
+        return new SpecificIntegerValue(other.value % this.value);
     }
 
     public IntegerValue shiftLeft(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value << other.value);
+        return new SpecificIntegerValue(this.value << other.value);
     }
 
     public IntegerValue shiftLeftOf(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(other.value << this.value);
+        return new SpecificIntegerValue(other.value << this.value);
     }
 
     public IntegerValue shiftRight(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value >> other.value);
+        return new SpecificIntegerValue(this.value >> other.value);
     }
 
     public IntegerValue shiftRightOf(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(other.value >> this.value);
+        return new SpecificIntegerValue(other.value >> this.value);
     }
 
     public IntegerValue unsignedShiftRight(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value >>> other.value);
+        return new SpecificIntegerValue(this.value >>> other.value);
     }
 
     public IntegerValue unsignedShiftRightOf(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(other.value >>> this.value);
+        return new SpecificIntegerValue(other.value >>> this.value);
     }
 
-    public LongValue shiftLeftOf(SpecificLongValue other)
+    public LongValue shiftLeftOf(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return LongValueFactory.create(other.value() << this.value);
+        return valueFactory.createLongValue(other.value() << this.value);
     }
 
-    public LongValue shiftRightOf(SpecificLongValue other)
+    public LongValue shiftRightOf(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return LongValueFactory.create(other.value() >> this.value);
+        return valueFactory.createLongValue(other.value() >> this.value);
     }
 
-    public LongValue unsignedShiftRightOf(SpecificLongValue other)
+    public LongValue unsignedShiftRightOf(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return LongValueFactory.create(other.value() >>> this.value);
+        return valueFactory.createLongValue(other.value() >>> this.value);
     }
 
     public IntegerValue and(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value & other.value);
+        return new SpecificIntegerValue(this.value & other.value);
     }
 
     public IntegerValue or(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value | other.value);
+        return new SpecificIntegerValue(this.value | other.value);
     }
 
     public IntegerValue xor(SpecificIntegerValue other)
     {
-        return IntegerValueFactory.create(this.value ^ other.value);
+        return new SpecificIntegerValue(this.value ^ other.value);
     }
 
     public int equal(SpecificIntegerValue other)
diff --git a/src/proguard/optimize/evaluation/value/SpecificLongValue.java b/src/proguard/evaluation/value/SpecificLongValue.java
similarity index 65%
rename from src/proguard/optimize/evaluation/value/SpecificLongValue.java
rename to src/proguard/evaluation/value/SpecificLongValue.java
index 94efe3a..c92e54d 100644
--- a/src/proguard/optimize/evaluation/value/SpecificLongValue.java
+++ b/src/proguard/evaluation/value/SpecificLongValue.java
@@ -1,6 +1,6 @@
-/* $Id: SpecificLongValue.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,16 +18,16 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This LongValue represents a specific long value.
  *
  * @author Eric Lafortune
  */
-class SpecificLongValue extends LongValue
+final class SpecificLongValue extends LongValue
 {
-    private long value;
+    private final long value;
 
 
     /**
@@ -127,9 +127,9 @@ class SpecificLongValue extends LongValue
         return other.xor(this);
     }
 
-    public IntegerValue compare(LongValue other)
+    public IntegerValue compare(LongValue other, ValueFactory valueFactory)
     {
-        return other.compareReverse(this);
+        return other.compareReverse(this, valueFactory);
     }
 
 
@@ -137,22 +137,22 @@ class SpecificLongValue extends LongValue
 
     public LongValue negate()
     {
-        return LongValueFactory.create(-value);
+        return new SpecificLongValue(-value);
     }
 
-    public IntegerValue convertToInteger()
+    public IntegerValue convertToInteger(ValueFactory valueFactory)
     {
-        return IntegerValueFactory.create((int)value);
+        return valueFactory.createIntegerValue((int)value);
     }
 
-    public FloatValue convertToFloat()
+    public FloatValue convertToFloat(ValueFactory valueFactory)
     {
-        return FloatValueFactory.create((float)value);
+        return valueFactory.createFloatValue((float)value);
     }
 
-    public DoubleValue convertToDouble()
+    public DoubleValue convertToDouble(ValueFactory valueFactory)
     {
-        return DoubleValueFactory.create((double)value);
+        return valueFactory.createDoubleValue((double)value);
     }
 
 
@@ -161,69 +161,84 @@ class SpecificLongValue extends LongValue
 
     public LongValue generalize(SpecificLongValue other)
     {
-        return this.value == other.value ? this : LongValueFactory.create();
+        return this.value == other.value ? this : ValueFactory.LONG_VALUE;
     }
 
     public LongValue add(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value + other.value);
+        return new SpecificLongValue(this.value + other.value);
     }
 
     public LongValue subtract(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value - other.value);
+        return new SpecificLongValue(this.value - other.value);
     }
 
     public LongValue subtractFrom(SpecificLongValue other)
     {
-        return LongValueFactory.create(other.value - this.value);
+        return new SpecificLongValue(other.value - this.value);
     }
 
     public LongValue multiply(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value * other.value);
+        return new SpecificLongValue(this.value * other.value);
     }
 
     public LongValue divide(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value / other.value);
+        return new SpecificLongValue(this.value / other.value);
     }
 
     public LongValue divideOf(SpecificLongValue other)
     {
-        return LongValueFactory.create(other.value / this.value);
+        return new SpecificLongValue(other.value / this.value);
     }
 
     public LongValue remainder(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value % other.value);
+        return new SpecificLongValue(this.value % other.value);
     }
 
     public LongValue remainderOf(SpecificLongValue other)
     {
-        return LongValueFactory.create(other.value % this.value);
+        return new SpecificLongValue(other.value % this.value);
+    }
+
+    public LongValue shiftLeft(SpecificIntegerValue other)
+    {
+        return new SpecificLongValue(this.value << other.value());
+    }
+
+    public LongValue shiftRight(SpecificIntegerValue other)
+    {
+        return new SpecificLongValue(this.value >> other.value());
+    }
+
+    public LongValue unsignedShiftRight(SpecificIntegerValue other)
+    {
+        return new SpecificLongValue(this.value >>> other.value());
     }
 
     public LongValue and(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value & other.value);
+        return new SpecificLongValue(this.value & other.value);
     }
 
     public LongValue or(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value | other.value);
+        return new SpecificLongValue(this.value | other.value);
     }
 
     public LongValue xor(SpecificLongValue other)
     {
-        return LongValueFactory.create(this.value ^ other.value);
+        return new SpecificLongValue(this.value ^ other.value);
     }
 
-    public IntegerValue compare(SpecificLongValue other)
+    public IntegerValue compare(SpecificLongValue other, ValueFactory valueFactory)
     {
-        return this.value <  other.value ? IntegerValueFactory.create(-1) :
-               this.value == other.value ? IntegerValueFactory.create(0) :
-                                           IntegerValueFactory.create(1);
+        return this.value <  other.value ? valueFactory.createIntegerValue(-1) :
+               this.value == other.value ? valueFactory.createIntegerValue(0) :
+                                           valueFactory.createIntegerValue(1);
     }
 
 
diff --git a/src/proguard/evaluation/value/SpecificValueFactory.java b/src/proguard/evaluation/value/SpecificValueFactory.java
new file mode 100644
index 0000000..5efec24
--- /dev/null
+++ b/src/proguard/evaluation/value/SpecificValueFactory.java
@@ -0,0 +1,89 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation.value;
+
+/**
+ * This class provides methods to create and reuse IntegerValue objects.
+ *
+ * @author Eric Lafortune
+ */
+public class SpecificValueFactory
+extends      ValueFactory
+{
+    // Shared copies of Value objects, to avoid creating a lot of objects.
+    static final IntegerValue INTEGER_VALUE_M1 = new SpecificIntegerValue(-1);
+    static final IntegerValue INTEGER_VALUE_0  = new SpecificIntegerValue(0);
+    static final IntegerValue INTEGER_VALUE_1  = new SpecificIntegerValue(1);
+    static final IntegerValue INTEGER_VALUE_2  = new SpecificIntegerValue(2);
+    static final IntegerValue INTEGER_VALUE_3  = new SpecificIntegerValue(3);
+    static final IntegerValue INTEGER_VALUE_4  = new SpecificIntegerValue(4);
+    static final IntegerValue INTEGER_VALUE_5  = new SpecificIntegerValue(5);
+    static final LongValue    LONG_VALUE_0     = new SpecificLongValue(0);
+    static final LongValue    LONG_VALUE_1     = new SpecificLongValue(1);
+    static final FloatValue   FLOAT_VALUE_0    = new SpecificFloatValue(0.0f);
+    static final FloatValue   FLOAT_VALUE_1    = new SpecificFloatValue(1.0f);
+    static final FloatValue   FLOAT_VALUE_2    = new SpecificFloatValue(2.0f);
+    static final DoubleValue  DOUBLE_VALUE_0   = new SpecificDoubleValue(0.0);
+    static final DoubleValue  DOUBLE_VALUE_1   = new SpecificDoubleValue(1.0);
+
+
+    // Implementations for ValueFactory.
+
+    public IntegerValue createIntegerValue(int value)
+    {
+        switch (value)
+        {
+            case -1: return INTEGER_VALUE_M1;
+            case  0: return INTEGER_VALUE_0;
+            case  1: return INTEGER_VALUE_1;
+            case  2: return INTEGER_VALUE_2;
+            case  3: return INTEGER_VALUE_3;
+            case  4: return INTEGER_VALUE_4;
+            case  5: return INTEGER_VALUE_5;
+            default: return new SpecificIntegerValue(value);
+        }
+    }
+
+
+    public LongValue createLongValue(long value)
+    {
+        return value == 0 ? LONG_VALUE_0 :
+               value == 1 ? LONG_VALUE_1 :
+                            new SpecificLongValue(value);
+    }
+
+
+    public FloatValue createFloatValue(float value)
+    {
+        return value == 0.0f ? FLOAT_VALUE_0 :
+               value == 1.0f ? FLOAT_VALUE_1 :
+               value == 2.0f ? FLOAT_VALUE_2 :
+                               new SpecificFloatValue(value);
+    }
+
+
+    public DoubleValue createDoubleValue(double value)
+    {
+        return value == 0.0 ? DOUBLE_VALUE_0 :
+               value == 1.0 ? DOUBLE_VALUE_1 :
+                              new SpecificDoubleValue(value);
+    }
+}
diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/evaluation/value/TopValue.java
similarity index 50%
copy from src/proguard/io/RenamedDataEntry.java
copy to src/proguard/evaluation/value/TopValue.java
index 61163f5..9bb6ca8 100644
--- a/src/proguard/io/RenamedDataEntry.java
+++ b/src/proguard/evaluation/value/TopValue.java
@@ -1,6 +1,6 @@
-/* $Id: RenamedDataEntry.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,60 +18,52 @@
  * 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 java.io.*;
+package proguard.evaluation.value;
 
 /**
- * This DataEntry wraps another data entry, returning a different name instead
- * of the wrapped data entry's name.
+ * This class represents a partially evaluated top value. A top value is the
+ * dummy value that takes up the extra space when storing a long value or a
+ * double value.
  *
  * @author Eric Lafortune
  */
-public class RenamedDataEntry implements DataEntry
+public class TopValue extends Category1Value
 {
-    private DataEntry dataEntry;
-    private String    name;
-
+    // Implementations for Value.
 
-    public RenamedDataEntry(DataEntry dataEntry,
-                            String    name)
+    public final Value generalize(Value other)
     {
-        this.dataEntry = dataEntry;
-        this.name      = name;
+        return this.getClass() == other.getClass() ? this : null;
     }
 
-
-    // Implementations for DataEntry.
-
-    public String getName()
+    public final int computationalType()
     {
-        return name;
+        return TYPE_TOP;
     }
 
-
-    public InputStream getInputStream() throws IOException
+    public final String internalType()
     {
-        return dataEntry.getInputStream();
+        return null;
     }
 
 
-    public void closeInputStream() throws IOException
+    // Implementations for Object.
+
+    public boolean equals(Object object)
     {
-        dataEntry.closeInputStream();
+        return object != null &&
+               this.getClass() == object.getClass();
     }
 
 
-    public DataEntry getParent()
+    public int hashCode()
     {
-        return dataEntry.getParent();
+        return this.getClass().hashCode();
     }
 
 
-    // Implementations for Object.
-
     public String toString()
     {
-        return name + " == " + dataEntry;
+        return "T";
     }
 }
diff --git a/src/proguard/optimize/evaluation/value/Value.java b/src/proguard/evaluation/value/Value.java
similarity index 74%
rename from src/proguard/optimize/evaluation/value/Value.java
rename to src/proguard/evaluation/value/Value.java
index 827b884..2cc4619 100644
--- a/src/proguard/optimize/evaluation/value/Value.java
+++ b/src/proguard/evaluation/value/Value.java
@@ -1,6 +1,6 @@
-/* $Id: Value.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,7 +18,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.evaluation.value;
+package proguard.evaluation.value;
 
 /**
  * This abstract class represents a partially evaluated value.
@@ -37,6 +37,7 @@ public abstract class Value
     public static final int TYPE_DOUBLE             = 4;
     public static final int TYPE_REFERENCE          = 5;
     public static final int TYPE_INSTRUCTION_OFFSET = 6;
+    public static final int TYPE_TOP                = 7;
 
 
     /**
@@ -129,15 +130,29 @@ public abstract class Value
 
 
     /**
-     * Returns the computational type of this Value. The type is one of
-     * <ul>
-     * <li><code>TYPE_INTEGER</code>
-     * <li><code>TYPE_LONG</code>
-     * <li><code>TYPE_FLOAT</code>
-     * <li><code>TYPE_DOUBLE</code>
-     * <li><code>TYPE_REFERENCE</code>
-     * <li><code>TYPE_INSTRUCTION_OFFSET</code>
-     * </ul>
+     * Returns the computational type of this Value.
+     * @return <code>TYPE_INTEGER</code>,
+     *         <code>TYPE_LONG</code>,
+     *         <code>TYPE_FLOAT</code>,
+     *         <code>TYPE_DOUBLE</code>,
+     *         <code>TYPE_REFERENCE</code>, or
+     *         <code>TYPE_INSTRUCTION_OFFSET</code>.
      */
     public abstract int computationalType();
+
+
+    /**
+     * Returns the internal type of this Value.
+     * @return <code>ClassConstants.INTERNAL_TYPE_BOOLEAN</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_BYTE</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_CHAR</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_SHORT</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_INT</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_LONG</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_FLOAT</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_DOUBLE</code>,
+     *         <code>ClassConstants.INTERNAL_TYPE_CLASS_START ... ClassConstants.INTERNAL_TYPE_CLASS_END</code>, or
+     *         an array type containing any of these types (always as String).
+     */
+    public abstract String internalType();
 }
diff --git a/src/proguard/evaluation/value/ValueFactory.java b/src/proguard/evaluation/value/ValueFactory.java
new file mode 100644
index 0000000..72f3e68
--- /dev/null
+++ b/src/proguard/evaluation/value/ValueFactory.java
@@ -0,0 +1,193 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.evaluation.value;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+/**
+ * This class provides methods to create and reuse Value objects.
+ *
+ * @author Eric Lafortune
+ */
+public class ValueFactory
+{
+    // Shared copies of Value objects, to avoid creating a lot of objects.
+    static final IntegerValue INTEGER_VALUE = new IntegerValue();
+    static final LongValue    LONG_VALUE    = new LongValue();
+    static final FloatValue   FLOAT_VALUE   = new FloatValue();
+    static final DoubleValue  DOUBLE_VALUE  = new DoubleValue();
+
+    static final ReferenceValue REFERENCE_VALUE_NULL                        = new ReferenceValue(null, null, true);
+    static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL = new ReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, null, true);
+    static final ReferenceValue REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL   = new ReferenceValue(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, null, false);
+
+
+    /**
+     * Creates a new undefined Value of the given type.
+     * The type must be a fully specified internal type for primitives, classes,
+     * or arrays.
+     */
+    public Value createValue(String type, Clazz referencedClass, boolean mayBeNull)
+    {
+        switch (type.charAt(0))
+        {
+            case ClassConstants.INTERNAL_TYPE_VOID:    return null;
+            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+            case ClassConstants.INTERNAL_TYPE_BYTE:
+            case ClassConstants.INTERNAL_TYPE_CHAR:
+            case ClassConstants.INTERNAL_TYPE_SHORT:
+            case ClassConstants.INTERNAL_TYPE_INT:     return INTEGER_VALUE;
+            case ClassConstants.INTERNAL_TYPE_LONG:    return LONG_VALUE;
+            case ClassConstants.INTERNAL_TYPE_FLOAT:   return FLOAT_VALUE;
+            case ClassConstants.INTERNAL_TYPE_DOUBLE:  return DOUBLE_VALUE;
+            default:                                   return createReferenceValue(ClassUtil.isInternalArrayType(type) ?
+                                                                                       type :
+                                                                                       ClassUtil.internalClassNameFromClassType(type),
+                                                                                   referencedClass,
+                                                                                   mayBeNull);
+        }
+    }
+
+    /**
+     * Creates a new IntegerValue with an undefined value.
+     */
+    public IntegerValue createIntegerValue()
+    {
+        return INTEGER_VALUE;
+    }
+
+    /**
+     * Creates a new IntegerValue with a given specific value.
+     */
+    public IntegerValue createIntegerValue(int value)
+    {
+        return createIntegerValue();
+    }
+
+
+    /**
+     * Creates a new LongValue with an undefined value.
+     */
+    public LongValue createLongValue()
+    {
+        return LONG_VALUE;
+    }
+
+    /**
+     * Creates a new LongValue with a given specific value.
+     */
+    public LongValue createLongValue(long value)
+    {
+        return createLongValue();
+    }
+
+
+    /**
+     * Creates a new FloatValue with an undefined value.
+     */
+    public FloatValue createFloatValue()
+    {
+        return FLOAT_VALUE;
+    }
+
+    /**
+     * Creates a new FloatValue with a given specific value.
+     */
+    public FloatValue createFloatValue(float value)
+    {
+        return createFloatValue();
+    }
+
+
+    /**
+     * Creates a new DoubleValue with an undefined value.
+     */
+    public DoubleValue createDoubleValue()
+    {
+        return DOUBLE_VALUE;
+    }
+
+    /**
+     * Creates a new DoubleValue with a given specific value.
+     */
+    public DoubleValue createDoubleValue(double value)
+    {
+        return createDoubleValue();
+    }
+
+
+    /**
+     * Creates a new ReferenceValue that represents <code>null</code>.
+     */
+    public ReferenceValue createReferenceValueNull()
+    {
+        return REFERENCE_VALUE_NULL;
+    }
+
+
+    /**
+     * Creates a new ReferenceValue of the given type. The type must be an
+     * internal class name or an array type. If the type is <code>null</code>,
+     * the ReferenceValue represents <code>null</code>.
+     */
+    public ReferenceValue createReferenceValue(String  type,
+                                               Clazz   referencedClass,
+                                               boolean mayBeNull)
+    {
+        return type == null                                                ? REFERENCE_VALUE_NULL                                 :
+               !type.equals(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT) ? new ReferenceValue(type, referencedClass, mayBeNull) :
+               mayBeNull                                                   ? REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL          :
+                                                                             REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL;
+    }
+
+
+    /**
+     * Creates a new ReferenceValue for arrays of the given type and length.
+     * The type must be a fully specified internal type for primitives, classes,
+     * or arrays.
+     */
+    public ReferenceValue createArrayReferenceValue(String       type,
+                                                    Clazz        referencedClass,
+                                                    IntegerValue arrayLength)
+    {
+        return createArrayReferenceValue(type,
+                                         referencedClass,
+                                         arrayLength,
+                                         createValue(type, referencedClass, false));
+    }
+
+
+    /**
+     * Creates a new ReferenceValue for arrays of the given type and length,
+     * containing the given element. The type must be a fully specified internal
+     * type for primitives, classes, or arrays.
+     */
+    public ReferenceValue createArrayReferenceValue(String       type,
+                                                    Clazz        referencedClass,
+                                                    IntegerValue arrayLength,
+                                                    Value        elementValue)
+    {
+        return createReferenceValue(ClassConstants.INTERNAL_TYPE_ARRAY + type,
+                                    referencedClass,
+                                    false);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/package.html b/src/proguard/evaluation/value/package.html
similarity index 100%
rename from src/proguard/optimize/evaluation/value/package.html
rename to src/proguard/evaluation/value/package.html
diff --git a/src/proguard/gui/ClassMemberSpecificationDialog.java b/src/proguard/gui/ClassMemberSpecificationDialog.java
deleted file mode 100644
index 19049b8..0000000
--- a/src/proguard/gui/ClassMemberSpecificationDialog.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/* $Id: ClassMemberSpecificationDialog.java,v 1.5.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.gui;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-import javax.swing.border.*;
-import javax.swing.border.Border;
-
-import proguard.*;
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.util.*;
-
-/**
- * This <code>JDialog</code> allows the user to enter a String.
- *
- * @author Eric Lafortune
- */
-class ClassMemberSpecificationDialog 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 boolean isField;
-
-    private JRadioButton[] publicRadioButtons;
-    private JRadioButton[] privateRadioButtons;
-    private JRadioButton[] protectedRadioButtons;
-    private JRadioButton[] staticRadioButtons;
-    private JRadioButton[] finalRadioButtons;
-
-    private JRadioButton[] volatileRadioButtons;
-    private JRadioButton[] transientRadioButtons;
-
-    private JRadioButton[] synchronizedRadioButtons;
-    private JRadioButton[] nativeRadioButtons;
-    private JRadioButton[] abstractRadioButtons;
-    private JRadioButton[] strictRadioButtons;
-
-    private JTextField nameTextField      = new JTextField(20);
-    private JTextField typeTextField      = new JTextField(20);
-    private JTextField argumentsTextField = new JTextField(20);
-    private int        returnValue;
-
-
-    public ClassMemberSpecificationDialog(JDialog owner, boolean isField)
-    {
-        super(owner, true);
-        setResizable(true);
-
-        // Create some constraints that can be reused.
-        GridBagConstraints constraints = new GridBagConstraints();
-        constraints.anchor = GridBagConstraints.WEST;
-        constraints.insets = new Insets(1, 2, 1, 2);
-
-        GridBagConstraints constraintsStretch = new GridBagConstraints();
-        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
-        constraintsStretch.weightx = 1.0;
-        constraintsStretch.anchor  = GridBagConstraints.WEST;
-        constraintsStretch.insets  = constraints.insets;
-
-        GridBagConstraints constraintsLast = new GridBagConstraints();
-        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
-        constraintsLast.anchor    = GridBagConstraints.WEST;
-        constraintsLast.insets    = constraints.insets;
-
-        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
-        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
-        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
-        constraintsLastStretch.weightx   = 1.0;
-        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
-        constraintsLastStretch.insets    = constraints.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    = constraints.insets;
-
-        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
-        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
-        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
-        stretchPanelConstraints.weightx   = 1.0;
-        stretchPanelConstraints.weighty   = 1.0;
-        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
-        stretchPanelConstraints.insets    = constraints.insets;
-
-        GridBagConstraints labelConstraints = new GridBagConstraints();
-        labelConstraints.anchor = GridBagConstraints.CENTER;
-        labelConstraints.insets = new Insets(2, 10, 2, 10);
-
-        GridBagConstraints lastLabelConstraints = new GridBagConstraints();
-        lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
-        lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
-        lastLabelConstraints.insets    = labelConstraints.insets;
-
-        GridBagConstraints okButtonConstraints = new GridBagConstraints();
-        okButtonConstraints.weightx = 1.0;
-        okButtonConstraints.weighty = 1.0;
-        okButtonConstraints.anchor  = GridBagConstraints.SOUTHEAST;
-        okButtonConstraints.insets  = new Insets(4, 4, 8, 4);
-
-        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
-        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
-        cancelButtonConstraints.weighty   = 1.0;
-        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
-        cancelButtonConstraints.insets    = okButtonConstraints.insets;
-
-        GridBagLayout layout = new GridBagLayout();
-
-        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
-
-        this.isField = isField;
-
-        // Create the access panel.
-        JPanel accessPanel = new JPanel(layout);
-        accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                               GUIResources.getMessage("access")));
-
-        accessPanel.add(Box.createGlue(),         labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("required")), labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("not")),      labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("dontCare")), labelConstraints);
-        accessPanel.add(Box.createGlue(),         constraintsLastStretch);
-
-        publicRadioButtons           = addRadioButtonTriplet("Public",       accessPanel);
-        privateRadioButtons          = addRadioButtonTriplet("Private",      accessPanel);
-        protectedRadioButtons        = addRadioButtonTriplet("Protected",    accessPanel);
-        staticRadioButtons           = addRadioButtonTriplet("Static",       accessPanel);
-        finalRadioButtons            = addRadioButtonTriplet("Final",        accessPanel);
-
-        if (isField)
-        {
-            volatileRadioButtons     = addRadioButtonTriplet("Volatile",     accessPanel);
-            transientRadioButtons    = addRadioButtonTriplet("Transient",    accessPanel);
-        }
-        else
-        {
-            synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel);
-            nativeRadioButtons       = addRadioButtonTriplet("Native",       accessPanel);
-            abstractRadioButtons     = addRadioButtonTriplet("Abstract",     accessPanel);
-            strictRadioButtons       = addRadioButtonTriplet("Strict",       accessPanel);
-        }
-
-        // Create the type panel.
-        JPanel typePanel = new JPanel(layout);
-        typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                             GUIResources.getMessage(isField ?
-                                                                                         "type" :
-                                                                                         "returnType")));
-
-        typePanel.add(typeTextField, constraintsLastStretch);
-
-        // Create the name panel.
-        JPanel namePanel = new JPanel(layout);
-        namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                             GUIResources.getMessage("name")));
-
-        namePanel.add(nameTextField, constraintsLastStretch);
-
-        // Create the arguments panel.
-        JPanel argumentsPanel = new JPanel(layout);
-        argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                  GUIResources.getMessage("arguments")));
-
-        argumentsPanel.add(argumentsTextField, constraintsLastStretch);
-
-        // Create the Ok button.
-        JButton okButton = new JButton(GUIResources.getMessage("ok"));
-        okButton.addActionListener(new ActionListener()
-        {
-            public void actionPerformed(ActionEvent e)
-            {
-                returnValue = APPROVE_OPTION;
-                hide();
-            }
-        });
-
-        // Create the Cancel button.
-        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
-        cancelButton.addActionListener(new ActionListener()
-        {
-            public void actionPerformed(ActionEvent e)
-            {
-                hide();
-            }
-        });
-
-        // Add all panels to the main panel.
-        JPanel mainPanel = new JPanel(layout);
-        mainPanel.add(accessPanel, panelConstraints);
-        mainPanel.add(typePanel,   panelConstraints);
-        mainPanel.add(namePanel,   panelConstraints);
-
-        if (!isField)
-        {
-            mainPanel.add(argumentsPanel, panelConstraints);
-        }
-
-        mainPanel.add(okButton,     okButtonConstraints);
-        mainPanel.add(cancelButton, cancelButtonConstraints);
-
-        getContentPane().add(mainPanel);
-    }
-
-
-    /**
-     * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
-     * given panel with a GridBagLayout, and returns the buttons in an array.
-     */
-    private JRadioButton[] addRadioButtonTriplet(String labelText,
-                                                 JPanel panel)
-    {
-        GridBagConstraints labelConstraints = new GridBagConstraints();
-        labelConstraints.anchor = GridBagConstraints.WEST;
-        labelConstraints.insets = new Insets(2, 10, 2, 10);
-
-        GridBagConstraints buttonConstraints = new GridBagConstraints();
-        buttonConstraints.insets = labelConstraints.insets;
-
-        GridBagConstraints lastGlueConstraints = new GridBagConstraints();
-        lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
-        lastGlueConstraints.weightx   = 1.0;
-
-        // Create the radio buttons.
-        JRadioButton radioButton0 = new JRadioButton();
-        JRadioButton radioButton1 = new JRadioButton();
-        JRadioButton radioButton2 = new JRadioButton();
-
-        // Put them in a button group.
-        ButtonGroup buttonGroup = new ButtonGroup();
-        buttonGroup.add(radioButton0);
-        buttonGroup.add(radioButton1);
-        buttonGroup.add(radioButton2);
-
-        // Add the label and the buttons to the panel.
-        panel.add(new JLabel(labelText), labelConstraints);
-        panel.add(radioButton0,          buttonConstraints);
-        panel.add(radioButton1,          buttonConstraints);
-        panel.add(radioButton2,          buttonConstraints);
-        panel.add(Box.createGlue(),      lastGlueConstraints);
-
-        return new JRadioButton[]
-        {
-             radioButton0,
-             radioButton1,
-             radioButton2
-        };
-    }
-
-
-    /**
-     * Sets the ClassMemberSpecification to be represented in this dialog.
-     */
-    public void setClassMemberSpecification(ClassMemberSpecification classMemberSpecification)
-    {
-        String name       = classMemberSpecification.name;
-        String descriptor = classMemberSpecification.descriptor;
-
-        // Set the access radio buttons.
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
-        setClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
-
-        // Set the class name text fields.
-        nameTextField.setText(name == null ? "" : name);
-
-        if (isField)
-        {
-            typeTextField     .setText(descriptor == null ? "" : ClassUtil.externalType(descriptor));
-        }
-        else
-        {
-            typeTextField     .setText(descriptor == null ? "" : ClassUtil.externalMethodReturnType(descriptor));
-            argumentsTextField.setText(descriptor == null ? "" : ClassUtil.externalMethodArguments(descriptor));
-        }
-    }
-
-
-    /**
-     * Returns the ClassMemberSpecification currently represented in this dialog.
-     */
-    public ClassMemberSpecification getClassMemberSpecification()
-    {
-        String  name      = nameTextField.getText();
-        String  type      = typeTextField.getText();
-        String  arguments = argumentsTextField.getText();
-
-        if (name.equals("") ||
-            name.equals("*"))
-        {
-            name = null;
-        }
-
-        if (type.equals("") ||
-            type.equals("*"))
-        {
-            type = null;
-        }
-
-        if (name != null ||
-            type != null)
-        {
-            if (isField)
-            {
-                if (type == null)
-                {
-                    type = ClassConstants.EXTERNAL_TYPE_INT;
-                }
-
-                type = ClassUtil.internalType(type);
-            }
-            else
-            {
-                if (type == null)
-                {
-                    type = ClassConstants.EXTERNAL_TYPE_VOID;
-                }
-
-                type = ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments));
-            }
-        }
-
-        ClassMemberSpecification classMemberSpecification =
-            new ClassMemberSpecification(0, 0, name, type);
-
-        // Also get the access radio button settings.
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
-        getClassMemberSpecificationRadioButtons(classMemberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
-
-        return classMemberSpecification;
-    }
-
-
-    /**
-     * 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;
-    }
-
-
-    /**
-     * Sets the appropriate radio button of a given triplet, based on the access
-     * flags of the given keep option.
-     */
-    private void setClassMemberSpecificationRadioButtons(ClassMemberSpecification classMemberSpecification,
-                                                         int                   flag,
-                                                         JRadioButton[]        radioButtons)
-    {
-        if (radioButtons != null)
-        {
-            int index = (classMemberSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
-                        (classMemberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
-                                                                                       2;
-            radioButtons[index].setSelected(true);
-        }
-    }
-
-
-    /**
-     * Updates the access flag of the given keep option, based on the given radio
-     * button triplet.
-     */
-    private void getClassMemberSpecificationRadioButtons(ClassMemberSpecification classMemberSpecification,
-                                                         int                   flag,
-                                                         JRadioButton[]        radioButtons)
-    {
-        if (radioButtons != null)
-        {
-            if      (radioButtons[0].isSelected())
-            {
-                classMemberSpecification.requiredSetAccessFlags   |= flag;
-            }
-            else if (radioButtons[1].isSelected())
-            {
-                classMemberSpecification.requiredUnsetAccessFlags |= flag;
-            }
-        }
-    }
-}
diff --git a/src/proguard/gui/ClassPathPanel.java b/src/proguard/gui/ClassPathPanel.java
index 460e852..d336f2f 100644
--- a/src/proguard/gui/ClassPathPanel.java
+++ b/src/proguard/gui/ClassPathPanel.java
@@ -1,4 +1,4 @@
-/* $Id: ClassPathPanel.java,v 1.16.2.2 2007/01/18 21:31:52 eric Exp $
+/* $Id: ClassPathPanel.java,v 1.22 2007/08/18 10:34:56 eric Exp $
  *
  * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
  *
@@ -36,10 +36,10 @@ import proguard.*;
  */
 class ClassPathPanel extends ListPanel
 {
-    private JFrame       owner;
-    private boolean      inputAndOutput;
-    private JFileChooser chooser;
-    private FilterDialog filterDialog;
+    private final JFrame       owner;
+    private final boolean      inputAndOutput;
+    private final JFileChooser chooser;
+    private final FilterDialog filterDialog;
 
 
     public ClassPathPanel(JFrame owner, boolean inputAndOutput)
@@ -57,11 +57,11 @@ class ClassPathPanel extends ListPanel
         chooser.setMultiSelectionEnabled(true);
         chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
         chooser.addChoosableFileFilter(
-            new ExtensionFileFilter(GUIResources.getMessage("jarWarEarZipExtensions"),
+            new ExtensionFileFilter(msg("jarWarEarZipExtensions"),
                                     new String[] { ".jar", ".war", ".ear", ".zip" }));
-        chooser.setApproveButtonText(GUIResources.getMessage("ok"));
+        chooser.setApproveButtonText(msg("ok"));
 
-        filterDialog = new FilterDialog(owner, GUIResources.getMessage("enterFilter"));
+        filterDialog = new FilterDialog(owner, msg("enterFilter"));
 
         addAddButton(inputAndOutput, false);
         if (inputAndOutput)
@@ -81,15 +81,15 @@ class ClassPathPanel extends ListPanel
     protected void addAddButton(boolean       inputAndOutput,
                                 final boolean isOutput)
     {
-        JButton addButton = new JButton(GUIResources.getMessage(inputAndOutput ?
-                                                                isOutput ? "addOutput" :
-                                                                           "addInput" :
-                                                                           "add"));
+        JButton addButton = new JButton(msg(inputAndOutput ?
+                                            isOutput       ? "addOutput" :
+                                                             "addInput" :
+                                                             "add"));
         addButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
             {
-                chooser.setDialogTitle(GUIResources.getMessage("addJars"));
+                chooser.setDialogTitle(msg("addJars"));
                 chooser.setSelectedFile(null);
                 chooser.setSelectedFiles(null);
 
@@ -105,13 +105,16 @@ class ClassPathPanel extends ListPanel
             }
         });
 
-        addButton(addButton);
+        addButton(tip(addButton, inputAndOutput ?
+                                 isOutput       ? "addOutputTip" :
+                                                  "addInputTip" :
+                                                  "addTip"));
     }
 
 
     protected void addEditButton()
     {
-        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        JButton editButton = new JButton(msg("edit"));
         editButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -132,7 +135,7 @@ class ClassPathPanel extends ListPanel
                     selectedFiles[index] = entry.getFile();
                 }
 
-                chooser.setDialogTitle(GUIResources.getMessage("chooseJars"));
+                chooser.setDialogTitle(msg("chooseJars"));
 
                 // Up to JDK 1.3.1, setSelectedFiles doesn't show in the file
                 // chooser, so we just use setSelectedFile first. It also sets
@@ -165,13 +168,13 @@ class ClassPathPanel extends ListPanel
             }
         });
 
-        addButton(editButton);
+        addButton(tip(editButton, "editTip"));
     }
 
 
     protected void addFilterButton()
     {
-        JButton filterButton = new JButton(GUIResources.getMessage("filter"));
+        JButton filterButton = new JButton(msg("filter"));
         filterButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -193,7 +196,7 @@ class ClassPathPanel extends ListPanel
             }
         });
 
-        addButton(filterButton);
+        addButton(tip(filterButton, "filterTip"));
     }
 
 
@@ -292,18 +295,40 @@ class ClassPathPanel extends ListPanel
 
 
     /**
+     * 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);
+    }
+
+
+    /**
      * This ListCellRenderer renders ClassPathEntry objects.
      */
     private class MyListCellRenderer implements ListCellRenderer
     {
         private static final String ARROW_IMAGE_FILE = "arrow.gif";
 
-        private JPanel cellPanel    = new JPanel(new GridBagLayout());
-        private JLabel iconLabel    = new JLabel("", JLabel.RIGHT);
-        private JLabel jarNameLabel = new JLabel("", JLabel.RIGHT);
-        private JLabel filterLabel  = new JLabel("", JLabel.RIGHT);
+        private final JPanel cellPanel    = new JPanel(new GridBagLayout());
+        private final JLabel iconLabel    = new JLabel("", JLabel.RIGHT);
+        private final JLabel jarNameLabel = new JLabel("", JLabel.RIGHT);
+        private final JLabel filterLabel  = new JLabel("", JLabel.RIGHT);
 
-        private Icon arrowIcon;
+        private final Icon arrowIcon;
 
 
         public MyListCellRenderer()
diff --git a/src/proguard/gui/ClassSpecificationDialog.java b/src/proguard/gui/ClassSpecificationDialog.java
index 11cb1ec..8c8ba9f 100644
--- a/src/proguard/gui/ClassSpecificationDialog.java
+++ b/src/proguard/gui/ClassSpecificationDialog.java
@@ -1,6 +1,6 @@
-/* $Id: ClassSpecificationDialog.java,v 1.4.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,25 +20,22 @@
  */
 package proguard.gui;
 
-import java.awt.*;
-import java.awt.event.*;
+import proguard.*;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
 
 import javax.swing.*;
 import javax.swing.border.*;
-import javax.swing.border.Border;
-
+import java.awt.*;
+import java.awt.event.*;
 import java.util.List;
 
-import proguard.ClassSpecification;
-import proguard.classfile.ClassConstants;
-import proguard.classfile.util.ClassUtil;
-
 /**
  * This <code>JDialog</code> allows the user to enter a String.
  *
  * @author Eric Lafortune
  */
-class ClassSpecificationDialog extends JDialog
+final class ClassSpecificationDialog extends JDialog
 {
     /**
      * Return value if the dialog is canceled (with the Cancel button or by
@@ -52,21 +49,28 @@ class ClassSpecificationDialog extends JDialog
     public static final int APPROVE_OPTION = 0;
 
 
-    private JTextArea commentsTextArea = new JTextArea(4, 20);
+    private final JTextArea commentsTextArea = new JTextArea(4, 20);
+
+    private final JRadioButton keepClassesAndMembersRadioButton  = new JRadioButton(msg("keep"));
+    private final JRadioButton keepClassMembersRadioButton       = new JRadioButton(msg("keepClassMembers"));
+    private final JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(msg("keepClassesWithMembers"));
+
+    private final JRadioButton allowShrinkingRadioButton    = new JRadioButton(msg("allowShrinking"));
+    private final JRadioButton allowOptimizationRadioButton = new JRadioButton(msg("allowOptimization"));
+    private final JRadioButton allowObfuscationRadioButton  = new JRadioButton(msg("allowObfuscation"));
 
-    private JRadioButton keepClassesAndMembersRadioButton  = new JRadioButton(GUIResources.getMessage("keep"));
-    private JRadioButton keepClassMembersRadioButton       = new JRadioButton(GUIResources.getMessage("keepClassMembers"));
-    private JRadioButton keepClassesWithMembersRadioButton = new JRadioButton(GUIResources.getMessage("keepClassesWithMembers"));
 
-    private JRadioButton[] publicRadioButtons;
-    private JRadioButton[] finalRadioButtons;
-    private JRadioButton[] interfaceRadioButtons;
-    private JRadioButton[] abstractRadioButtons;
+    private final JRadioButton[] publicRadioButtons;
+    private final JRadioButton[] finalRadioButtons;
+    private final JRadioButton[] interfaceRadioButtons;
+    private final JRadioButton[] abstractRadioButtons;
 
-    private JTextField classNameTextField        = new JTextField(20);
-    private JTextField extendsClassNameTextField = new JTextField(20);
+    private final JTextField annotationTypeTextField        = new JTextField(20);
+    private final JTextField classNameTextField             = new JTextField(20);
+    private final JTextField extendsAnnotationTypeTextField = new JTextField(20);
+    private final JTextField extendsClassNameTextField      = new JTextField(20);
 
-    private ClassMemberSpecificationsPanel classMembersPanel;
+    private final MemberSpecificationsPanel memberSpecificationsPanel;
 
     private int returnValue;
 
@@ -124,17 +128,23 @@ class ClassSpecificationDialog extends JDialog
         lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
         lastLabelConstraints.insets    = labelConstraints.insets;
 
+        GridBagConstraints advancedButtonConstraints = new GridBagConstraints();
+        advancedButtonConstraints.weightx = 1.0;
+        advancedButtonConstraints.weighty = 1.0;
+        advancedButtonConstraints.anchor  = GridBagConstraints.SOUTHWEST;
+        advancedButtonConstraints.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  = new Insets(4, 4, 8, 4);
+        okButtonConstraints.insets  = advancedButtonConstraints.insets;
 
         GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
         cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
         cancelButtonConstraints.weighty   = 1.0;
         cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
-        cancelButtonConstraints.insets    = okButtonConstraints.insets;
+        cancelButtonConstraints.insets    = advancedButtonConstraints.insets;
 
         GridBagLayout layout = new GridBagLayout();
 
@@ -143,12 +153,12 @@ class ClassSpecificationDialog extends JDialog
         // Create the comments panel.
         JPanel commentsPanel = new JPanel(layout);
         commentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                 GUIResources.getMessage("comments")));
+                                                                 msg("comments")));
 
         JScrollPane commentsScrollPane = new JScrollPane(commentsTextArea);
         commentsScrollPane.setBorder(classNameTextField.getBorder());
 
-        commentsPanel.add(commentsScrollPane, constraintsLastStretch);
+        commentsPanel.add(tip(commentsScrollPane, "commentsTip"), constraintsLastStretch);
 
         // Create the keep option panel.
         ButtonGroup keepButtonGroup = new ButtonGroup();
@@ -158,50 +168,92 @@ class ClassSpecificationDialog extends JDialog
 
         JPanel keepOptionPanel = new JPanel(layout);
         keepOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                   GUIResources.getMessage("keepTitle")));
+                                                                   msg("keepTitle")));
+
+        keepOptionPanel.add(tip(keepClassesAndMembersRadioButton,  "keepTip"),                   constraintsLastStretch);
+        keepOptionPanel.add(tip(keepClassMembersRadioButton,       "keepClassMembersTip"),       constraintsLastStretch);
+        keepOptionPanel.add(tip(keepClassesWithMembersRadioButton, "keepClassesWithMembersTip"), constraintsLastStretch);
 
-        keepOptionPanel.add(keepClassesAndMembersRadioButton,  constraintsLastStretch);
-        keepOptionPanel.add(keepClassMembersRadioButton,       constraintsLastStretch);
-        keepOptionPanel.add(keepClassesWithMembersRadioButton, constraintsLastStretch);
+        // Create the allow option panel.
+        final JPanel allowOptionPanel = new JPanel(layout);
+        allowOptionPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                    msg("allowTitle")));
+
+        allowOptionPanel.add(tip(allowShrinkingRadioButton,    "allowShrinkingTip"),    constraintsLastStretch);
+        allowOptionPanel.add(tip(allowOptimizationRadioButton, "allowOptimizationTip"), constraintsLastStretch);
+        allowOptionPanel.add(tip(allowObfuscationRadioButton,  "allowObfuscationTip"),  constraintsLastStretch);
 
         // Create the access panel.
         JPanel accessPanel = new JPanel(layout);
         accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                               GUIResources.getMessage("access")));
+                                                               msg("access")));
 
-        accessPanel.add(Box.createGlue(),         labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("required")),   labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("not")),        labelConstraints);
-        accessPanel.add(new JLabel(GUIResources.getMessage("dontCare")), labelConstraints);
-        accessPanel.add(Box.createGlue(),         constraintsLastStretch);
+        accessPanel.add(Box.createGlue(),                                labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("not")),      "notTip"),      labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints);
+        accessPanel.add(Box.createGlue(),                                constraintsLastStretch);
 
         publicRadioButtons    = addRadioButtonTriplet("Public",    accessPanel);
         finalRadioButtons     = addRadioButtonTriplet("Final",     accessPanel);
         interfaceRadioButtons = addRadioButtonTriplet("Interface", accessPanel);
         abstractRadioButtons  = addRadioButtonTriplet("Abstract",  accessPanel);
 
+        // Create the annotation type panel.
+        final JPanel annotationTypePanel = new JPanel(layout);
+        annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                       msg("annotation")));
+
+        annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch);
+
         // Create the class name panel.
         JPanel classNamePanel = new JPanel(layout);
         classNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                  GUIResources.getMessage("class")));
+                                                                  msg("class")));
+
+        classNamePanel.add(tip(classNameTextField, "classNameTip"), constraintsLastStretch);
+
+        // Create the extends annotation type panel.
+        final JPanel extendsAnnotationTypePanel = new JPanel(layout);
+        extendsAnnotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                              msg("extendsImplementsAnnotation")));
 
-        classNamePanel.add(classNameTextField, constraintsLastStretch);
+        extendsAnnotationTypePanel.add(tip(extendsAnnotationTypeTextField, "classNameTip"), constraintsLastStretch);
 
         // Create the extends class name panel.
         JPanel extendsClassNamePanel = new JPanel(layout);
         extendsClassNamePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                         GUIResources.getMessage("extendsImplementsClass")));
+                                                                         msg("extendsImplementsClass")));
 
-        extendsClassNamePanel.add(extendsClassNameTextField, constraintsLastStretch);
+        extendsClassNamePanel.add(tip(extendsClassNameTextField, "classNameTip"), constraintsLastStretch);
 
 
         // Create the class member list panel.
-        classMembersPanel = new ClassMemberSpecificationsPanel(this, fullKeepOptions);
-        classMembersPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                                     GUIResources.getMessage("classMembers")));
+        memberSpecificationsPanel = new MemberSpecificationsPanel(this, fullKeepOptions);
+        memberSpecificationsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                             msg("classMembers")));
+
+        // Create the Advanced button.
+        final JButton advancedButton = new JButton(msg("basic"));
+        advancedButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                boolean visible = !allowOptionPanel.isVisible();
+
+                allowOptionPanel.setVisible(visible);
+                annotationTypePanel.setVisible(visible);
+                extendsAnnotationTypePanel.setVisible(visible);
+
+                advancedButton.setText(msg(visible ? "basic" : "advanced"));
+
+                pack();
+            }
+        });
+        advancedButton.doClick();
 
         // Create the Ok button.
-        JButton okButton = new JButton(GUIResources.getMessage("ok"));
+        JButton okButton = new JButton(msg("ok"));
         okButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -212,7 +264,7 @@ class ClassSpecificationDialog extends JDialog
         });
 
         // Create the Cancel button.
-        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
+        JButton cancelButton = new JButton(msg("cancel"));
         cancelButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -223,18 +275,22 @@ class ClassSpecificationDialog extends JDialog
 
         // Add all panels to the main panel.
         JPanel mainPanel = new JPanel(layout);
-        mainPanel.add(commentsPanel,         panelConstraints);
+        mainPanel.add(tip(commentsPanel,              "commentsTip"),                    panelConstraints);
         if (fullKeepOptions)
         {
-            mainPanel.add(keepOptionPanel,       panelConstraints);
+            mainPanel.add(tip(keepOptionPanel,        "keepTitleTip"),                   panelConstraints);
+            mainPanel.add(tip(allowOptionPanel,       "allowTitleTip"),                  panelConstraints);
         }
-        mainPanel.add(accessPanel,           panelConstraints);
-        mainPanel.add(classNamePanel,        panelConstraints);
-        mainPanel.add(extendsClassNamePanel, panelConstraints);
-        mainPanel.add(classMembersPanel,     stretchPanelConstraints);
+        mainPanel.add(tip(accessPanel,                "accessTip"),                      panelConstraints);
+        mainPanel.add(tip(annotationTypePanel,        "annotationTip"),                  panelConstraints);
+        mainPanel.add(tip(classNamePanel,             "classTip"),                       panelConstraints);
+        mainPanel.add(tip(extendsAnnotationTypePanel, "extendsImplementsAnnotationTip"), panelConstraints);
+        mainPanel.add(tip(extendsClassNamePanel,      "extendsImplementsClassTip"),      panelConstraints);
+        mainPanel.add(tip(memberSpecificationsPanel,  "classMembersTip"),                stretchPanelConstraints);
 
-        mainPanel.add(okButton,              okButtonConstraints);
-        mainPanel.add(cancelButton,          cancelButtonConstraints);
+        mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints);
+        mainPanel.add(okButton,                           okButtonConstraints);
+        mainPanel.add(cancelButton,                       cancelButtonConstraints);
 
         getContentPane().add(mainPanel);
     }
@@ -286,41 +342,83 @@ class ClassSpecificationDialog extends JDialog
 
 
     /**
-     * Sets the ClassSpecification to be represented in this dialog.
+     * Sets the KeepSpecification to be represented in this dialog.
      */
-    public void setClassSpecification(ClassSpecification classSpecification)
+    public void setKeepSpecification(KeepSpecification keepSpecification)
     {
-        String  className         = classSpecification.className;
-        String  extendsClassName  = classSpecification.extendsClassName;
-        boolean markClassFiles    = classSpecification.markClassFiles;
-        boolean markConditionally = classSpecification.markConditionally;
-        String  comments          = classSpecification.comments;
-        List    keepFieldOptions  = classSpecification.fieldSpecifications;
-        List    keepMethodOptions = classSpecification.methodSpecifications;
-
-        // Set the comments text area.
-        commentsTextArea.setText(comments == null ? "" : comments);
+        boolean markClasses       = keepSpecification.markClasses;
+        boolean markConditionally = keepSpecification.markConditionally;
+        boolean allowShrinking    = keepSpecification.allowShrinking;
+        boolean allowOptimization = keepSpecification.allowOptimization;
+        boolean allowObfuscation  = keepSpecification.allowObfuscation;
 
         // Figure out the proper keep radio button and set it.
         JRadioButton keepOptionRadioButton =
             markConditionally ? keepClassesWithMembersRadioButton :
-            markClassFiles    ? keepClassesAndMembersRadioButton  :
+            markClasses       ? keepClassesAndMembersRadioButton  :
                                 keepClassMembersRadioButton;
 
         keepOptionRadioButton.setSelected(true);
 
+        // Set the allow radio buttons.
+        allowShrinkingRadioButton   .setSelected(allowShrinking);
+        allowOptimizationRadioButton.setSelected(allowOptimization);
+        allowObfuscationRadioButton .setSelected(allowObfuscation);
+
+        setClassSpecification(keepSpecification);
+    }
+
+
+    /**
+     * Sets the ClassSpecification to be represented in this dialog.
+     */
+    public void setClassSpecification(ClassSpecification classSpecification)
+    {
+        String comments              = classSpecification.comments;
+        String annotationType        = classSpecification.annotationType;
+        String className             = classSpecification.className;
+        String extendsAnnotationType = classSpecification.extendsAnnotationType;
+        String extendsClassName      = classSpecification.extendsClassName;
+        List   keepFieldOptions      = classSpecification.fieldSpecifications;
+        List   keepMethodOptions     = classSpecification.methodSpecifications;
+
+        // Set the comments text area.
+        commentsTextArea.setText(comments == null ? "" : comments);
+
         // Set the access radio buttons.
         setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,    publicRadioButtons);
         setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_FINAL,     finalRadioButtons);
         setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_INTERFACE, interfaceRadioButtons);
         setClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,  abstractRadioButtons);
 
-        // Set the class name text fields.
-        classNameTextField       .setText(className        == null ? "*" : ClassUtil.externalClassName(className));
-        extendsClassNameTextField.setText(extendsClassName == null ? ""  : ClassUtil.externalClassName(extendsClassName));
+        // Set the class and annotation text fields.
+        annotationTypeTextField       .setText(annotationType        == null ? ""  : ClassUtil.externalType(annotationType));
+        classNameTextField            .setText(className             == null ? "*" : ClassUtil.externalClassName(className));
+        extendsAnnotationTypeTextField.setText(extendsAnnotationType == null ? ""  : ClassUtil.externalType(extendsAnnotationType));
+        extendsClassNameTextField     .setText(extendsClassName      == null ? ""  : ClassUtil.externalClassName(extendsClassName));
 
         // Set the keep class member option list.
-        classMembersPanel.setClassMemberSpecifications(keepFieldOptions, keepMethodOptions);
+        memberSpecificationsPanel.setMemberSpecifications(keepFieldOptions, keepMethodOptions);
+    }
+
+
+    /**
+     * Returns the KeepSpecification currently represented in this dialog.
+     */
+    public KeepSpecification getKeepSpecification()
+    {
+        boolean markClasses       = !keepClassMembersRadioButton     .isSelected();
+        boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
+        boolean allowShrinking    = allowShrinkingRadioButton        .isSelected();
+        boolean allowOptimization = allowOptimizationRadioButton     .isSelected();
+        boolean allowObfuscation  = allowObfuscationRadioButton      .isSelected();
+
+        return new KeepSpecification(markClasses,
+                                     markConditionally,
+                                     allowShrinking,
+                                     allowOptimization,
+                                     allowObfuscation,
+                                     getClassSpecification());
     }
 
 
@@ -329,21 +427,21 @@ class ClassSpecificationDialog extends JDialog
      */
     public ClassSpecification getClassSpecification()
     {
-        String  comments          = commentsTextArea.getText();
-        String  className         = classNameTextField.getText();
-        String  extendsClassName  = extendsClassNameTextField.getText();
-        boolean markClassFiles    = !keepClassMembersRadioButton.isSelected();
-        boolean markConditionally = keepClassesWithMembersRadioButton.isSelected();
+        String comments              = commentsTextArea.getText();
+        String annotationType        = annotationTypeTextField.getText();
+        String className             = classNameTextField.getText();
+        String extendsAnnotationType = extendsAnnotationTypeTextField.getText();
+        String extendsClassName      = extendsClassNameTextField.getText();
 
         ClassSpecification classSpecification =
-            new ClassSpecification(0,
-                                    0,
-                                    className.equals("") ||
-                                    className.equals("*")       ? null : ClassUtil.internalClassName(className),
-                                    extendsClassName.equals("") ? null : ClassUtil.internalClassName(extendsClassName),
-                                    markClassFiles,
-                                    markConditionally,
-                                    comments.equals("")         ? null : comments);
+            new ClassSpecification(comments.equals("")              ? null : comments,
+                                   0,
+                                   0,
+                                   annotationType.equals("")        ? null : ClassUtil.internalType(annotationType),
+                                   className.equals("") ||
+                                   className.equals("*")            ? null : ClassUtil.internalClassName(className),
+                                   extendsAnnotationType.equals("") ? null : ClassUtil.internalType(extendsAnnotationType),
+                                   extendsClassName.equals("")      ? null : ClassUtil.internalClassName(extendsClassName));
 
         // Also get the access radio button settings.
         getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,    publicRadioButtons);
@@ -352,8 +450,8 @@ class ClassSpecificationDialog extends JDialog
         getClassSpecificationRadioButtons(classSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,  abstractRadioButtons);
 
         // Get the keep class member option lists.
-        classSpecification.fieldSpecifications  = classMembersPanel.getClassMemberSpecifications(true);
-        classSpecification.methodSpecifications = classMembersPanel.getClassMemberSpecifications(false);
+        classSpecification.fieldSpecifications  = memberSpecificationsPanel.getMemberSpecifications(true);
+        classSpecification.methodSpecifications = memberSpecificationsPanel.getMemberSpecifications(false);
 
         return classSpecification;
     }
@@ -411,4 +509,26 @@ class ClassSpecificationDialog extends JDialog
             classSpecification.requiredUnsetAccessFlags |= flag;
         }
     }
+
+
+    /**
+     * 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);
+    }
 }
diff --git a/src/proguard/gui/ClassSpecificationsPanel.java b/src/proguard/gui/ClassSpecificationsPanel.java
index 1c928e3..4f32976 100644
--- a/src/proguard/gui/ClassSpecificationsPanel.java
+++ b/src/proguard/gui/ClassSpecificationsPanel.java
@@ -1,6 +1,6 @@
-/* $Id: ClassSpecificationsPanel.java,v 1.5.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,14 +20,14 @@
  */
 package proguard.gui;
 
-import proguard.*;
+import proguard.ClassSpecification;
 import proguard.classfile.util.ClassUtil;
 
-import java.awt.Component;
+import javax.swing.*;
+import java.awt.*;
 import java.awt.event.*;
 import java.util.*;
-
-import javax.swing.*;
+import java.util.List;
 
 
 /**
@@ -38,7 +38,7 @@ import javax.swing.*;
  */
 class ClassSpecificationsPanel extends ListPanel
 {
-    private ClassSpecificationDialog classSpecificationDialog;
+    protected final ClassSpecificationDialog classSpecificationDialog;
 
 
     public ClassSpecificationsPanel(JFrame owner, boolean fullKeepOptions)
@@ -61,28 +61,28 @@ class ClassSpecificationsPanel extends ListPanel
 
     protected void addAddButton()
     {
-        JButton addButton = new JButton(GUIResources.getMessage("add"));
+        JButton addButton = new JButton(msg("add"));
         addButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
             {
-                classSpecificationDialog.setClassSpecification(new ClassSpecification());
+                setClassSpecification(createClassSpecification());
                 int returnValue = classSpecificationDialog.showDialog();
                 if (returnValue == ClassSpecificationDialog.APPROVE_OPTION)
                 {
                     // Add the new element.
-                    addElement(classSpecificationDialog.getClassSpecification());
+                    addElement(getClassSpecification());
                 }
             }
         });
 
-        addButton(addButton);
+        addButton(tip(addButton, "addTip"));
     }
 
 
     protected void addEditButton()
     {
-        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        JButton editButton = new JButton(msg("edit"));
         editButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -90,18 +90,36 @@ class ClassSpecificationsPanel extends ListPanel
                 ClassSpecification selectedClassSpecification =
                     (ClassSpecification)list.getSelectedValue();
 
-                classSpecificationDialog.setClassSpecification(selectedClassSpecification);
+                setClassSpecification(selectedClassSpecification);
                 int returnValue = classSpecificationDialog.showDialog();
                 if (returnValue == ClassSpecificationDialog.APPROVE_OPTION)
                 {
                     // Replace the old element.
-                    setElementAt(classSpecificationDialog.getClassSpecification(),
+                    setElementAt(getClassSpecification(),
                                  list.getSelectedIndex());
                 }
             }
         });
 
-        addButton(editButton);
+        addButton(tip(editButton, "editTip"));
+    }
+
+
+    protected ClassSpecification createClassSpecification()
+    {
+        return new ClassSpecification();
+    }
+
+
+    protected void setClassSpecification(ClassSpecification classSpecification)
+    {
+        classSpecificationDialog.setClassSpecification(classSpecification);
+    }
+
+
+    protected ClassSpecification getClassSpecification()
+    {
+        return classSpecificationDialog.getClassSpecification();
     }
 
 
@@ -148,11 +166,33 @@ class ClassSpecificationsPanel extends ListPanel
 
 
     /**
+     * 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);
+    }
+
+
+    /**
      * This ListCellRenderer renders ClassSpecification objects.
      */
     private static class MyListCellRenderer implements ListCellRenderer
     {
-        JLabel label = new JLabel();
+        private final JLabel label = new JLabel();
 
 
         // Implementations for ListCellRenderer.
@@ -163,14 +203,14 @@ class ClassSpecificationsPanel extends ListPanel
                                                       boolean isSelected,
                                                       boolean cellHasFocus)
         {
-            ClassSpecification option = (ClassSpecification)value;
+            ClassSpecification classSpecification = (ClassSpecification)value;
 
-            String comments = option.comments;
+            String comments = classSpecification.comments;
 
-            label.setText(comments                 != null ? comments.trim()                                                                                        :
-                          option.className         != null ? (GUIResources.getMessage("class") + ' ' + ClassUtil.externalClassName(option.className))               :
-                          option.extendsClassName  != null ? (GUIResources.getMessage("extensionsOf") + ' ' + ClassUtil.externalClassName(option.extendsClassName)) :
-                                                             (GUIResources.getMessage("specificationNumber") + index));
+            label.setText(comments                            != null ? comments.trim()                                                                                        :
+                          classSpecification.className        != null ? (msg("class") + ' ' + ClassUtil.externalClassName(classSpecification.className))               :
+                          classSpecification.extendsClassName != null ? (msg("extensionsOf") + ' ' + ClassUtil.externalClassName(classSpecification.extendsClassName)) :
+                                                                        (msg("specificationNumber") + index));
 
             if (isSelected)
             {
diff --git a/src/proguard/gui/ExtensionFileFilter.java b/src/proguard/gui/ExtensionFileFilter.java
index 90f3157..88dfdd0 100644
--- a/src/proguard/gui/ExtensionFileFilter.java
+++ b/src/proguard/gui/ExtensionFileFilter.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionFileFilter.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,8 +20,8 @@
  */
 package proguard.gui;
 
-import java.io.File;
 import javax.swing.filechooser.FileFilter;
+import java.io.File;
 
 
 /**
@@ -30,10 +30,10 @@ import javax.swing.filechooser.FileFilter;
  *
  * @author Eric Lafortune
  */
-class ExtensionFileFilter extends FileFilter
+final class ExtensionFileFilter extends FileFilter
 {
-    private String   description;
-    private String[] extensions;
+    private final String   description;
+    private final String[] extensions;
 
 
     /**
diff --git a/src/proguard/gui/FilterDialog.java b/src/proguard/gui/FilterDialog.java
index 9b6839b..f2d5c9b 100644
--- a/src/proguard/gui/FilterDialog.java
+++ b/src/proguard/gui/FilterDialog.java
@@ -1,6 +1,6 @@
-/* $Id: FilterDialog.java,v 1.4.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,10 @@
  */
 package proguard.gui;
 
-import java.awt.*;
-import java.awt.event.*;
-
 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.
@@ -51,11 +50,11 @@ public class FilterDialog extends JDialog
     private static final String DEFAULT_ZIP_FILTER = "**.zip";
 
 
-    private JTextField filterTextField    = new JTextField(40);
-    private JTextField jarFilterTextField = new JTextField(40);
-    private JTextField warFilterTextField = new JTextField(40);
-    private JTextField earFilterTextField = new JTextField(40);
-    private JTextField zipFilterTextField = new JTextField(40);
+    private final JTextField filterTextField    = new JTextField(40);
+    private final JTextField jarFilterTextField = new JTextField(40);
+    private final JTextField warFilterTextField = new JTextField(40);
+    private final JTextField earFilterTextField = new JTextField(40);
+    private final JTextField zipFilterTextField = new JTextField(40);
     private int        returnValue;
 
 
@@ -117,36 +116,36 @@ public class FilterDialog extends JDialog
         explanationTextArea.setWrapStyleWord(true);
 
         // Create the filter labels.
-        JLabel filterLabel    = new JLabel(GUIResources.getMessage("nameFilter"));
-        JLabel jarFilterLabel = new JLabel(GUIResources.getMessage("jarNameFilter"));
-        JLabel warFilterLabel = new JLabel(GUIResources.getMessage("warNameFilter"));
-        JLabel earFilterLabel = new JLabel(GUIResources.getMessage("earNameFilter"));
-        JLabel zipFilterLabel = new JLabel(GUIResources.getMessage("zipNameFilter"));
+        JLabel filterLabel    = new JLabel(msg("nameFilter"));
+        JLabel jarFilterLabel = new JLabel(msg("jarNameFilter"));
+        JLabel warFilterLabel = new JLabel(msg("warNameFilter"));
+        JLabel earFilterLabel = new JLabel(msg("earNameFilter"));
+        JLabel zipFilterLabel = new JLabel(msg("zipNameFilter"));
 
         // Create the filter panel.
         JPanel filterPanel = new JPanel(layout);
         filterPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
-                                                               GUIResources.getMessage("filters")));
+                                                               msg("filters")));
 
         filterPanel.add(explanationTextArea, textConstraints);
 
-        filterPanel.add(filterLabel,         labelConstraints);
-        filterPanel.add(filterTextField,     textFieldConstraints);
+        filterPanel.add(tip(filterLabel,        "nameFilterTip"),     labelConstraints);
+        filterPanel.add(tip(filterTextField,    "fileNameFilterTip"), textFieldConstraints);
 
-        filterPanel.add(jarFilterLabel,      labelConstraints);
-        filterPanel.add(jarFilterTextField,  textFieldConstraints);
+        filterPanel.add(tip(jarFilterLabel,     "jarNameFilterTip"),  labelConstraints);
+        filterPanel.add(tip(jarFilterTextField, "fileNameFilterTip"), textFieldConstraints);
 
-        filterPanel.add(warFilterLabel,      labelConstraints);
-        filterPanel.add(warFilterTextField,  textFieldConstraints);
+        filterPanel.add(tip(warFilterLabel,     "warNameFilterTip"),  labelConstraints);
+        filterPanel.add(tip(warFilterTextField, "fileNameFilterTip"), textFieldConstraints);
 
-        filterPanel.add(earFilterLabel,      labelConstraints);
-        filterPanel.add(earFilterTextField,  textFieldConstraints);
+        filterPanel.add(tip(earFilterLabel,     "earNameFilterTip"),  labelConstraints);
+        filterPanel.add(tip(earFilterTextField, "fileNameFilterTip"), textFieldConstraints);
 
-        filterPanel.add(zipFilterLabel,      labelConstraints);
-        filterPanel.add(zipFilterTextField,  textFieldConstraints);
+        filterPanel.add(tip(zipFilterLabel,     "zipNameFilterTip"),  labelConstraints);
+        filterPanel.add(tip(zipFilterTextField, "fileNameFilterTip"), textFieldConstraints);
 
 
-        JButton okButton = new JButton(GUIResources.getMessage("ok"));
+        JButton okButton = new JButton(msg("ok"));
         okButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -156,7 +155,7 @@ public class FilterDialog extends JDialog
             }
         });
 
-        JButton cancelButton = new JButton(GUIResources.getMessage("cancel"));
+        JButton cancelButton = new JButton(msg("cancel"));
         cancelButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -293,4 +292,26 @@ public class FilterDialog extends JDialog
 
         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);
+    }
 }
diff --git a/src/proguard/gui/GUIResources.java b/src/proguard/gui/GUIResources.java
index be0b712..ae52210 100644
--- a/src/proguard/gui/GUIResources.java
+++ b/src/proguard/gui/GUIResources.java
@@ -1,6 +1,6 @@
-/* $Id: GUIResources.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,7 +21,7 @@
 package proguard.gui;
 
 import java.text.MessageFormat;
-import java.util.*;
+import java.util.ResourceBundle;
 
 
 /**
@@ -31,8 +31,8 @@ import java.util.*;
  */
 class GUIResources
 {
-    private static ResourceBundle messages  = ResourceBundle.getBundle(new GUIResources().getClass().getName());
-    private static MessageFormat  formatter = new MessageFormat("");
+    private static final ResourceBundle messages  = ResourceBundle.getBundle(GUIResources.class.getName());
+    private static final MessageFormat  formatter = new MessageFormat("");
 
 
     /**
diff --git a/src/proguard/gui/GUIResources.properties b/src/proguard/gui/GUIResources.properties
index 94463fb..4acb6ac 100644
--- a/src/proguard/gui/GUIResources.properties
+++ b/src/proguard/gui/GUIResources.properties
@@ -16,33 +16,42 @@ reTraceTab      = ReTrace
 #
 # Splash text.
 #
-developed    = Developed by Eric Lafortune
-shrinking    = Shrinking
-optimization = Optimization
-obfuscation  = Obfuscation
+developed       = Developed by Eric Lafortune
+shrinking       = Shrinking
+optimization    = Optimization
+obfuscation     = Obfuscation
+preverification = Preverification
 
 #
 # Panel titles.
 #
-welcome                       = Welcome to ProGuard, version 3.9
+welcome                       = Welcome to ProGuard, version 4.1
 options                       = Options
 keepAdditional                = Keep additional classes and class members
 keepNamesAdditional           = Keep additional class names and class member names
 assumeNoSideEffectsAdditional = Assume no side effects for additional methods
 whyAreYouKeeping              = Why are you keeping
+preverificationAndTargeting   = Preverification and targeting
 consistencyAndCorrectness     = Consistency and correctness
 processingConsole             = Processing console
 reTraceSettings               = ReTrace settings
-
-mappingFile            = Mapping file
-obfuscatedStackTrace   = Obfuscated stack trace
-deobfuscatedStackTrace = De-obfuscated stack trace
+deobfuscatedStackTrace        = De-obfuscated stack trace
+
+keepAdditionalTip = \
+  If required, keep additional classes, fields, and methods as entry points.
+keepNamesAdditionalTip = \
+  If required, keep the names of additional classes, fields, and methods.
+assumeNoSideEffectsAdditionalTip = \
+  <html>Optionally specify additional methods that don't have any side effects.<br>\
+  <i>Only add entries if you know what you're doing!</i></html>
+whyAreYouKeepingTip = \
+  Ask ProGuard why it is keeping certain classes, fields, or methods.
 
 #
 # Info texts.
 #
 proGuardInfo = \
-  ProGuard is a free class file shrinker, optimizer, and obfuscator.\
+  ProGuard is a free class file shrinker, optimizer, obfuscator, and preverifier.\
   \n\n\
   With this GUI, you can create, load, modify, and save ProGuard configurations. \
   \n\
@@ -53,17 +62,13 @@ proGuardInfo = \
   \n\n\
   ProGuard and ReTrace are written and maintained by Eric Lafortune.\
   \n\n\
-  Based on class file IO code by Mark Welsh.\
-  \n\n\
   Distributed under the GNU General Public License.\
   \n\
-  Copyright (c) 1999-2007.
+  Copyright (c) 2002-2007.
 
 processingInfo = \
   You can now start processing your code, \
-  or you can run ProGuard from the command line using your saved configuration.\
-  \n\n\
-  It's always a good idea to save your configuration first.
+  or you can run ProGuard from the command line using your saved configuration.
 
 reTraceInfo = \
   If you had ProGuard write out a mapping file, \
@@ -77,14 +82,13 @@ reTraceInfo = \
 #
 programJars = Program jars, wars, ears, zips, and directories
 libraryJars = Library jars, wars, ears, zips, and directories
-outputJars  = Output jars, wars, ears, zips, and directories
 
-printSeeds                       = Print seeds
 shrink                           = Shrink
 printUsage                       = Print usage
 
 optimize                         = Optimize
 allowAccessModification          = Allow access modification
+optimizationPasses               = Optimization passes
 
 obfuscate                        = Obfuscate
 printMapping                     = Print mapping
@@ -92,62 +96,144 @@ applyMapping                     = Apply mapping
 obfuscationDictionary            = Obfuscation dictionary
 overloadAggressively             = Overload aggressively
 useUniqueClassMemberNames        = Use unique class member names
-defaultPackage                   = Default package
+flattenPackageHierarchy          = Flatten package hierarchy
+repackageClasses                 = Repackage classes
 useMixedCaseClassNames           = Use mixed-case class names
 keepAttributes                   = Keep attributes
 renameSourceFileAttribute        = Rename SourceFile attribute
+adaptResourceFileNames           = Adapt resource file names
+adaptResourceFileContents        = Adapt resource file contents
+
+preverify                        = Preverify
+microEdition                     = Micro Edition
 
 verbose                          = Verbose
-note                             = Note Class.forName invocations with variable arguments
-warn                             = Warn about missing libraries
-ignoreWarnings                   = Ignore warnings about missing libraries
+note                             = Note special or unusual input
+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
-
-#
-# Panel titles and labels for boilerplate keep options.
-#
-boilerplate_keep                = Keep
-boilerplate_applications        = Applications
-boilerplate_applets             = Applets
-boilerplate_servlets            = Servlets
-boilerplate_midlets             = Midlets
-boilerplate_xlets               = Xlets
-boilerplate_library             = Library
-
-boilerplate_also_keep           = Also keep
-boilerplate_enumerations        = Enumerations
-boilerplate_serialization_code  = Serialization code
-boilerplate_beaninfo_classes    = BeanInfo classes
-boilerplate_bean_classes        = Bean classes
-boilerplate_database_drivers    = Database drivers
-boilerplate_swing_ui_l&f        = Swing UI L&F
-boilerplate_rmi_interfaces      = RMI interfaces
-boilerplate_rmi_implementations = RMI implementations
-
-#
-# Panel titles and labels for boilerplate keep names options.
-#
-boilerplate_keep_names          = Keep names
-boilerplate_native_method_names = Native method names
-boilerplate__class_method_names = .class method names
-
-#
-# Labels for boilerplate "no side effect methods" options.
-#
-boilerplate_remove                          = Remove
-boilerplate_system_method_calls             = System method calls without side effects
-boilerplate_math_method_calls               = Math method calls without side effects
-boilerplate_number_method_calls             = Number method calls without side effects
-boilerplate_string_method_calls             = String method calls without side effects
-boilerplate_stringbuffer_method_calls       = StringBuffer method calls without side effects
-boilerplate_stringbuilder_method_calls      = StringBuilder method calls without side effects
-
-boilerplate_remove_debugging                = Remove debugging
-boilerplate_throwable_printstacktrace_calls = Throwable.printStackTrace() calls
-boilerplate_thread_dumpstack_calls          = Thread.dumpStack() calls
-boilerplate_all_logging_api_calls           = All logging API calls
-boilerplate_all_log4j_api_calls             = All Log4j API calls
+forceProcessing                  = Force processing
+target                           = Target
+targets                          = 1.0,1.1,1.2,1.3,1.4,1.5,1.6
+printSeeds                       = Print seeds
+printConfiguration               = Print configuration
+dump                             = Print class files
+
+mappingFile                      = Mapping file
+obfuscatedStackTrace             = Obfuscated stack trace
+
+programJarsTip = \
+  <html>The input jars (wars, ears, zips, directories), followed by<br>\
+  their corresponding output jars (wars, ears, zips, directories).</html>
+libraryJarsTip = \
+  <html>The library jars (wars, ears, zips, directories), on which the program jars depend.<br>\
+  The library jars are required for processing, but they are not copied to the output.</html>
+
+shrinkTip = \
+  Remove unused classes, fields, and methods from the output.
+printUsageTip = \
+  Print out the list of unused classes, fields, and methods.
+
+optimizeTip = \
+  Optimize the bytecode of the processed classes.
+allowAccessModificationTip = \
+  Allow the optimization step to modify the access modifiers of classes, fields, and methods.
+optimizationPassesTip = \
+  The number of optimization passes to be performed.
+
+obfuscateTip = \
+  Obfuscate the names of the processed classes, fields, and methods.
+printMappingTip = \
+  Print out the obfuscation mapping of original names to obfuscated names.
+applyMappingTip = \
+  Apply the given mapping of original names to obfuscated names.
+obfuscationDictionaryTip = \
+  Use the words in the given file for obfuscating field names and method names.
+overloadAggressivelyTip = \
+  <html>Allow fields and methods to get the same obfuscated names, even if only their types or<br>\
+  return types differ. This is not allowed in the Java language, but it is allowed in bytecode.</html>
+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>
+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).
+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.\
+  <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.\
+  <li>"LocalVariable*Table" can be useful for debugging.\
+  <li>"Sourcefile,LineNumberTable" are necessary for generating stack traces.\
+  <li>"*Annotations*" is necessary for preserving annotations.\
+  </ul>\
+  The wildcard <code>*</code> and the negator <code>!</code> are allowed.</html>
+renameSourceFileAttributeTip = \
+  <html>Put the given string in the "SourceFile" attribute of the processed class files.<br>\
+  It will appear as the file name of the classes in stack traces.</html>
+sourceFileAttributeTip = \
+  The replacement "SourceFile" string.
+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>\
+  on the obfuscated names of the processed classes.</html>
+fileNameFilterTip = \
+  <html>A filter on file 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></html>
+
+preverifyTip = \
+  Preverify the processed classes, for Java Micro Edition or for Java 6.
+microEditionTip = \
+  Target Java Micro Edition.
+
+verboseTip = \
+  Print out verbose messages while processing.
+noteTip = \
+  Print out notes about special or unusual input.
+warnTip = \
+  <html>Print out warnings about possibly erronous input.<br>\
+  <i>Only unset this option if you know what you're doing!</i></html>
+ignoreWarningsTip = \
+  <html>Ignore any warnings about possibly erronous input.<br>\
+  <i>Only set this option if you know what you're doing!</i></html>
+skipNonPublicLibraryClassesTip = \
+  <html>Skip reading non-public library classes, for efficiency.<br>\
+  You may have to unset this option if ProGuard complains about missing classes.</html>
+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>
+forceProcessingTip = \
+  Always process the input, even if the output seems up to date.
+targetTip = \
+  Target the specified version of Java.
+printSeedsTip = \
+  Print out the list of kept classes, fields, and methods.
+printConfigurationTip = \
+  Print out the configuration.
+dumpTip = \
+  Print out the internal structure of the processed class files.
+
+mappingFileTip = \
+  The file containing the mapping of original names to obfuscated names.
+obfuscatedStackTraceTip = \
+  A stack trace produced by previously obfuscated code.
 
 #
 # Titles and labels corresponding to ProGuard keep options.
@@ -158,36 +244,158 @@ keep                   = Keep classes and class members
 keepClassMembers       = Keep class members only
 keepClassesWithMembers = Keep classes and class members, if members are present
 
+allowTitle = Allow
+
+allowShrinking    = Allow shrinking
+allowOptimization = Allow optimization
+allowObfuscation  = Allow obfuscation
+
+keepTitleTip = Keep the specified classes and/or their fields and methods.
+
+keepTip = \
+  <html>Keep the specified classes, fields, and methods as entry points.<br>\
+  This is the most common option.</html>
+keepClassMembersTip = \
+  Only keep the specified fields and methods as entry points.
+keepClassesWithMembersTip = \
+  <html>Keep the specified classes, fields, and methods,<br>\
+  on the condition that the fields and methods are present.</html>
+
+allowTitleTip = \
+  <html>Optionally relax keeping the specified classes, fields, and methods.<br>\
+  <i>These are advanced options.</i></html>
+
+allowShrinkingTip = \
+  Remove the specified classes, fields, and methods anyway, if they are not used.
+allowOptimizationTip = \
+  <html>Optimize the specified classes, fields, and methods as entry points anyway.<br>\
+  <i>Only set this option if you know what you're doing!</i></html>
+allowObfuscationTip = \
+  <html>Obfuscate the names of the specified classes, fields, and methods anyway.<br>\
+  <i>Only set this option if you know what you're doing!</i></html>
+
 #
 # Further keep titles and labels.
 #
-comments               = Comments
-access                 = Access
-required               = Required
-not                    = Not
-dontCare               = Don't care
-class                  = Class
-extendsImplementsClass = Extends/implements class
-classMembers           = Class members
+comments                    = Comments
+access                      = Access
+required                    = Required
+not                         = Not
+dontCare                    = Don't care
+annotation                  = Annotation
+class                       = Class
+extendsImplementsAnnotation = Extends/implements class with annotation
+extendsImplementsClass      = Extends/implements class
+classMembers                = Class members
 
 extensionsOf = Extensions of
 specificationNumber = Specification #
 
-type       = Type
-returnType = Return type
-name       = Name
-arguments  = Arguments
+fieldType     = Field type
+returnType    = Return type
+name          = Name
+argumentTypes = Argument types
+
+commentsTip = \
+  Optionally add a comment for this option in the configuration file.
+accessTip = \
+  <html>Optionally place constraints on the access modifiers of this element.<br>\
+  E.g. only match public elements.</html>
+requiredTip = \
+  The access modifier has to be set.
+notTip = \
+  The access modifier must not be set.
+dontCareTip = \
+  The access modifier is irrelevant.
+annotationTip = \
+  <html>Optionally require the given annotation to be present on this element.<br>\
+  E.g. only match elements that have an annotation <code>myPackage.MyAnnotation</code>.<br>\
+  <i>This is an advanced option.</i></html>
+classTip = \
+  The name of the class or interface.
+extendsImplementsAnnotationTip = \
+  <html>Optionally require the given annotation to be present on the<br>\
+  extended or implemented class or interface.<br>\
+  E.g. only match classes that extend a class that has an annotation<br>\
+  <code>myPackage.MyAnnotation</code>.<br>\
+  <i>This is an advanced option.</i></html>
+extendsImplementsClassTip = \
+  <html>Optionally require the class to implement or extend the given class or interface.<br>\
+  E.g. only match classes that implement an interface <code>myPackage.MyInterface</code>.</html>
+classMembersTip = \
+  <html>Optionally keep fields and methods as entry points in the matching class or classes.<br>\
+  E.g. keep all public '<code>get*</code>' methods as entry points.</html>
+
+fieldTypeTip     = The field type.
+returnTypeTip    = The method return type, if any.
+nameTip          = The name.
+argumentTypesTip = The method argument types, if any.
+
+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.\
+  </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>
+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.\
+  </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.\
+  </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.\
+  </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.\
+  </ul></html>
 
 #
 # File selection titles.
 #
 selectConfigurationFile         = Select a configuration file...
 saveConfigurationFile           = Save configuration...
-selectSeedsFile                 = Select a seeds output file...
 selectUsageFile                 = Select a usage output file...
 selectPrintMappingFile          = Select an output mapping file...
 selectApplyMappingFile          = Select an input mapping file...
 selectObfuscationDictionaryFile = Select an obfuscation dictionary...
+selectSeedsFile                 = Select a seeds output file...
+selectDumpFile                  = Select a class dump file...
 selectStackTraceFile            = Select a stack trace file...
 
 cantOpenConfigurationFile  = Can''t open the configuration file [{0}]
@@ -200,9 +408,7 @@ proExtension           = *.pro (ProGuard configurations)
 
 addJars     = Add one or more jars or directories...
 chooseJars  = Choose different jars or directories...
-enterFilter = \
-  You can enter optional filters for the file names contained in the selected entries. \
-  The filters are comma-separated lists of relative file names, supporting ?, * and ** wildcards, and ! negators. \
+enterFilter = Optionally filter the file names contained in the selected entries.
 
 filters       = Filters
 nameFilter    = File name filter
@@ -211,12 +417,23 @@ warNameFilter = War name filter
 earNameFilter = Ear name filter
 zipNameFilter = Zip name filter
 
+outputFileTip = The optional output file.
+inputFileTip  = The input file.
+
+nameFilterTip    = A filter on plain class file names and resource file names.
+jarNameFilterTip = A filter on jar file names.
+warNameFilterTip = A filter on war file names.
+earNameFilterTip = A filter on ear file names.
+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
 
@@ -242,6 +459,30 @@ loadStackTrace    = Load stack trace...
 process           = Process!
 reTrace           = ReTrace!
 
+advancedTip  = Toggle between showing basic options and advanced options.
+
+addInputTip  = Add an input jar, war, ear, zip, or directory.
+addOutputTip = Add an output jar, war, ear, zip, or directory.
+addTip       = Add an entry.
+editTip      = Edit the selected entries.
+filterTip    = Put filters on the contents of the selected entries.
+removeTip    = Remove the selected entries.
+moveUpTip    = Move the selected entries up in the list.
+moveDownTip  = Move the selected entries down in the list.
+
+moveToLibrariesTip = Move to selected entries to the libraries.
+moveToProgramTip   = Move to selected entries to the program.
+
+addFieldTip  = Add a field to the specification.
+addMethodTip = Add a method to the specification.
+
+loadConfigurationTip = Optionally load an initial configuration.
+viewConfigurationTip = View the current configuration.
+saveConfigurationTip = Save the current configuration.
+loadStackTraceTip    = Load a stack trace from a file.
+processTip           = Start processing, based on the current configuration.
+reTraceTip           = De-obfuscate the given stack trace.
+
 #
 # Progress messages and error messages.
 #
diff --git a/src/proguard/gui/KeepSpecificationsPanel.java b/src/proguard/gui/KeepSpecificationsPanel.java
new file mode 100644
index 0000000..8bfd16b
--- /dev/null
+++ b/src/proguard/gui/KeepSpecificationsPanel.java
@@ -0,0 +1,81 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import proguard.*;
+
+import javax.swing.*;
+
+/**
+ * This <code>ListPanel</code> allows the user to add, edit, move, and remove
+ * KeepSpecification entries in a list.
+ *
+ * @author Eric Lafortune
+ */
+final class KeepSpecificationsPanel extends ClassSpecificationsPanel
+{
+    private final boolean markClasses;
+    private final boolean markConditionally;
+    private final boolean allowShrinking;
+    private final boolean allowOptimization;
+    private final boolean allowObfuscation;
+
+
+    public KeepSpecificationsPanel(JFrame  owner,
+                                   boolean markClasses,
+                                   boolean markConditionally,
+                                   boolean allowShrinking,
+                                   boolean allowOptimization,
+                                   boolean allowObfuscation)
+    {
+        super(owner, true);
+
+        this.markClasses       = markClasses;
+        this.markConditionally = markConditionally;
+        this.allowShrinking    = allowShrinking;
+        this.allowOptimization = allowOptimization;
+        this.allowObfuscation  = allowObfuscation;
+    }
+
+
+    // Factory methods for ClassSpecificationsPanel.
+
+    protected ClassSpecification createClassSpecification()
+    {
+        return new KeepSpecification(markClasses,
+                                     markConditionally,
+                                     allowShrinking,
+                                     allowOptimization,
+                                     allowObfuscation);
+    }
+
+
+    protected void setClassSpecification(ClassSpecification classSpecification)
+    {
+        classSpecificationDialog.setKeepSpecification((KeepSpecification)classSpecification);
+    }
+
+
+    protected ClassSpecification getClassSpecification()
+    {
+        return classSpecificationDialog.getKeepSpecification();
+    }
+}
diff --git a/src/proguard/gui/ListPanel.java b/src/proguard/gui/ListPanel.java
index f5935aa..6b92358 100644
--- a/src/proguard/gui/ListPanel.java
+++ b/src/proguard/gui/ListPanel.java
@@ -1,4 +1,4 @@
-/* $Id: ListPanel.java,v 1.9.2.2 2007/01/18 21:31:52 eric Exp $
+/* $Id: ListPanel.java,v 1.15 2007/08/18 10:34:56 eric Exp $
  *
  * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
  *
@@ -37,8 +37,8 @@ import javax.swing.event.*;
  */
 abstract class ListPanel extends JPanel
 {
-    protected DefaultListModel listModel = new DefaultListModel();
-    protected JList            list      = new JList(listModel);
+    protected final DefaultListModel listModel = new DefaultListModel();
+    protected final JList            list      = new JList(listModel);
 
     protected int firstSelectionButton = 2;
 
@@ -81,7 +81,7 @@ abstract class ListPanel extends JPanel
 
     protected void addRemoveButton()
     {
-        JButton removeButton = new JButton(GUIResources.getMessage("remove"));
+        JButton removeButton = new JButton(msg("remove"));
         removeButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -91,13 +91,13 @@ abstract class ListPanel extends JPanel
             }
         });
 
-        addButton(removeButton);
+        addButton(tip(removeButton, "removeTip"));
     }
 
 
     protected void addUpButton()
     {
-        JButton upButton = new JButton(GUIResources.getMessage("moveUp"));
+        JButton upButton = new JButton(msg("moveUp"));
         upButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -112,13 +112,13 @@ abstract class ListPanel extends JPanel
             }
         });
 
-        addButton(upButton);
+        addButton(tip(upButton, "moveUpTip"));
     }
 
 
     protected void addDownButton()
     {
-        JButton downButton = new JButton(GUIResources.getMessage("moveDown"));
+        JButton downButton = new JButton(msg("moveDown"));
         downButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -133,20 +133,22 @@ abstract class ListPanel extends JPanel
             }
         });
 
-        addButton(downButton);
+        addButton(tip(downButton, "moveDownTip"));
     }
 
 
     /**
      * Adds a button that allows to copy or move entries to another ListPanel.
      *
-     * @param buttonText the button text.
-     * @param panel      the other ListPanel.
+     * @param buttonTextKey the button text key.
+     * @param tipKey        the tool tip key.
+     * @param panel         the other ListPanel.
      */
-    public void addCopyToPanelButton(String          buttonText,
+    public void addCopyToPanelButton(String          buttonTextKey,
+                                     String          tipKey,
                                      final ListPanel panel)
     {
-        JButton moveButton = new JButton(buttonText);
+        JButton moveButton = new JButton(msg(buttonTextKey));
         moveButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
@@ -162,11 +164,11 @@ abstract class ListPanel extends JPanel
             }
         });
 
-        addButton(moveButton);
+        addButton(tip(moveButton, tipKey));
     }
 
 
-    protected void addButton(JButton button)
+    protected void addButton(JComponent button)
     {
         GridBagConstraints buttonConstraints = new GridBagConstraints();
         buttonConstraints.gridwidth = GridBagConstraints.REMAINDER;
@@ -314,4 +316,26 @@ abstract class ListPanel extends JPanel
             getComponent(index).setEnabled(selected);
         }
     }
+
+
+    /**
+     * 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);
+    }
 }
diff --git a/src/proguard/gui/MemberSpecificationDialog.java b/src/proguard/gui/MemberSpecificationDialog.java
new file mode 100644
index 0000000..32243a7
--- /dev/null
+++ b/src/proguard/gui/MemberSpecificationDialog.java
@@ -0,0 +1,497 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.gui;
+
+import proguard.MemberSpecification;
+import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
+import proguard.util.ListUtil;
+
+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 MemberSpecificationDialog 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 boolean isField;
+
+    private final JRadioButton[] publicRadioButtons;
+    private final JRadioButton[] privateRadioButtons;
+    private final JRadioButton[] protectedRadioButtons;
+    private final JRadioButton[] staticRadioButtons;
+    private final JRadioButton[] finalRadioButtons;
+
+    private JRadioButton[] volatileRadioButtons;
+    private JRadioButton[] transientRadioButtons;
+
+    private JRadioButton[] synchronizedRadioButtons;
+    private JRadioButton[] nativeRadioButtons;
+    private JRadioButton[] abstractRadioButtons;
+    private JRadioButton[] strictRadioButtons;
+
+    private final JTextField annotationTypeTextField = new JTextField(20);
+    private final JTextField nameTextField           = new JTextField(20);
+    private final JTextField typeTextField           = new JTextField(20);
+    private final JTextField argumentTypesTextField  = new JTextField(20);
+
+    private int returnValue;
+
+
+    public MemberSpecificationDialog(JDialog owner, boolean isField)
+    {
+        super(owner, true);
+        setResizable(true);
+
+        // Create some constraints that can be reused.
+        GridBagConstraints constraints = new GridBagConstraints();
+        constraints.anchor = GridBagConstraints.WEST;
+        constraints.insets = new Insets(1, 2, 1, 2);
+
+        GridBagConstraints constraintsStretch = new GridBagConstraints();
+        constraintsStretch.fill    = GridBagConstraints.HORIZONTAL;
+        constraintsStretch.weightx = 1.0;
+        constraintsStretch.anchor  = GridBagConstraints.WEST;
+        constraintsStretch.insets  = constraints.insets;
+
+        GridBagConstraints constraintsLast = new GridBagConstraints();
+        constraintsLast.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLast.anchor    = GridBagConstraints.WEST;
+        constraintsLast.insets    = constraints.insets;
+
+        GridBagConstraints constraintsLastStretch = new GridBagConstraints();
+        constraintsLastStretch.gridwidth = GridBagConstraints.REMAINDER;
+        constraintsLastStretch.fill      = GridBagConstraints.HORIZONTAL;
+        constraintsLastStretch.weightx   = 1.0;
+        constraintsLastStretch.anchor    = GridBagConstraints.WEST;
+        constraintsLastStretch.insets    = constraints.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    = constraints.insets;
+
+        GridBagConstraints stretchPanelConstraints = new GridBagConstraints();
+        stretchPanelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        stretchPanelConstraints.fill      = GridBagConstraints.BOTH;
+        stretchPanelConstraints.weightx   = 1.0;
+        stretchPanelConstraints.weighty   = 1.0;
+        stretchPanelConstraints.anchor    = GridBagConstraints.NORTHWEST;
+        stretchPanelConstraints.insets    = constraints.insets;
+
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.CENTER;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints lastLabelConstraints = new GridBagConstraints();
+        lastLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastLabelConstraints.anchor    = GridBagConstraints.CENTER;
+        lastLabelConstraints.insets    = labelConstraints.insets;
+
+        GridBagConstraints advancedButtonConstraints = new GridBagConstraints();
+        advancedButtonConstraints.weightx = 1.0;
+        advancedButtonConstraints.weighty = 1.0;
+        advancedButtonConstraints.anchor  = GridBagConstraints.SOUTHWEST;
+        advancedButtonConstraints.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  = advancedButtonConstraints.insets;
+
+        GridBagConstraints cancelButtonConstraints = new GridBagConstraints();
+        cancelButtonConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        cancelButtonConstraints.weighty   = 1.0;
+        cancelButtonConstraints.anchor    = GridBagConstraints.SOUTHEAST;
+        cancelButtonConstraints.insets    = okButtonConstraints.insets;
+
+        GridBagLayout layout = new GridBagLayout();
+
+        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+        this.isField = isField;
+
+        // Create the access panel.
+        JPanel accessPanel = new JPanel(layout);
+        accessPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                               msg("access")));
+
+        accessPanel.add(Box.createGlue(),                                labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("required")), "requiredTip"), labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("not")),      "notTip"),      labelConstraints);
+        accessPanel.add(tip(new JLabel(msg("dontCare")), "dontCareTip"), labelConstraints);
+        accessPanel.add(Box.createGlue(),                                constraintsLastStretch);
+
+        publicRadioButtons    = addRadioButtonTriplet("Public",    accessPanel);
+        privateRadioButtons   = addRadioButtonTriplet("Private",   accessPanel);
+        protectedRadioButtons = addRadioButtonTriplet("Protected", accessPanel);
+        staticRadioButtons    = addRadioButtonTriplet("Static",    accessPanel);
+        finalRadioButtons     = addRadioButtonTriplet("Final",     accessPanel);
+
+        if (isField)
+        {
+            volatileRadioButtons  = addRadioButtonTriplet("Volatile",  accessPanel);
+            transientRadioButtons = addRadioButtonTriplet("Transient", accessPanel);
+        }
+        else
+        {
+            synchronizedRadioButtons = addRadioButtonTriplet("Synchronized", accessPanel);
+            nativeRadioButtons       = addRadioButtonTriplet("Native",       accessPanel);
+            abstractRadioButtons     = addRadioButtonTriplet("Abstract",     accessPanel);
+            strictRadioButtons       = addRadioButtonTriplet("Strict",       accessPanel);
+        }
+
+        // Create the type panel.
+        JPanel typePanel = new JPanel(layout);
+        typePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                             msg(isField ? "fieldType" :
+                                                                           "returnType")));
+
+        typePanel.add(tip(typeTextField, "typeTip"), constraintsLastStretch);
+
+        // Create the annotation type panel.
+        final JPanel annotationTypePanel = new JPanel(layout);
+        annotationTypePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                       msg("annotation")));
+
+        annotationTypePanel.add(tip(annotationTypeTextField, "classNameTip"), constraintsLastStretch);
+
+        // Create the name panel.
+        JPanel namePanel = new JPanel(layout);
+        namePanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                             msg("name")));
+
+        namePanel.add(tip(nameTextField, isField ? "fieldNameTip" :
+                                                   "methodNameTip"), constraintsLastStretch);
+
+        // Create the arguments panel.
+        JPanel argumentsPanel = new JPanel(layout);
+        argumentsPanel.setBorder(BorderFactory.createTitledBorder(etchedBorder,
+                                                                  msg("argumentTypes")));
+
+        argumentsPanel.add(tip(argumentTypesTextField, "argumentTypes2Tip"), constraintsLastStretch);
+
+        // Create the Advanced button.
+        final JButton advancedButton = new JButton(msg("basic"));
+        advancedButton.addActionListener(new ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                boolean visible = !annotationTypePanel.isVisible();
+
+                annotationTypePanel.setVisible(visible);
+
+                advancedButton.setText(msg(visible ? "basic" : "advanced"));
+
+                pack();
+            }
+        });
+        advancedButton.doClick();
+
+        // 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.
+        JPanel mainPanel = new JPanel(layout);
+        mainPanel.add(tip(accessPanel,         "accessTip"),       panelConstraints);
+        mainPanel.add(tip(annotationTypePanel, "annotationTip"),   panelConstraints);
+        mainPanel.add(tip(typePanel, isField ? "fieldTypeTip" :
+                                               "returnTypeTip"),   panelConstraints);
+        mainPanel.add(tip(namePanel,           "nameTip"),         panelConstraints);
+
+        if (!isField)
+        {
+            mainPanel.add(tip(argumentsPanel, "argumentTypesTip"), panelConstraints);
+        }
+
+        mainPanel.add(tip(advancedButton, "advancedTip"), advancedButtonConstraints);
+        mainPanel.add(okButton,                           okButtonConstraints);
+        mainPanel.add(cancelButton,                       cancelButtonConstraints);
+
+        getContentPane().add(mainPanel);
+    }
+
+
+    /**
+     * Adds a JLabel and three JRadioButton instances in a ButtonGroup to the
+     * given panel with a GridBagLayout, and returns the buttons in an array.
+     */
+    private JRadioButton[] addRadioButtonTriplet(String labelText,
+                                                 JPanel panel)
+    {
+        GridBagConstraints labelConstraints = new GridBagConstraints();
+        labelConstraints.anchor = GridBagConstraints.WEST;
+        labelConstraints.insets = new Insets(2, 10, 2, 10);
+
+        GridBagConstraints buttonConstraints = new GridBagConstraints();
+        buttonConstraints.insets = labelConstraints.insets;
+
+        GridBagConstraints lastGlueConstraints = new GridBagConstraints();
+        lastGlueConstraints.gridwidth = GridBagConstraints.REMAINDER;
+        lastGlueConstraints.weightx   = 1.0;
+
+        // Create the radio buttons.
+        JRadioButton radioButton0 = new JRadioButton();
+        JRadioButton radioButton1 = new JRadioButton();
+        JRadioButton radioButton2 = new JRadioButton();
+
+        // Put them in a button group.
+        ButtonGroup buttonGroup = new ButtonGroup();
+        buttonGroup.add(radioButton0);
+        buttonGroup.add(radioButton1);
+        buttonGroup.add(radioButton2);
+
+        // Add the label and the buttons to the panel.
+        panel.add(new JLabel(labelText), labelConstraints);
+        panel.add(radioButton0,          buttonConstraints);
+        panel.add(radioButton1,          buttonConstraints);
+        panel.add(radioButton2,          buttonConstraints);
+        panel.add(Box.createGlue(),      lastGlueConstraints);
+
+        return new JRadioButton[]
+        {
+             radioButton0,
+             radioButton1,
+             radioButton2
+        };
+    }
+
+
+    /**
+     * Sets the MemberSpecification to be represented in this dialog.
+     */
+    public void setMemberSpecification(MemberSpecification memberSpecification)
+    {
+        String annotationType = memberSpecification.annotationType;
+        String name           = memberSpecification.name;
+        String descriptor     = memberSpecification.descriptor;
+
+        // Set the class name text fields.
+        annotationTypeTextField.setText(annotationType == null ? "" : ClassUtil.externalType(annotationType));
+
+        // Set the access radio buttons.
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
+        setMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
+
+        // Set the class name text fields.
+        nameTextField.setText(name == null ? "*" : name);
+
+        if (isField)
+        {
+            typeTextField         .setText(descriptor == null ? "***" : ClassUtil.externalType(descriptor));
+        }
+        else
+        {
+            typeTextField         .setText(descriptor == null ? "***" : ClassUtil.externalMethodReturnType(descriptor));
+            argumentTypesTextField.setText(descriptor == null ? "..." : ClassUtil.externalMethodArguments(descriptor));
+        }
+    }
+
+
+    /**
+     * Returns the MemberSpecification currently represented in this dialog.
+     */
+    public MemberSpecification getMemberSpecification()
+    {
+        String annotationType = annotationTypeTextField.getText();
+        String name           = nameTextField.getText();
+        String type           = typeTextField.getText();
+        String arguments      = argumentTypesTextField.getText();
+
+        // Convert all class member specifications into the internal format.
+        annotationType =
+            annotationType.equals("") ||
+            annotationType.equals("***") ? null : ClassUtil.internalType(annotationType);
+
+        if (name.equals("") ||
+            name.equals("*"))
+        {
+            name = null;
+        }
+
+        if (isField)
+        {
+            type =
+                type.equals("") ||
+                type.equals("***") ? null : ClassUtil.internalType(type);
+        }
+        else
+        {
+            if (type.equals(""))
+            {
+                type = ClassConstants.EXTERNAL_TYPE_VOID;
+            }
+
+            type =
+                type     .equals("***") &&
+                arguments.equals("...") ? null :
+                    ClassUtil.internalMethodDescriptor(type, ListUtil.commaSeparatedList(arguments));
+        }
+
+        MemberSpecification memberSpecification =
+            new MemberSpecification(0, 0, annotationType, name, type);
+
+        // Also get the access radio button settings.
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PUBLIC,       publicRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PRIVATE,      privateRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_PROTECTED,    protectedRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STATIC,       staticRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_FINAL,        finalRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_VOLATILE,     volatileRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_TRANSIENT,    transientRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_SYNCHRONIZED, synchronizedRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_NATIVE,       nativeRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_ABSTRACT,     abstractRadioButtons);
+        getMemberSpecificationRadioButtons(memberSpecification, ClassConstants.INTERNAL_ACC_STRICT,       strictRadioButtons);
+
+        return memberSpecification;
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Sets the appropriate radio button of a given triplet, based on the access
+     * flags of the given keep option.
+     */
+    private void setMemberSpecificationRadioButtons(MemberSpecification memberSpecification,
+                                                         int            flag,
+                                                         JRadioButton[] radioButtons)
+    {
+        if (radioButtons != null)
+        {
+            int index = (memberSpecification.requiredSetAccessFlags   & flag) != 0 ? 0 :
+                        (memberSpecification.requiredUnsetAccessFlags & flag) != 0 ? 1 :
+                                                                                       2;
+            radioButtons[index].setSelected(true);
+        }
+    }
+
+
+    /**
+     * Updates the access flag of the given keep option, based on the given radio
+     * button triplet.
+     */
+    private void getMemberSpecificationRadioButtons(MemberSpecification memberSpecification,
+                                                    int                 flag,
+                                                    JRadioButton[]      radioButtons)
+    {
+        if (radioButtons != null)
+        {
+            if      (radioButtons[0].isSelected())
+            {
+                memberSpecification.requiredSetAccessFlags   |= flag;
+            }
+            else if (radioButtons[1].isSelected())
+            {
+                memberSpecification.requiredUnsetAccessFlags |= flag;
+            }
+        }
+    }
+
+
+    /**
+     * 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);
+    }
+}
diff --git a/src/proguard/gui/ClassMemberSpecificationsPanel.java b/src/proguard/gui/MemberSpecificationsPanel.java
similarity index 51%
rename from src/proguard/gui/ClassMemberSpecificationsPanel.java
rename to src/proguard/gui/MemberSpecificationsPanel.java
index 99bfe4d..73981ba 100644
--- a/src/proguard/gui/ClassMemberSpecificationsPanel.java
+++ b/src/proguard/gui/MemberSpecificationsPanel.java
@@ -1,6 +1,6 @@
-/* $Id: ClassMemberSpecificationsPanel.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,29 +21,29 @@
 package proguard.gui;
 
 import proguard.*;
-import proguard.classfile.util.ClassUtil;
 import proguard.classfile.ClassConstants;
+import proguard.classfile.util.ClassUtil;
 
-import java.awt.Component;
+import javax.swing.*;
+import java.awt.*;
 import java.awt.event.*;
 import java.util.*;
-
-import javax.swing.*;
+import java.util.List;
 
 
 /**
  * This <code>ListPanel</code> allows the user to add, edit, move, and remove
- * ClassMemberSpecification entries in a list.
+ * MemberSpecification entries in a list.
  *
  * @author Eric Lafortune
  */
-class ClassMemberSpecificationsPanel extends ListPanel
+final class MemberSpecificationsPanel extends ListPanel
 {
-    private ClassMemberSpecificationDialog fieldSpecificationDialog;
-    private ClassMemberSpecificationDialog methodSpecificationDialog;
+    private final MemberSpecificationDialog fieldSpecificationDialog;
+    private final MemberSpecificationDialog methodSpecificationDialog;
 
 
-    public ClassMemberSpecificationsPanel(JDialog owner, boolean fullKeepOptions)
+    public MemberSpecificationsPanel(JDialog owner, boolean fullKeepOptions)
     {
         super();
 
@@ -51,8 +51,8 @@ class ClassMemberSpecificationsPanel extends ListPanel
 
         list.setCellRenderer(new MyListCellRenderer());
 
-        fieldSpecificationDialog  = new ClassMemberSpecificationDialog(owner, true);
-        methodSpecificationDialog = new ClassMemberSpecificationDialog(owner, false);
+        fieldSpecificationDialog  = new MemberSpecificationDialog(owner, true);
+        methodSpecificationDialog = new MemberSpecificationDialog(owner, false);
 
         if (fullKeepOptions)
         {
@@ -70,84 +70,84 @@ class ClassMemberSpecificationsPanel extends ListPanel
 
     protected void addAddFieldButton()
     {
-        JButton addFieldButton = new JButton(GUIResources.getMessage("addField"));
+        JButton addFieldButton = new JButton(msg("addField"));
         addFieldButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
             {
-                fieldSpecificationDialog.setClassMemberSpecification(new ClassMemberSpecification());
+                fieldSpecificationDialog.setMemberSpecification(new MemberSpecification());
                 int returnValue = fieldSpecificationDialog.showDialog();
-                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                if (returnValue == MemberSpecificationDialog.APPROVE_OPTION)
                 {
                     // Add the new element.
-                    addElement(new MyClassMemberSpecificationWrapper(fieldSpecificationDialog.getClassMemberSpecification(),
+                    addElement(new MyMemberSpecificationWrapper(fieldSpecificationDialog.getMemberSpecification(),
                                                                   true));
                 }
             }
         });
 
-        addButton(addFieldButton);
+        addButton(tip(addFieldButton, "addFieldTip"));
     }
 
 
     protected void addAddMethodButton()
     {
-        JButton addMethodButton = new JButton(GUIResources.getMessage("addMethod"));
+        JButton addMethodButton = new JButton(msg("addMethod"));
         addMethodButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
             {
-                methodSpecificationDialog.setClassMemberSpecification(new ClassMemberSpecification());
+                methodSpecificationDialog.setMemberSpecification(new MemberSpecification());
                 int returnValue = methodSpecificationDialog.showDialog();
-                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                if (returnValue == MemberSpecificationDialog.APPROVE_OPTION)
                 {
                     // Add the new element.
-                    addElement(new MyClassMemberSpecificationWrapper(methodSpecificationDialog.getClassMemberSpecification(),
+                    addElement(new MyMemberSpecificationWrapper(methodSpecificationDialog.getMemberSpecification(),
                                                                   false));
                 }
             }
         });
 
-        addButton(addMethodButton);
+        addButton(tip(addMethodButton, "addMethodTip"));
     }
 
 
     protected void addEditButton()
     {
-        JButton editButton = new JButton(GUIResources.getMessage("edit"));
+        JButton editButton = new JButton(msg("edit"));
         editButton.addActionListener(new ActionListener()
         {
             public void actionPerformed(ActionEvent e)
             {
-                MyClassMemberSpecificationWrapper wrapper =
-                    (MyClassMemberSpecificationWrapper)list.getSelectedValue();
+                MyMemberSpecificationWrapper wrapper =
+                    (MyMemberSpecificationWrapper)list.getSelectedValue();
 
-                ClassMemberSpecificationDialog classMemberSpecificationDialog =
+                MemberSpecificationDialog memberSpecificationDialog =
                     wrapper.isField ?
                         fieldSpecificationDialog :
                         methodSpecificationDialog;
 
-                classMemberSpecificationDialog.setClassMemberSpecification(wrapper.classMemberSpecification);
-                int returnValue = classMemberSpecificationDialog.showDialog();
-                if (returnValue == ClassMemberSpecificationDialog.APPROVE_OPTION)
+                memberSpecificationDialog.setMemberSpecification(wrapper.memberSpecification);
+                int returnValue = memberSpecificationDialog.showDialog();
+                if (returnValue == MemberSpecificationDialog.APPROVE_OPTION)
                 {
                     // Replace the old element.
-                    wrapper.classMemberSpecification = classMemberSpecificationDialog.getClassMemberSpecification();
+                    wrapper.memberSpecification = memberSpecificationDialog.getMemberSpecification();
                     setElementAt(wrapper,
                                  list.getSelectedIndex());
                 }
             }
         });
 
-        addButton(editButton);
+        addButton(tip(editButton, "editTip"));
     }
 
 
     /**
-     * Sets the ClassMemberSpecification instances to be represented in this panel.
+     * Sets the MemberSpecification instances to be represented in this panel.
      */
-    public void setClassMemberSpecifications(List fieldSpecifications,
-                                             List methodSpecifications)
+    public void setMemberSpecifications(List fieldSpecifications,
+                                        List methodSpecifications)
     {
         listModel.clear();
 
@@ -156,8 +156,8 @@ class ClassMemberSpecificationsPanel extends ListPanel
             for (int index = 0; index < fieldSpecifications.size(); index++)
             {
                 listModel.addElement(
-                    new MyClassMemberSpecificationWrapper((ClassMemberSpecification)fieldSpecifications.get(index),
-                                                          true));
+                    new MyMemberSpecificationWrapper((MemberSpecification)fieldSpecifications.get(index),
+                                                     true));
             }
         }
 
@@ -166,8 +166,8 @@ class ClassMemberSpecificationsPanel extends ListPanel
             for (int index = 0; index < methodSpecifications.size(); index++)
             {
                 listModel.addElement(
-                    new MyClassMemberSpecificationWrapper((ClassMemberSpecification)methodSpecifications.get(index),
-                                                          false));
+                    new MyMemberSpecificationWrapper((MemberSpecification)methodSpecifications.get(index),
+                                                     false));
             }
         }
 
@@ -178,13 +178,13 @@ class ClassMemberSpecificationsPanel extends ListPanel
 
 
     /**
-     * Returns the ClassMemberSpecification instances currently represented in
+     * Returns the MemberSpecification instances currently represented in
      * this panel, referring to fields or to methods.
      *
      * @param isField specifies whether specifications referring to fields or
      *                specifications referring to methods should be returned.
      */
-    public List getClassMemberSpecifications(boolean isField)
+    public List getMemberSpecifications(boolean isField)
     {
         int size = listModel.size();
         if (size == 0)
@@ -192,28 +192,28 @@ class ClassMemberSpecificationsPanel extends ListPanel
             return null;
         }
 
-        List classMemberSpecifcations = new ArrayList(size);
+        List memberSpecifications = new ArrayList(size);
         for (int index = 0; index < size; index++)
         {
-            MyClassMemberSpecificationWrapper wrapper =
-                (MyClassMemberSpecificationWrapper)listModel.get(index);
+            MyMemberSpecificationWrapper wrapper =
+                (MyMemberSpecificationWrapper)listModel.get(index);
 
             if (wrapper.isField == isField)
             {
-                classMemberSpecifcations.add(wrapper.classMemberSpecification);
+                memberSpecifications.add(wrapper.memberSpecification);
             }
         }
 
-        return classMemberSpecifcations;
+        return memberSpecifications;
     }
 
 
     /**
-     * This ListCellRenderer renders ClassMemberSpecification objects.
+     * This ListCellRenderer renders MemberSpecification objects.
      */
     private static class MyListCellRenderer implements ListCellRenderer
     {
-        JLabel label = new JLabel();
+        private final JLabel label = new JLabel();
 
 
         // Implementations for ListCellRenderer.
@@ -224,20 +224,26 @@ class ClassMemberSpecificationsPanel extends ListPanel
                                                       boolean isSelected,
                                                       boolean cellHasFocus)
         {
-            MyClassMemberSpecificationWrapper wrapper = (MyClassMemberSpecificationWrapper)value;
+            MyMemberSpecificationWrapper wrapper = (MyMemberSpecificationWrapper)value;
 
-            ClassMemberSpecification option = wrapper.classMemberSpecification;
+            MemberSpecification option = wrapper.memberSpecification;
             String name       = option.name;
             String descriptor = option.descriptor;
 
-            if (name == null)
-            {
-                name = "*";
-            }
-
             label.setText(wrapper.isField ?
-                (descriptor == null ? "<fields>"  : ClassUtil.externalFullFieldDescription(0, name, descriptor)) :
-                (descriptor == null ? "<methods>" : ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT, 0, name, descriptor)));
+                (descriptor == null ? name == null ?
+                    "<fields>" :
+                    "***" + ' ' + name :
+                    ClassUtil.externalFullFieldDescription(0,
+                                                           name == null ? "*" : name,
+                                                           descriptor)) :
+                (descriptor == null ? name == null ?
+                    "<methods>" :
+                    "***" + ' ' + name + "(...)" :
+                    ClassUtil.externalFullMethodDescription(ClassConstants.INTERNAL_METHOD_NAME_INIT,
+                                                            0,
+                                                            name == null ? "*" : name,
+                                                            descriptor)));
 
             if (isSelected)
             {
@@ -258,18 +264,40 @@ class ClassMemberSpecificationsPanel extends ListPanel
 
 
     /**
-     * This class wraps a ClassMemberSpecification, additionally storing whether
+     * 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);
+    }
+
+
+    /**
+     * This class wraps a MemberSpecification, additionally storing whether
      * the option refers to a field or to a method.
      */
-    private static class MyClassMemberSpecificationWrapper
+    private static class MyMemberSpecificationWrapper
     {
-        public ClassMemberSpecification classMemberSpecification;
-        public boolean                  isField;
+        public MemberSpecification memberSpecification;
+        public final boolean             isField;
 
-        public MyClassMemberSpecificationWrapper(ClassMemberSpecification classMemberSpecification,
-                                                 boolean                  isField)
+        public MyMemberSpecificationWrapper(MemberSpecification memberSpecification,
+                                            boolean             isField)
         {
-            this.classMemberSpecification = classMemberSpecification;
+            this.memberSpecification = memberSpecification;
             this.isField                  = isField;
         }
     }
diff --git a/src/proguard/gui/MessageDialogRunnable.java b/src/proguard/gui/MessageDialogRunnable.java
index 6bbe842..588c748 100644
--- a/src/proguard/gui/MessageDialogRunnable.java
+++ b/src/proguard/gui/MessageDialogRunnable.java
@@ -1,6 +1,6 @@
-/* $Id: MessageDialogRunnable.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,8 @@
  */
 package proguard.gui;
 
-import java.awt.*;
-
 import javax.swing.*;
+import java.awt.*;
 
 
 /**
@@ -30,17 +29,17 @@ import javax.swing.*;
  *
  * @author Eric Lafortune
  */
-class MessageDialogRunnable implements Runnable
+final class MessageDialogRunnable implements Runnable
 {
-    private Component parentComponent;
-    private Object    message;
-    private String    title;
-    private int       messageType;
+    private final Component parentComponent;
+    private final Object    message;
+    private final String    title;
+    private final int       messageType;
 
 
     /**
      * Creates a new MessageDialogRunnable object.
-     * @see JOptionPane.showMessageDialog
+     * @see JOptionPane#showMessageDialog(Component, Object, String, int)
      */
     public static void showMessageDialog(Component parentComponent,
                                          Object    message,
@@ -56,7 +55,7 @@ class MessageDialogRunnable implements Runnable
 
     /**
      * Creates a new MessageDialogRunnable object.
-     * @see JOptionPane.showMessageDialog
+     * @see JOptionPane#showMessageDialog(Component, Object, String, int)
      */
     public MessageDialogRunnable(Component parentComponent,
                                  Object    message,
diff --git a/src/proguard/gui/ProGuardGUI.java b/src/proguard/gui/ProGuardGUI.java
index 3c1f77c..991ecf9 100644
--- a/src/proguard/gui/ProGuardGUI.java
+++ b/src/proguard/gui/ProGuardGUI.java
@@ -1,6 +1,6 @@
-/* $Id: ProGuardGUI.java,v 1.35.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -48,80 +48,101 @@ public class ProGuardGUI extends JFrame
     private static final String BOILERPLATE_CONFIGURATION = "boilerplate.pro";
     private static final String DEFAULT_CONFIGURATION     = "default.pro";
 
-    private static final String KEEP_ATTRIBUTE_DEFAULT        = "InnerClasses,SourceFile,LineNumberTable,Deprecated,Signature,*Annotation*,EnclosingMethod";
-    private static final String SOURCE_FILE_ATTRIBUTE_DEFAULT = "SourceFile";
+    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";
+    private static final String ADAPT_RESOURCE_FILE_CONTENTS_DEFAULT = "**.properties,META-INF/MANIFEST.MF";
 
     private static final Border BORDER = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
 
     static boolean systemOutRedirected;
 
-    private JFileChooser configurationChooser = new JFileChooser("");
-    private JFileChooser fileChooser          = new JFileChooser("");
-
-    private SplashPanel splashPanel;
-
-    private ClassPathPanel programPanel = new ClassPathPanel(this, true);
-    private ClassPathPanel libraryPanel = new ClassPathPanel(this, false);
-
-    private ClassSpecification[] boilerplateKeep;
-    private JCheckBox[]          boilerplateKeepCheckBoxes;
-    private JTextField[]         boilerplateKeepTextFields;
-
-    private ClassSpecificationsPanel additionalKeepPanel = new ClassSpecificationsPanel(this, true);
-
-    private ClassSpecification[] boilerplateKeepNames;
-    private JCheckBox[]          boilerplateKeepNamesCheckBoxes;
-    private JTextField[]         boilerplateKeepNamesTextFields;
-
-    private ClassSpecificationsPanel additionalKeepNamesPanel = new ClassSpecificationsPanel(this, true);
-
-    private ClassSpecification[] boilerplateNoSideEffectMethods;
-    private JCheckBox[]          boilerplateNoSideEffectMethodCheckBoxes;
-
-    private ClassSpecificationsPanel additionalNoSideEffectsPanel = new ClassSpecificationsPanel(this, false);
-
-    private ClassSpecificationsPanel whyAreYouKeepingPanel = new ClassSpecificationsPanel(this, false);
-
-    private JCheckBox shrinkCheckBox                           = new JCheckBox(msg("shrink"));
-    private JCheckBox printUsageCheckBox                       = new JCheckBox(msg("printUsage"));
-
-    private JCheckBox optimizeCheckBox                         = new JCheckBox(msg("optimize"));
-    private JCheckBox allowAccessModificationCheckBox          = new JCheckBox(msg("allowAccessModification"));
-
-    private JCheckBox obfuscateCheckBox                        = new JCheckBox(msg("obfuscate"));
-    private JCheckBox printMappingCheckBox                     = new JCheckBox(msg("printMapping"));
-    private JCheckBox applyMappingCheckBox                     = new JCheckBox(msg("applyMapping"));
-    private JCheckBox obfuscationDictionaryCheckBox            = new JCheckBox(msg("obfuscationDictionary"));
-    private JCheckBox overloadAggressivelyCheckBox             = new JCheckBox(msg("overloadAggressively"));
-    private JCheckBox useUniqueClassMemberNamesCheckBox        = new JCheckBox(msg("useUniqueClassMemberNames"));
-    private JCheckBox defaultPackageCheckBox                   = new JCheckBox(msg("defaultPackage"));
-    private JCheckBox useMixedCaseClassNamesCheckBox           = new JCheckBox(msg("useMixedCaseClassNames"));
-    private JCheckBox keepAttributesCheckBox                   = new JCheckBox(msg("keepAttributes"));
-    private JCheckBox newSourceFileAttributeCheckBox           = new JCheckBox(msg("renameSourceFileAttribute"));
-
-    private JCheckBox printSeedsCheckBox                       = new JCheckBox(msg("printSeeds"));
-    private JCheckBox verboseCheckBox                          = new JCheckBox(msg("verbose"));
-    private JCheckBox ignoreWarningsCheckBox                   = new JCheckBox(msg("ignoreWarnings"));
-    private JCheckBox warnCheckBox                             = new JCheckBox(msg("warn"));
-    private JCheckBox noteCheckBox                             = new JCheckBox(msg("note"));
-    private JCheckBox skipNonPublicLibraryClassesCheckBox      = new JCheckBox(msg("skipNonPublicLibraryClasses"));
-    private JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers"));
-
-    private JTextField printUsageTextField                     = new JTextField(40);
-    private JTextField printMappingTextField                   = new JTextField(40);
-    private JTextField applyMappingTextField                   = new JTextField(40);
-    private JTextField obfuscationDictionaryTextField          = new JTextField(40);
-    private JTextField defaultPackageTextField                 = new JTextField(40);
-    private JTextField keepAttributesTextField                 = new JTextField(40);
-    private JTextField newSourceFileAttributeTextField         = new JTextField(40);
-    private JTextField printSeedsTextField                     = new JTextField(40);
-
-    private JTextArea  consoleTextArea                         = new JTextArea(msg("processingInfo"), 3, 40);
-
-    private JTextField reTraceMappingTextField                 = new JTextField(40);
-    private JCheckBox  reTraceVerboseCheckBox                  = new JCheckBox(msg("verbose"));
-    private JTextArea  stackTraceTextArea                      = new JTextArea(3, 40);
-    private JTextArea  reTraceTextArea                         = new JTextArea(msg("reTraceInfo"), 3, 40);
+    private final JFileChooser configurationChooser = new JFileChooser("");
+    private final JFileChooser fileChooser          = new JFileChooser("");
+
+    private final SplashPanel splashPanel;
+
+    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 final KeepSpecificationsPanel additionalKeepPanel = new KeepSpecificationsPanel(this, true, false, false, false, false);
+
+    private       KeepSpecification[] boilerplateKeepNames;
+    private final JCheckBox[]         boilerplateKeepNamesCheckBoxes;
+    private final JTextField[]        boilerplateKeepNamesTextFields;
+
+    private final KeepSpecificationsPanel additionalKeepNamesPanel = new KeepSpecificationsPanel(this, true, false, true, false, false);
+
+    private       ClassSpecification[] boilerplateNoSideEffectMethods;
+    private final JCheckBox[]          boilerplateNoSideEffectMethodCheckBoxes;
+
+    private final ClassSpecificationsPanel additionalNoSideEffectsPanel = new ClassSpecificationsPanel(this, false);
+
+    private final ClassSpecificationsPanel whyAreYouKeepingPanel = new ClassSpecificationsPanel(this, false);
+
+    private final JCheckBox shrinkCheckBox     = new JCheckBox(msg("shrink"));
+    private final JCheckBox printUsageCheckBox = new JCheckBox(msg("printUsage"));
+
+    private final JCheckBox optimizeCheckBox                = new JCheckBox(msg("optimize"));
+    private final JCheckBox allowAccessModificationCheckBox = new JCheckBox(msg("allowAccessModification"));
+    private final JLabel    optimizationPassesLabel         = new JLabel(msg("optimizationPasses"));
+    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"));
+    private final JCheckBox applyMappingCheckBox              = new JCheckBox(msg("applyMapping"));
+    private final JCheckBox obfuscationDictionaryCheckBox     = new JCheckBox(msg("obfuscationDictionary"));
+    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 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 adaptResourceFileNamesCheckBox    = new JCheckBox(msg("adaptResourceFileNames"));
+    private final JCheckBox adaptResourceFileContentsCheckBox = new JCheckBox(msg("adaptResourceFileContents"));
+
+    private final JCheckBox preverifyCheckBox    = new JCheckBox(msg("preverify"));
+    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 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 skipNonPublicLibraryClassesCheckBox      = new JCheckBox(msg("skipNonPublicLibraryClasses"));
+    private final JCheckBox skipNonPublicLibraryClassMembersCheckBox = new JCheckBox(msg("skipNonPublicLibraryClassMembers"));
+    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 printMappingTextField              = new JTextField(40);
+    private final JTextField applyMappingTextField              = new JTextField(40);
+    private final JTextField obfuscationDictionaryTextField     = 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 adaptResourceFileNamesTextField    = new JTextField(40);
+    private final JTextField adaptResourceFileContentsTextField = new JTextField(40);
+    private final JTextField printSeedsTextField                = new JTextField(40);
+    private final JTextField printConfigurationTextField        = new JTextField(40);
+    private final JTextField dumpTextField                      = new JTextField(40);
+
+    private final JTextArea  consoleTextArea = new JTextArea(msg("processingInfo"), 3, 40);
+
+    private final JCheckBox  reTraceVerboseCheckBox  = new JCheckBox(msg("verbose"));
+    private final JTextField reTraceMappingTextField = new JTextField(40);
+    private final JTextArea  stackTraceTextArea      = new JTextArea(3, 40);
+    private final JTextArea  reTraceTextArea         = new JTextArea(msg("reTraceInfo"), 3, 40);
 
 
     /**
@@ -218,47 +239,42 @@ public class ProGuardGUI extends JFrame
             new ExtensionFileFilter(msg("proExtension"), new String[] { ".pro" }));
 
         // Create the opening panel.
-        Font font = new Font("sansserif", Font.BOLD, 50);
-        Color fontColor = Color.white;
-
         Sprite splash =
             new CompositeSprite(new Sprite[]
         {
+            new ColorSprite(new ConstantColor(Color.gray),
+            new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
             new TextSprite(new ConstantString("ProGuard"),
-                           new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
-                           new ConstantColor(Color.gray),
                            new ConstantInt(160),
-                           new LinearInt(-10, 120, new SmoothTiming(500, 1000))),
+                           new LinearInt(-10, 120, new SmoothTiming(500, 1000))))),
 
+            new ColorSprite(new ConstantColor(Color.white),
+            new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 45)),
             new ShadowedSprite(new ConstantInt(3),
                                new ConstantInt(3),
                                new ConstantDouble(0.4),
-                               new ConstantInt(2),
+                               new ConstantInt(1),
                                new CompositeSprite(new Sprite[]
             {
                 new TextSprite(new ConstantString(msg("shrinking")),
-                               new ConstantFont(font),
-                               new ConstantColor(fontColor),
                                new LinearInt(1000, 60, new SmoothTiming(1000, 2000)),
                                new ConstantInt(70)),
                 new TextSprite(new ConstantString(msg("optimization")),
-                               new ConstantFont(font),
-                               new ConstantColor(fontColor),
                                new LinearInt(1000, 400, new SmoothTiming(1500, 2500)),
                                new ConstantInt(60)),
                 new TextSprite(new ConstantString(msg("obfuscation")),
-                               new ConstantFont(font),
-                               new ConstantColor(fontColor),
-                               new LinearInt(1000, 350, new SmoothTiming(2000, 3000)),
+                               new LinearInt(1000, 10, new SmoothTiming(2000, 3000)),
+                               new ConstantInt(145)),
+                new TextSprite(new ConstantString(msg("preverification")),
+                               new LinearInt(1000, 350, new SmoothTiming(2500, 3500)),
                                new ConstantInt(140)),
-                new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3000, 5000)),
-                               new ConstantFont(new Font("monospaced", Font.BOLD, 20)),
-                               new ConstantColor(fontColor),
+                new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 30)),
+                new TextSprite(new TypeWriterString(msg("developed"), new LinearTiming(3500, 5500)),
                                new ConstantInt(250),
-                               new ConstantInt(170)),
-            })),
+                               new ConstantInt(200))),
+            })))),
         });
-        splashPanel = new SplashPanel(splash, 0.5, 5000L);
+        splashPanel = new SplashPanel(splash, 0.5, 5500L);
         splashPanel.setPreferredSize(new Dimension(0, 200));
 
         JTextArea welcomeTextArea = new JTextArea(msg("proGuardInfo"), 18, 50);
@@ -278,8 +294,8 @@ public class ProGuardGUI extends JFrame
         // TODO: properly clone the ClassPath objects.
         // This is awkward to implement in the generic ListPanel.addElements(...)
         // method, since the Object.clone() method is not public.
-        programPanel.addCopyToPanelButton(msg("moveToLibraries"), libraryPanel);
-        libraryPanel.addCopyToPanelButton(msg("moveToProgram"),   programPanel);
+        programPanel.addCopyToPanelButton("moveToLibraries", "moveToLibrariesTip", libraryPanel);
+        libraryPanel.addCopyToPanelButton("moveToProgram",   "moveToProgramTip",   programPanel);
 
         // Collect all buttons of these panels and make sure they are equally
         // sized.
@@ -287,14 +303,13 @@ public class ProGuardGUI extends JFrame
         panelButtons.addAll(programPanel.getButtons());
         panelButtons.addAll(libraryPanel.getButtons());
         setCommonPreferredSize(panelButtons);
-        panelButtons = null;
 
         addBorder(programPanel, "programJars" );
         addBorder(libraryPanel, "libraryJars" );
 
         JPanel inputOutputPanel = new JPanel(layout);
-        inputOutputPanel.add(programPanel, stretchPanelConstraints);
-        inputOutputPanel.add(libraryPanel, stretchPanelConstraints);
+        inputOutputPanel.add(tip(programPanel, "programJarsTip"), stretchPanelConstraints);
+        inputOutputPanel.add(tip(libraryPanel, "libraryJarsTip"), stretchPanelConstraints);
 
         // Load the boiler plate options.
         loadBoilerplateConfiguration();
@@ -309,21 +324,21 @@ public class ProGuardGUI extends JFrame
         JPanel shrinkingOptionsPanel = new JPanel(layout);
         addBorder(shrinkingOptionsPanel, "options");
 
-        shrinkingOptionsPanel.add(shrinkCheckBox,         constraintsLastStretch);
-        shrinkingOptionsPanel.add(printUsageCheckBox,     constraints);
-        shrinkingOptionsPanel.add(printUsageTextField,    constraintsStretch);
-        shrinkingOptionsPanel.add(printUsageBrowseButton, constraintsLast);
+        shrinkingOptionsPanel.add(tip(shrinkCheckBox,         "shrinkTip"),       constraintsLastStretch);
+        shrinkingOptionsPanel.add(tip(printUsageCheckBox,     "printUsageTip"),   constraints);
+        shrinkingOptionsPanel.add(tip(printUsageTextField,    "outputFileTip"),   constraintsStretch);
+        shrinkingOptionsPanel.add(tip(printUsageBrowseButton, "selectUsageFile"), constraintsLast);
 
         JPanel shrinkingPanel = new JPanel(layout);
 
         shrinkingPanel.add(shrinkingOptionsPanel, panelConstraints);
-        addClassSpecifications(boilerplateKeep,
+        addClassSpecifications(extractClassSpecifications(boilerplateKeep),
                                shrinkingPanel,
                                boilerplateKeepCheckBoxes,
                                boilerplateKeepTextFields);
 
         addBorder(additionalKeepPanel, "keepAdditional");
-        shrinkingPanel.add(additionalKeepPanel, stretchPanelConstraints);
+        shrinkingPanel.add(tip(additionalKeepPanel, "keepAdditionalTip"), stretchPanelConstraints);
 
         // Create the boiler plate keep names panels.
         boilerplateKeepNamesCheckBoxes = new JCheckBox[boilerplateKeepNames.length];
@@ -339,36 +354,42 @@ public class ProGuardGUI extends JFrame
         JPanel obfuscationOptionsPanel = new JPanel(layout);
         addBorder(obfuscationOptionsPanel, "options");
 
-        obfuscationOptionsPanel.add(obfuscateCheckBox,                 constraintsLastStretch);
-        obfuscationOptionsPanel.add(printMappingCheckBox,              constraints);
-        obfuscationOptionsPanel.add(printMappingTextField,             constraintsStretch);
-        obfuscationOptionsPanel.add(printMappingBrowseButton,          constraintsLast);
-        obfuscationOptionsPanel.add(applyMappingCheckBox,              constraints);
-        obfuscationOptionsPanel.add(applyMappingTextField,             constraintsStretch);
-        obfuscationOptionsPanel.add(applyMappingBrowseButton,          constraintsLast);
-        obfuscationOptionsPanel.add(obfuscationDictionaryCheckBox,     constraints);
-        obfuscationOptionsPanel.add(obfuscationDictionaryTextField,    constraintsStretch);
-        obfuscationOptionsPanel.add(obfucationDictionaryBrowseButton,  constraintsLast);
-        obfuscationOptionsPanel.add(overloadAggressivelyCheckBox,      constraintsLastStretch);
-        obfuscationOptionsPanel.add(useUniqueClassMemberNamesCheckBox, constraintsLastStretch);
-        obfuscationOptionsPanel.add(defaultPackageCheckBox,            constraints);
-        obfuscationOptionsPanel.add(defaultPackageTextField,           constraintsLastStretch);
-        obfuscationOptionsPanel.add(useMixedCaseClassNamesCheckBox,    constraintsLastStretch);
-        obfuscationOptionsPanel.add(keepAttributesCheckBox,            constraints);
-        obfuscationOptionsPanel.add(keepAttributesTextField,           constraintsLastStretch);
-        obfuscationOptionsPanel.add(newSourceFileAttributeCheckBox,    constraints);
-        obfuscationOptionsPanel.add(newSourceFileAttributeTextField,   constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(obfuscateCheckBox,                  "obfuscateTip"),                    constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(printMappingCheckBox,               "printMappingTip"),                 constraints);
+        obfuscationOptionsPanel.add(tip(printMappingTextField,              "outputFileTip"),                   constraintsStretch);
+        obfuscationOptionsPanel.add(tip(printMappingBrowseButton,           "selectPrintMappingFile"),          constraintsLast);
+        obfuscationOptionsPanel.add(tip(applyMappingCheckBox,               "applyMappingTip"),                 constraints);
+        obfuscationOptionsPanel.add(tip(applyMappingTextField,              "inputFileTip"),                    constraintsStretch);
+        obfuscationOptionsPanel.add(tip(applyMappingBrowseButton,           "selectApplyMappingFile"),          constraintsLast);
+        obfuscationOptionsPanel.add(tip(obfuscationDictionaryCheckBox,      "obfuscationDictionaryTip"),        constraints);
+        obfuscationOptionsPanel.add(tip(obfuscationDictionaryTextField,     "inputFileTip"),                    constraintsStretch);
+        obfuscationOptionsPanel.add(tip(obfucationDictionaryBrowseButton,   "selectObfuscationDictionaryFile"), constraintsLast);
+        obfuscationOptionsPanel.add(tip(overloadAggressivelyCheckBox,       "overloadAggressivelyTip"),         constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(useUniqueClassMemberNamesCheckBox,  "useUniqueClassMemberNamesTip"),    constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(useMixedCaseClassNamesCheckBox,     "useMixedCaseClassNamesTip"),       constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(flattenPackageHierarchyCheckBox,    "flattenPackageHierarchyTip"),      constraints);
+        obfuscationOptionsPanel.add(tip(flattenPackageHierarchyTextField,   "packageTip"),                      constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(repackageClassesCheckBox,           "repackageClassesTip"),             constraints);
+        obfuscationOptionsPanel.add(tip(repackageClassesTextField,          "packageTip"),                      constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(keepAttributesCheckBox,             "keepAttributesTip"),               constraints);
+        obfuscationOptionsPanel.add(tip(keepAttributesTextField,            "attributesTip"),                   constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(newSourceFileAttributeCheckBox,     "renameSourceFileAttributeTip"),    constraints);
+        obfuscationOptionsPanel.add(tip(newSourceFileAttributeTextField,    "sourceFileAttributeTip"),          constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(adaptResourceFileNamesCheckBox,     "adaptResourceFileNamesTip"),       constraints);
+        obfuscationOptionsPanel.add(tip(adaptResourceFileNamesTextField,    "fileNameFilterTip"),               constraintsLastStretch);
+        obfuscationOptionsPanel.add(tip(adaptResourceFileContentsCheckBox,  "adaptResourceFileContentsTip"),    constraints);
+        obfuscationOptionsPanel.add(tip(adaptResourceFileContentsTextField, "fileNameFilterTip"),               constraintsLastStretch);
 
         JPanel obfuscationPanel = new JPanel(layout);
 
         obfuscationPanel.add(obfuscationOptionsPanel, panelConstraints);
-        addClassSpecifications(boilerplateKeepNames,
+        addClassSpecifications(extractClassSpecifications(boilerplateKeepNames),
                                obfuscationPanel,
                                boilerplateKeepNamesCheckBoxes,
                                boilerplateKeepNamesTextFields);
 
         addBorder(additionalKeepNamesPanel, "keepNamesAdditional");
-        obfuscationPanel.add(additionalKeepNamesPanel, stretchPanelConstraints);
+        obfuscationPanel.add(tip(additionalKeepNamesPanel, "keepNamesAdditionalTip"), stretchPanelConstraints);
 
         // Create the boiler plate "no side effect methods" panels.
         boilerplateNoSideEffectMethodCheckBoxes = new JCheckBox[boilerplateNoSideEffectMethods.length];
@@ -376,8 +397,10 @@ public class ProGuardGUI extends JFrame
         JPanel optimizationOptionsPanel = new JPanel(layout);
         addBorder(optimizationOptionsPanel, "options");
 
-        optimizationOptionsPanel.add(optimizeCheckBox,                constraintsLastStretch);
-        optimizationOptionsPanel.add(allowAccessModificationCheckBox, constraintsLastStretch);
+        optimizationOptionsPanel.add(tip(optimizeCheckBox,                "optimizeTip"),                constraintsLastStretch);
+        optimizationOptionsPanel.add(tip(allowAccessModificationCheckBox, "allowAccessModificationTip"), constraintsLastStretch);
+        optimizationOptionsPanel.add(tip(optimizationPassesLabel,         "optimizationPassesTip"),      constraints);
+        optimizationOptionsPanel.add(tip(optimizationPassesSpinner,       "optimizationPassesTip"),      constraintsLast);
 
         JPanel optimizationPanel = new JPanel(layout);
 
@@ -388,24 +411,48 @@ public class ProGuardGUI extends JFrame
                                null);
 
         addBorder(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditional");
-        optimizationPanel.add(additionalNoSideEffectsPanel, stretchPanelConstraints);
+        optimizationPanel.add(tip(additionalNoSideEffectsPanel, "assumeNoSideEffectsAdditionalTip"), stretchPanelConstraints);
 
         // Create the options panel.
-        JButton printSeedsBrowseButton = createBrowseButton(printSeedsTextField,
-                                                            msg("selectSeedsFile"));
+        JPanel preverificationOptionsPanel = new JPanel(layout);
+        addBorder(preverificationOptionsPanel, "preverificationAndTargeting");
+
+        preverificationOptionsPanel.add(tip(preverifyCheckBox,    "preverifyTip"),    constraintsLastStretch);
+        preverificationOptionsPanel.add(tip(microEditionCheckBox, "microEditionTip"), constraintsLastStretch);
+        preverificationOptionsPanel.add(tip(targetCheckBox,       "targetTip"),       constraints);
+        preverificationOptionsPanel.add(tip(targetComboBox,       "targetTip"),       constraintsLast);
+
+        JButton printSeedsBrowseButton =
+            createBrowseButton(printSeedsTextField, msg("selectSeedsFile"));
+
+        JButton printConfigurationBrowseButton =
+            createBrowseButton(printConfigurationTextField, msg( "selectConfigurationFile"));
+
+        JButton dumpBrowseButton =
+            createBrowseButton(dumpTextField, msg("selectDumpFile"));
+
+        // Select the most recent target by default.
+        targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1);
 
         JPanel consistencyPanel = new JPanel(layout);
         addBorder(consistencyPanel, "consistencyAndCorrectness");
 
-        consistencyPanel.add(printSeedsCheckBox,                       constraints);
-        consistencyPanel.add(printSeedsTextField,                      constraintsStretch);
-        consistencyPanel.add(printSeedsBrowseButton,                   constraintsLast);
-        consistencyPanel.add(verboseCheckBox,                          constraintsLastStretch);
-        consistencyPanel.add(noteCheckBox,                             constraintsLastStretch);
-        consistencyPanel.add(warnCheckBox,                             constraintsLastStretch);
-        consistencyPanel.add(ignoreWarningsCheckBox,                   constraintsLastStretch);
-        consistencyPanel.add(skipNonPublicLibraryClassesCheckBox,      constraintsLastStretch);
-        consistencyPanel.add(skipNonPublicLibraryClassMembersCheckBox, constraintsLastStretch);
+        consistencyPanel.add(tip(verboseCheckBox,                          "verboseTip"),                          constraintsLastStretch);
+        consistencyPanel.add(tip(noteCheckBox,                             "noteTip"),                             constraintsLastStretch);
+        consistencyPanel.add(tip(warnCheckBox,                             "warnTip"),                             constraintsLastStretch);
+        consistencyPanel.add(tip(ignoreWarningsCheckBox,                   "ignoreWarningsTip"),                   constraintsLastStretch);
+        consistencyPanel.add(tip(skipNonPublicLibraryClassesCheckBox,      "skipNonPublicLibraryClassesTip"),      constraintsLastStretch);
+        consistencyPanel.add(tip(skipNonPublicLibraryClassMembersCheckBox, "skipNonPublicLibraryClassMembersTip"), constraintsLastStretch);
+        consistencyPanel.add(tip(forceProcessingCheckBox,                  "forceProcessingTip"),                  constraintsLastStretch);
+        consistencyPanel.add(tip(printSeedsCheckBox,                       "printSeedsTip"),                       constraints);
+        consistencyPanel.add(tip(printSeedsTextField,                      "outputFileTip"),                       constraintsStretch);
+        consistencyPanel.add(tip(printSeedsBrowseButton,                   "selectSeedsFile"),                     constraintsLast);
+        consistencyPanel.add(tip(printConfigurationCheckBox,               "printConfigurationTip"),               constraints);
+        consistencyPanel.add(tip(printConfigurationTextField,              "outputFileTip"),                       constraintsStretch);
+        consistencyPanel.add(tip(printConfigurationBrowseButton,           "selectConfigurationFile"),             constraintsLast);
+        consistencyPanel.add(tip(dumpCheckBox,                             "dumpTip"),                             constraints);
+        consistencyPanel.add(tip(dumpTextField,                            "outputFileTip"),                       constraintsStretch);
+        consistencyPanel.add(tip(dumpBrowseButton,                         "selectDumpFile"),                      constraintsLast);
 
         // Collect all components that are followed by text fields and make
         // sure they are equally sized. That way the text fields start at the
@@ -413,16 +460,18 @@ public class ProGuardGUI extends JFrame
         setCommonPreferredSize(Arrays.asList(new JComponent[] {
             printMappingCheckBox,
             applyMappingCheckBox,
-            defaultPackageCheckBox,
+            flattenPackageHierarchyCheckBox,
+            repackageClassesCheckBox,
             newSourceFileAttributeCheckBox,
         }));
 
         JPanel optionsPanel = new JPanel(layout);
 
-        optionsPanel.add(consistencyPanel,  panelConstraints);
+        optionsPanel.add(preverificationOptionsPanel, panelConstraints);
+        optionsPanel.add(consistencyPanel,            panelConstraints);
 
         addBorder(whyAreYouKeepingPanel, "whyAreYouKeeping");
-        optionsPanel.add(whyAreYouKeepingPanel, stretchPanelConstraints);
+        optionsPanel.add(tip(whyAreYouKeepingPanel, "whyAreYouKeepingTip"), stretchPanelConstraints);
 
         // Create the process panel.
         consoleTextArea.setOpaque(false);
@@ -459,10 +508,10 @@ public class ProGuardGUI extends JFrame
         JLabel reTraceMappingLabel = new JLabel(msg("mappingFile"));
         reTraceMappingLabel.setForeground(reTraceVerboseCheckBox.getForeground());
 
-        reTraceSettingsPanel.add(reTraceMappingLabel,        constraints);
-        reTraceSettingsPanel.add(reTraceMappingTextField,    constraintsStretch);
-        reTraceSettingsPanel.add(reTraceMappingBrowseButton, constraintsLast);
-        reTraceSettingsPanel.add(reTraceVerboseCheckBox,     constraintsLastStretch);
+        reTraceSettingsPanel.add(tip(reTraceVerboseCheckBox,     "verboseTip"),             constraintsLastStretch);
+        reTraceSettingsPanel.add(tip(reTraceMappingLabel,        "mappingFileTip"),         constraints);
+        reTraceSettingsPanel.add(tip(reTraceMappingTextField,    "inputFileTip"),           constraintsStretch);
+        reTraceSettingsPanel.add(tip(reTraceMappingBrowseButton, "selectApplyMappingFile"), constraintsLast);
 
         stackTraceTextArea.setOpaque(true);
         stackTraceTextArea.setEditable(true);
@@ -480,9 +529,9 @@ public class ProGuardGUI extends JFrame
         addBorder(reTraceScrollPane, "deobfuscatedStackTrace");
 
         JPanel reTracePanel = new JPanel(layout);
-        reTracePanel.add(reTraceSettingsPanel, panelConstraints);
-        reTracePanel.add(stackTraceScrollPane, panelConstraints);
-        reTracePanel.add(reTraceScrollPane,    stretchPanelConstraints);
+        reTracePanel.add(reTraceSettingsPanel,                                 panelConstraints);
+        reTracePanel.add(tip(stackTraceScrollPane, "obfuscatedStackTraceTip"), panelConstraints);
+        reTracePanel.add(reTraceScrollPane,                                    stretchPanelConstraints);
 
         // Create the load button.
         JButton loadStackTraceButton = new JButton(msg("loadStackTrace"));
@@ -505,9 +554,9 @@ public class ProGuardGUI extends JFrame
             this.getClass().getResource(TITLE_IMAGE_FILE)));
 
         // Add the bottom buttons to each panel.
-        proGuardPanel     .add(Box.createGlue(),           glueConstraints);
-        proGuardPanel     .add(loadButton,                 bottomButtonConstraints);
-        proGuardPanel     .add(createNextButton(tabs),     lastBottomButtonConstraints);
+        proGuardPanel     .add(Box.createGlue(),                                      glueConstraints);
+        proGuardPanel     .add(tip(loadButton,             "loadConfigurationTip"),   bottomButtonConstraints);
+        proGuardPanel     .add(createNextButton(tabs),                                lastBottomButtonConstraints);
 
         inputOutputPanel  .add(Box.createGlue(),           glueConstraints);
         inputOutputPanel  .add(createPreviousButton(tabs), bottomButtonConstraints);
@@ -529,15 +578,15 @@ public class ProGuardGUI extends JFrame
         optionsPanel      .add(createPreviousButton(tabs), bottomButtonConstraints);
         optionsPanel      .add(createNextButton(tabs),     lastBottomButtonConstraints);
 
-        processPanel      .add(Box.createGlue(),           glueConstraints);
-        processPanel      .add(createPreviousButton(tabs), bottomButtonConstraints);
-        processPanel      .add(viewButton,                 bottomButtonConstraints);
-        processPanel      .add(saveButton,                 bottomButtonConstraints);
-        processPanel      .add(processButton,              lastBottomButtonConstraints);
+        processPanel      .add(Box.createGlue(),                                      glueConstraints);
+        processPanel      .add(createPreviousButton(tabs),                            bottomButtonConstraints);
+        processPanel      .add(tip(viewButton,             "viewConfigurationTip"),   bottomButtonConstraints);
+        processPanel      .add(tip(saveButton,             "saveConfigurationTip"),   bottomButtonConstraints);
+        processPanel      .add(tip(processButton,          "processTip"),             lastBottomButtonConstraints);
 
-        reTracePanel      .add(Box.createGlue(),           glueConstraints);
-        reTracePanel      .add(loadStackTraceButton,       bottomButtonConstraints);
-        reTracePanel      .add(reTraceButton,              lastBottomButtonConstraints);
+        reTracePanel      .add(Box.createGlue(),                                      glueConstraints);
+        reTracePanel      .add(tip(loadStackTraceButton,   "loadStackTraceTip"),      bottomButtonConstraints);
+        reTracePanel      .add(tip(reTraceButton,          "reTraceTip"),             lastBottomButtonConstraints);
 
         // Initialize the GUI settings to reasonable defaults.
         loadConfiguration(this.getClass().getResource(DEFAULT_CONFIGURATION));
@@ -560,7 +609,7 @@ public class ProGuardGUI extends JFrame
 
 
     /**
-     * Loads the boilerplate keep class file options from the boilerplate file
+     * Loads the boilerplate keep class options from the boilerplate file
      * into the boilerplate array.
      */
     private void loadBoilerplateConfiguration()
@@ -577,12 +626,12 @@ public class ProGuardGUI extends JFrame
                 parser.parse(configuration);
 
                 // We're interested in the keep options.
-                boilerplateKeep = new ClassSpecification[configuration.keep.size()];
-                configuration.keep.toArray(boilerplateKeep);
+                boilerplateKeep =
+                    extractKeepSpecifications(configuration.keep, false, false);
 
-                // We're interested in the keep names options.
-                boilerplateKeepNames = new ClassSpecification[configuration.keepNames.size()];
-                configuration.keepNames.toArray(boilerplateKeepNames);
+                // We're interested in the keep options.
+                boilerplateKeepNames =
+                    extractKeepSpecifications(configuration.keep, true, false);
 
                 // We're interested in the side effects options.
                 boilerplateNoSideEffectMethods = new ClassSpecification[configuration.assumeNoSideEffects.size()];
@@ -601,6 +650,50 @@ public class ProGuardGUI extends JFrame
 
 
     /**
+     * Returns an array containing the ClassSpecifications instances with
+     * matching flags.
+     */
+    private KeepSpecification[] 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)
+            {
+                 matches.add(keepSpecification);
+            }
+        }
+
+        KeepSpecification[] matchingKeepSpecifications = new KeepSpecification[matches.size()];
+        matches.toArray(matchingKeepSpecifications);
+
+        return matchingKeepSpecifications;
+    }
+
+
+    /**
+     * Returns an array containing the ClassSpecification instances of the
+     * given array of KeepSpecification instances.
+     */
+    private ClassSpecification[] extractClassSpecifications(KeepSpecification[] keepSpecifications)
+    {
+        ClassSpecification[] classSpecifications = new ClassSpecification[keepSpecifications.length];
+
+        for (int index = 0; index < classSpecifications.length; index++)
+        {
+            classSpecifications[index] = keepSpecifications[index];
+        }
+
+        return classSpecifications;
+    }
+
+
+    /**
      * Creates a panel with the given boiler plate class specifications.
      */
     private void addClassSpecifications(ClassSpecification[] boilerplateClassSpecifications,
@@ -633,30 +726,28 @@ public class ProGuardGUI extends JFrame
         JPanel keepSubpanel  = null;
         for (int index = 0; index < boilerplateClassSpecifications.length; index++)
         {
-            ClassSpecification classSpecification =
-                boilerplateClassSpecifications[index];
-
             // The panel structure is derived from the comments.
-            String comments = classSpecification.comments;
-            int dashIndex = comments.indexOf('-');
-            int periodIndex = comments.indexOf('.', dashIndex);
-            String panelName = comments.substring(0, dashIndex).trim();
-            String optionName = comments.substring(dashIndex + 1, periodIndex).trim();
-            if (!panelName.equals(lastPanelName))
+            String comments    = boilerplateClassSpecifications[index].comments;
+            int    dashIndex   = comments.indexOf('-');
+            int    periodIndex = comments.indexOf('.', dashIndex);
+            String panelName   = comments.substring(0, dashIndex).trim();
+            String optionName  = comments.substring(dashIndex + 1, periodIndex).replace('_', '.').trim();
+            String toolTip     = comments.substring(periodIndex + 1);
+            if (keepSubpanel == null || !panelName.equals(lastPanelName))
             {
                 // Create a new keep subpanel and add it.
                 keepSubpanel = new JPanel(layout);
-                String titleKey = "boilerplate_" + panelName.toLowerCase().replace(' ', '_');
-                addBorder(keepSubpanel, titleKey);
+                keepSubpanel.setBorder(BorderFactory.createTitledBorder(BORDER, panelName));
                 classSpecificationsPanel.add(keepSubpanel, panelConstraints);
 
                 lastPanelName = panelName;
             }
 
             // Add the check box to the subpanel.
-            String messageKey = "boilerplate_" + optionName.toLowerCase().replace(' ', '_');
-            boilerplateCheckBoxes[index] = new JCheckBox(msg(messageKey));
-            keepSubpanel.add(boilerplateCheckBoxes[index],
+            JCheckBox boilerplateCheckBox = new JCheckBox(optionName);
+            boilerplateCheckBox.setToolTipText(toolTip);
+            boilerplateCheckBoxes[index] = boilerplateCheckBox;
+            keepSubpanel.add(boilerplateCheckBox,
                              boilerplateTextFields != null ?
                                  constraints :
                                  constraintsLastStretch);
@@ -665,7 +756,7 @@ public class ProGuardGUI extends JFrame
             {
                 // Add the text field to the subpanel.
                 boilerplateTextFields[index] = new JTextField(40);
-                keepSubpanel.add(boilerplateTextFields[index], constraintsLastStretch);
+                keepSubpanel.add(tip(boilerplateTextFields[index], "classNamesTip"), constraintsLastStretch);
             }
         }
     }
@@ -789,24 +880,20 @@ public class ProGuardGUI extends JFrame
         for (int index = 0; index < boilerplateKeep.length; index++)
         {
             String classNames =
-                findMatchingClassSpecifications(boilerplateKeep[index],
-                                                configuration.keep);
+                findMatchingKeepSpecifications(boilerplateKeep[index],
+                                               configuration.keep);
 
             boilerplateKeepCheckBoxes[index].setSelected(classNames != null);
             boilerplateKeepTextFields[index].setText(classNames == null ? "*" : classNames);
         }
 
-        // Set up the additional keep options. Note that the matched boilerplate
-        // options have been removed from the list.
-        additionalKeepPanel.setClassSpecifications(configuration.keep);
-
 
         // Set up the boilerplate keep names options.
         for (int index = 0; index < boilerplateKeepNames.length; index++)
         {
             String classNames =
-                findMatchingClassSpecifications(boilerplateKeepNames[index],
-                                                configuration.keepNames);
+                findMatchingKeepSpecifications(boilerplateKeepNames[index],
+                                               configuration.keep);
 
             boilerplateKeepNamesCheckBoxes[index].setSelected(classNames != null);
             boilerplateKeepNamesTextFields[index].setText(classNames == null ? "*" : classNames);
@@ -814,7 +901,13 @@ public class ProGuardGUI extends JFrame
 
         // Set up the additional keep options. Note that the matched boilerplate
         // options have been removed from the list.
-        additionalKeepNamesPanel.setClassSpecifications(configuration.keepNames);
+        additionalKeepPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep,
+                                                                              false));
+
+        // Set up the additional keep options. Note that the matched boilerplate
+        // options have been removed from the list.
+        additionalKeepNamesPanel.setClassSpecifications(filteredKeepSpecifications(configuration.keep,
+                                                                                   true));
 
 
         // Set up the boilerplate "no side effect methods" options.
@@ -836,38 +929,63 @@ 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);
 
         obfuscateCheckBox                       .setSelected(configuration.obfuscate);
-        printMappingCheckBox                    .setSelected(configuration.printMapping != null);
-        applyMappingCheckBox                    .setSelected(configuration.applyMapping != null);
-        obfuscationDictionaryCheckBox           .setSelected(configuration.obfuscationDictionary != null);
+        printMappingCheckBox                    .setSelected(configuration.printMapping              != null);
+        applyMappingCheckBox                    .setSelected(configuration.applyMapping              != null);
+        obfuscationDictionaryCheckBox           .setSelected(configuration.obfuscationDictionary     != null);
         overloadAggressivelyCheckBox            .setSelected(configuration.overloadAggressively);
         useUniqueClassMemberNamesCheckBox       .setSelected(configuration.useUniqueClassMemberNames);
-        defaultPackageCheckBox                  .setSelected(configuration.defaultPackage != null);
         useMixedCaseClassNamesCheckBox          .setSelected(configuration.useMixedCaseClassNames);
-        keepAttributesCheckBox                  .setSelected(configuration.keepAttributes != null);
-        newSourceFileAttributeCheckBox          .setSelected(configuration.newSourceFileAttribute != null);
+        flattenPackageHierarchyCheckBox         .setSelected(configuration.flattenPackageHierarchy   != null);
+        repackageClassesCheckBox                .setSelected(configuration.repackageClasses          != null);
+        keepAttributesCheckBox                  .setSelected(configuration.keepAttributes            != null);
+        newSourceFileAttributeCheckBox          .setSelected(configuration.newSourceFileAttribute    != null);
+        adaptResourceFileNamesCheckBox          .setSelected(configuration.adaptResourceFileNames    != null);
+        adaptResourceFileContentsCheckBox       .setSelected(configuration.adaptResourceFileContents != null);
+
+        preverifyCheckBox                       .setSelected(configuration.preverify);
+        microEditionCheckBox                    .setSelected(configuration.microEdition);
+        targetCheckBox                          .setSelected(configuration.targetClassVersion != 0);
 
-        printSeedsCheckBox                      .setSelected(configuration.printSeeds != null);
         verboseCheckBox                         .setSelected(configuration.verbose);
         noteCheckBox                            .setSelected(configuration.note);
         warnCheckBox                            .setSelected(configuration.warn);
         ignoreWarningsCheckBox                  .setSelected(configuration.ignoreWarnings);
         skipNonPublicLibraryClassesCheckBox     .setSelected(configuration.skipNonPublicLibraryClasses);
         skipNonPublicLibraryClassMembersCheckBox.setSelected(configuration.skipNonPublicLibraryClassMembers);
+        forceProcessingCheckBox                 .setSelected(configuration.lastModified == Long.MAX_VALUE);
+        printSeedsCheckBox                      .setSelected(configuration.printSeeds              != null);
+        printConfigurationCheckBox              .setSelected(configuration.printConfiguration      != null);
+        dumpCheckBox                            .setSelected(configuration.dump                    != null);
 
         printUsageTextField                     .setText(fileName(configuration.printUsage));
         printMappingTextField                   .setText(fileName(configuration.printMapping));
         applyMappingTextField                   .setText(fileName(configuration.applyMapping));
         obfuscationDictionaryTextField          .setText(fileName(configuration.obfuscationDictionary));
-        defaultPackageTextField                 .setText(configuration.defaultPackage);
-        keepAttributesTextField                 .setText(configuration.keepAttributes         == null ? KEEP_ATTRIBUTE_DEFAULT : ListUtil.commaSeparatedString(configuration.keepAttributes));
-        newSourceFileAttributeTextField         .setText(configuration.newSourceFileAttribute == null ? SOURCE_FILE_ATTRIBUTE_DEFAULT : configuration.newSourceFileAttribute);
+        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);
+        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));
         printSeedsTextField                     .setText(fileName(configuration.printSeeds));
+        printConfigurationTextField             .setText(fileName(configuration.printConfiguration));
+        dumpTextField                           .setText(fileName(configuration.dump));
+
+        if (configuration.targetClassVersion != 0)
+        {
+            targetComboBox.setSelectedItem(ClassUtil.externalClassVersion(configuration.targetClassVersion));
+        }
+        else
+        {
+            targetComboBox.setSelectedIndex(targetComboBox.getItemCount() - 1);
+        }
 
         if (configuration.printMapping != null)
         {
@@ -887,18 +1005,8 @@ public class ProGuardGUI extends JFrame
         configuration.programJars = programPanel.getClassPath();
         configuration.libraryJars = libraryPanel.getClassPath();
 
-        // Collect the boilerplate keep options.
         List keep = new ArrayList();
 
-        for (int index = 0; index < boilerplateKeep.length; index++)
-        {
-            if (boilerplateKeepCheckBoxes[index].isSelected())
-            {
-                keep.add(classSpecification(boilerplateKeep[index],
-                                            boilerplateKeepTextFields[index].getText()));
-            }
-        }
-
         // Collect the additional keep options.
         List additionalKeep = additionalKeepPanel.getClassSpecifications();
         if (additionalKeep != null)
@@ -906,36 +1014,37 @@ public class ProGuardGUI extends JFrame
             keep.addAll(additionalKeep);
         }
 
-        // Put the list of keep options in the configuration.
-        if (keep.size() > 0)
+        // Collect the additional keep names options.
+        List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications();
+        if (additionalKeepNames != null)
         {
-            configuration.keep = keep;
+            keep.addAll(additionalKeepNames);
         }
 
+        // Collect the boilerplate keep options.
+        for (int index = 0; index < boilerplateKeep.length; index++)
+        {
+            if (boilerplateKeepCheckBoxes[index].isSelected())
+            {
+                keep.add(classSpecification(boilerplateKeep[index],
+                                            boilerplateKeepTextFields[index].getText()));
+            }
+        }
 
         // Collect the boilerplate keep names options.
-        List keepNames = new ArrayList();
-
         for (int index = 0; index < boilerplateKeepNames.length; index++)
         {
             if (boilerplateKeepNamesCheckBoxes[index].isSelected())
             {
-                keepNames.add(classSpecification(boilerplateKeepNames[index],
-                                                 boilerplateKeepNamesTextFields[index].getText()));
+                keep.add(classSpecification(boilerplateKeepNames[index],
+                                            boilerplateKeepNamesTextFields[index].getText()));
             }
         }
 
-        // Collect the additional keep names options.
-        List additionalKeepNames = additionalKeepNamesPanel.getClassSpecifications();
-        if (additionalKeepNames != null)
-        {
-            keepNames.addAll(additionalKeepNames);
-        }
-
-        // Put the list of keep names options in the configuration.
-        if (keepNames.size() > 0)
+        // Put the list of keep specifications in the configuration.
+        if (keep.size() > 0)
         {
-            configuration.keepNames = keepNames;
+            configuration.keep = keep;
         }
 
 
@@ -973,26 +1082,37 @@ public class ProGuardGUI extends JFrame
         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.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.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.overloadAggressively             = overloadAggressivelyCheckBox            .isSelected();
         configuration.useUniqueClassMemberNames        = useUniqueClassMemberNamesCheckBox       .isSelected();
-        configuration.defaultPackage                   = defaultPackageCheckBox                  .isSelected() ?          defaultPackageTextField                   .getText()  : null;
         configuration.useMixedCaseClassNames           = useMixedCaseClassNamesCheckBox          .isSelected();
-        configuration.keepAttributes                   = keepAttributesCheckBox                  .isSelected() ? ListUtil.commaSeparatedList(keepAttributesTextField.getText()) : null;
-        configuration.newSourceFileAttribute           = newSourceFileAttributeCheckBox          .isSelected() ?          newSourceFileAttributeTextField           .getText()  : null;
+        configuration.flattenPackageHierarchy          = flattenPackageHierarchyCheckBox         .isSelected() ? ClassUtil.externalClassName(flattenPackageHierarchyTextField  .getText()) : null;
+        configuration.repackageClasses                 = repackageClassesCheckBox                .isSelected() ? ClassUtil.externalClassName(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.preverify                        = preverifyCheckBox                       .isSelected();
+        configuration.microEdition                     = microEditionCheckBox                    .isSelected();
+        configuration.targetClassVersion               = targetCheckBox                          .isSelected() ? ClassUtil.internalClassVersion(targetComboBox.getSelectedItem().toString()) : 0;
 
-        configuration.printSeeds                       = printSeedsCheckBox                      .isSelected() ? new File(printSeedsTextField                       .getText()) : null;
         configuration.verbose                          = verboseCheckBox                         .isSelected();
         configuration.note                             = noteCheckBox                            .isSelected();
         configuration.warn                             = warnCheckBox                            .isSelected();
         configuration.ignoreWarnings                   = ignoreWarningsCheckBox                  .isSelected();
         configuration.skipNonPublicLibraryClasses      = skipNonPublicLibraryClassesCheckBox     .isSelected();
         configuration.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembersCheckBox.isSelected();
+        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;
 
         return configuration;
     }
@@ -1027,28 +1147,52 @@ public class ProGuardGUI extends JFrame
 
 
     /**
-     * Looks in the given list for class specifications that match the given
-     * template. Returns a comma-separated string of class file names from
-     * matching class specifications, and removes the matching class
+     * Returns the subset of the given list of keep specifications, with
+     * matching shrinking flag.
+     */
+    private List filteredKeepSpecifications(List    keepSpecifications,
+                                            boolean allowShrinking)
+    {
+        List filteredKeepSpecifications = new ArrayList();
+
+        for (int index = 0; index < keepSpecifications.size(); index++)
+        {
+            KeepSpecification keepSpecification =
+                (KeepSpecification)keepSpecifications.get(index);
+
+            if (keepSpecification.allowShrinking == allowShrinking)
+            {
+                filteredKeepSpecifications.add(keepSpecification);
+            }
+        }
+
+        return filteredKeepSpecifications;
+    }
+
+
+    /**
+     * Looks in the given list for keep specifications that match the given
+     * template. Returns a comma-separated string of class names from
+     * matching keep specifications, and removes the matching keep
      * specifications as a side effect.
      */
-    private String findMatchingClassSpecifications(ClassSpecification classSpecificationTemplate,
-                                                   List                classSpecifications)
+    private String findMatchingKeepSpecifications(KeepSpecification keepSpecificationTemplate,
+                                                  List              keepSpecifications)
     {
-        if (classSpecifications == null)
+        if (keepSpecifications == null)
         {
             return null;
         }
 
         StringBuffer buffer = null;
 
-        for (int index = 0; index < classSpecifications.size(); index++)
+        for (int index = 0; index < keepSpecifications.size(); index++)
         {
-            ClassSpecification listedClassSpecification =
-                (ClassSpecification)classSpecifications.get(index);
-            String className = listedClassSpecification.className;
-            classSpecificationTemplate.className = className;
-            if (classSpecificationTemplate.equals(listedClassSpecification))
+            KeepSpecification listedKeepSpecification =
+                (KeepSpecification)keepSpecifications.get(index);
+            String className = listedKeepSpecification.className;
+            keepSpecificationTemplate.className = className;
+            if (keepSpecificationTemplate.equals(listedKeepSpecification))
             {
                 if (buffer == null)
                 {
@@ -1061,7 +1205,7 @@ public class ProGuardGUI extends JFrame
                 buffer.append(className == null ? "*" : ClassUtil.externalClassName(className));
 
                 // Remove the matching option as a side effect.
-                classSpecifications.remove(index--);
+                keepSpecifications.remove(index--);
             }
         }
 
@@ -1070,30 +1214,8 @@ public class ProGuardGUI extends JFrame
 
 
     /**
-     * Adds class specifications to the given list, based on the given template
-     * and the comma-separated list of class names to be filled in.
-     * @deprecated Add a single class specification using
-     *             #classSpecification(ClassSpecification,String).
-     */
-    private void addClassSpecifications(List               classSpecifications,
-                                        ClassSpecification classSpecificationTemplate,
-                                        String             classNamesString)
-    {
-        List classNames = ListUtil.commaSeparatedList(classNamesString);
-
-        for (int index = 0; index < classNames.size(); index++)
-        {
-            String className = (String)classNames.get(index);
-
-            // Add a modified copy of the template to the list.
-            classSpecifications.add(classSpecification(classSpecificationTemplate, className));
-        }
-    }
-
-
-    /**
-     * Returns a class specification, based on the given template and the class
-     * name to be filled in.
+     * Returns a class specification or keep specification, based on the given
+     * template and the class name to be filled in.
      */
     private ClassSpecification classSpecification(ClassSpecification classSpecificationTemplate,
                                                   String             className)
@@ -1229,15 +1351,29 @@ public class ProGuardGUI extends JFrame
     {
         try
         {
-            // Read the entire stack trace file into a buffer.
-            File file = new File(fileName);
-            byte[] buffer = new byte[(int)file.length()];
-            InputStream inputStream = new FileInputStream(file);
-            inputStream.read(buffer);
-            inputStream.close();
+            StringBuffer buffer = new StringBuffer(1024);
+
+            Reader reader = new BufferedReader(new FileReader(fileName));
+            try
+            {
+                while (true)
+                {
+                    int c = reader.read();
+                    if (c < 0)
+                    {
+                        break;
+                    }
+
+                    buffer.append(c);
+                }
+            }
+            finally
+            {
+                reader.close();
+            }
 
             // Put the stack trace in the text area.
-            stackTraceTextArea.setText(new String(buffer));
+            stackTraceTextArea.setText(buffer.toString());
         }
         catch (IOException ex)
         {
@@ -1305,16 +1441,22 @@ public class ProGuardGUI extends JFrame
 
                 try
                 {
-                    // TODO: write out relative path names and path names with system
-                    // properties.
+                    // TODO: write out relative path names and path names with system properties.
 
                     // Write the configuration.
                     ConfigurationWriter writer = new ConfigurationWriter(outputStream);
-                    writer.write(getProGuardConfiguration());
-                    writer.close();
+                    try
+                    {
+                        writer.write(getProGuardConfiguration());
+                    }
+                    finally
+                    {
+                        writer.close();
+                    }
                 }
                 catch (IOException ex)
                 {
+                    // This shouldn't happen.
                 }
 
                 // Scroll to the top of the configuration.
@@ -1412,16 +1554,29 @@ public class ProGuardGUI extends JFrame
     /**
      * Returns the file name of the given file, if any.
      */
-    private String fileName(File file)
+    private static String fileName(File file)
     {
         return file == null ? "" : file.getAbsolutePath();
     }
 
+
+    /**
+     * 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 String msg(String messageKey)
+    private static String msg(String messageKey)
     {
          return GUIResources.getMessage(messageKey);
     }
@@ -1441,40 +1596,47 @@ public class ProGuardGUI extends JFrame
     /**
      * The main method for the ProGuard GUI.
      */
-    public static void main(String[] args)
+    public static void main(final String[] args)
     {
-        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();
-        }
+//        SwingUtil.invokeAndWait(new Runnable()
+//        {
+//            public void run()
+            {
+                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] + "...]");
+                }
+
+            }
+//        });
     }
 }
diff --git a/src/proguard/gui/ProGuardRunnable.java b/src/proguard/gui/ProGuardRunnable.java
index 0711095..3cff177 100644
--- a/src/proguard/gui/ProGuardRunnable.java
+++ b/src/proguard/gui/ProGuardRunnable.java
@@ -1,6 +1,6 @@
-/* $Id: ProGuardRunnable.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,11 @@
  */
 package proguard.gui;
 
-import java.awt.*;
-import java.io.*;
+import proguard.*;
 
 import javax.swing.*;
-
-import proguard.*;
+import java.awt.*;
+import java.io.PrintStream;
 
 
 /**
@@ -35,11 +34,11 @@ import proguard.*;
  * @see ProGuard
  * @author Eric Lafortune
  */
-class ProGuardRunnable implements Runnable
+final class ProGuardRunnable implements Runnable
 {
-    private JTextArea     consoleTextArea;
-    private Configuration configuration;
-    private String        configurationFileName;
+    private final JTextArea     consoleTextArea;
+    private final Configuration configuration;
+    private final String        configurationFileName;
 
 
     /**
@@ -114,13 +113,15 @@ class ProGuardRunnable implements Runnable
                                                     msg("errorProcessing"),
                                                     JOptionPane.ERROR_MESSAGE);
         }
+        finally
+        {
+            // Make sure all output has been sent to the console text area.
+            printStream.close();
 
-        // Make sure all output has been sent to the console text area.
-        printStream.flush();
-
-        // Restore the old System's out and err streams.
-        System.setOut(oldOut);
-        System.setErr(oldErr);
+            // Restore the old System's out and err streams.
+            System.setOut(oldOut);
+            System.setErr(oldErr);
+        }
 
         consoleTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
diff --git a/src/proguard/gui/ReTraceRunnable.java b/src/proguard/gui/ReTraceRunnable.java
index 7b549c5..23df458 100644
--- a/src/proguard/gui/ReTraceRunnable.java
+++ b/src/proguard/gui/ReTraceRunnable.java
@@ -1,6 +1,6 @@
-/* $Id: ReTraceRunnable.java,v 1.8.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,11 @@
  */
 package proguard.gui;
 
-import java.awt.Cursor;
-import java.io.*;
+import proguard.retrace.ReTrace;
 
 import javax.swing.*;
-
-import proguard.retrace.ReTrace;
+import java.awt.*;
+import java.io.*;
 
 
 /**
@@ -35,12 +34,12 @@ import proguard.retrace.ReTrace;
  * @see ReTrace
  * @author Eric Lafortune
  */
-class ReTraceRunnable implements Runnable
+final class ReTraceRunnable implements Runnable
 {
-    private JTextArea consoleTextArea;
-    private boolean   verbose;
-    private File      mappingFile;
-    private String    stackTrace;
+    private final JTextArea consoleTextArea;
+    private final boolean   verbose;
+    private final File      mappingFile;
+    private final String    stackTrace;
 
 
     /**
@@ -55,10 +54,10 @@ class ReTraceRunnable implements Runnable
                            File      mappingFile,
                            String    stackTrace)
     {
-        this.consoleTextArea  = consoleTextArea;
-        this.verbose          = verbose;
-        this.mappingFile      = mappingFile;
-        this.stackTrace       = stackTrace;
+        this.consoleTextArea = consoleTextArea;
+        this.verbose         = verbose;
+        this.mappingFile     = mappingFile;
+        this.stackTrace      = stackTrace;
     }
 
 
diff --git a/src/proguard/gui/SwingUtil.java b/src/proguard/gui/SwingUtil.java
index 0842777..c03065e 100644
--- a/src/proguard/gui/SwingUtil.java
+++ b/src/proguard/gui/SwingUtil.java
@@ -1,6 +1,6 @@
-/* $Id: SwingUtil.java,v 1.5.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -36,7 +36,7 @@ class SwingUtil
      * Invokes the given Runnable in the AWT event dispatching thread,
      * and waits for it to finish. This method may be called from any thread,
      * including the event dispatching thread itself.
-     * @see SwingUtilities#invokeAndWait(java.lang.Runnable)
+     * @see SwingUtilities#invokeAndWait(Runnable)
      * @param runnable the Runnable to be executed.
      */
     public static void invokeAndWait(Runnable runnable)
@@ -63,7 +63,7 @@ class SwingUtil
      * Invokes the given Runnable in the AWT event dispatching thread, not
      * necessarily right away. This method may be called from any thread,
      * including the event dispatching thread itself.
-     * @see SwingUtilities#invokeLater(java.lang.Runnable)
+     * @see SwingUtilities#invokeLater(Runnable)
      * @param runnable the Runnable to be executed.
      */
     public static void invokeLater(Runnable runnable)
diff --git a/src/proguard/gui/TabbedPane.java b/src/proguard/gui/TabbedPane.java
index 836f398..a48627f 100644
--- a/src/proguard/gui/TabbedPane.java
+++ b/src/proguard/gui/TabbedPane.java
@@ -1,6 +1,6 @@
-/* $Id: TabbedPane.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,10 @@
  */
 package proguard.gui;
 
+import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
 
-import javax.swing.*;
-
 
 /**
  * This <code>Jpanel</code> is similar to a <code>JTabbedPane</code>.
@@ -37,9 +36,9 @@ import javax.swing.*;
 public class TabbedPane
      extends JPanel
 {
-    private CardLayout  cardLayout  = new CardLayout();
-    private JPanel      cardPanel   = new JPanel(cardLayout);
-    private ButtonGroup buttonGroup = new ButtonGroup();
+    private final CardLayout  cardLayout  = new CardLayout();
+    private final JPanel      cardPanel   = new JPanel(cardLayout);
+    private final ButtonGroup buttonGroup = new ButtonGroup();
 
 
     /**
@@ -91,7 +90,7 @@ public class TabbedPane
                     return;
                 }
 
-                if (b == false && isArmed())
+                if (!b && isArmed())
                 {
                     setSelected(!this.isSelected());
                 }
diff --git a/src/proguard/gui/TextAreaOutputStream.java b/src/proguard/gui/TextAreaOutputStream.java
index de1063f..a0c8abe 100644
--- a/src/proguard/gui/TextAreaOutputStream.java
+++ b/src/proguard/gui/TextAreaOutputStream.java
@@ -1,6 +1,6 @@
-/* $Id: TextAreaOutputStream.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,8 @@
  */
 package proguard.gui;
 
-import java.io.*;
-
 import javax.swing.*;
+import java.io.*;
 
 
 /**
@@ -30,9 +29,9 @@ import javax.swing.*;
  *
  * @author Eric Lafortune
  */
-class TextAreaOutputStream extends FilterOutputStream implements Runnable
+final class TextAreaOutputStream extends FilterOutputStream implements Runnable
 {
-    private JTextArea textArea;
+    private final JTextArea textArea;
 
 
     public TextAreaOutputStream(JTextArea textArea)
@@ -56,7 +55,7 @@ class TextAreaOutputStream extends FilterOutputStream implements Runnable
 
     // Implementation for Runnable.
 
-    public synchronized void run()
+    public void run()
     {
         ByteArrayOutputStream out = (ByteArrayOutputStream)super.out;
 
diff --git a/src/proguard/gui/boilerplate.pro b/src/proguard/gui/boilerplate.pro
index ad11708..4a1ac2b 100644
--- a/src/proguard/gui/boilerplate.pro
+++ b/src/proguard/gui/boilerplate.pro
@@ -1,4 +1,5 @@
-# Keep - Applications. Keep all application classes that have a main method.
+# Keep - Applications. Keep all application classes, along with their 'main'
+# methods.
 -keepclasseswithmembers public class * {
     public static void main(java.lang.String[]);
 }
@@ -15,70 +16,66 @@
 # Keep - Xlets. Keep all extensions of java.tv.xlet.Xlet.
 -keep public class * extends java.tv.xlet.Xlet
 
-# Keep - Library. Keep all externally accessible classes, fields, and methods.
+# Keep - Library. Keep all public and protected classes, fields, and methods.
 -keep public class * {
     public protected <fields>;
     public protected <methods>;
 }
 
-# Also keep - Enumerations. Keep special static methods that are required in
+# Also keep - Enumerations. Keep the special static methods that are required in
 # enumeration classes.
 -keepclassmembers class * extends java.lang.Enum {
     public static **[] values();
     public static ** valueOf(java.lang.String);
 }
 
-# Also keep - Serialization code. Keep all fields and methods that are
-# used for serialization.
+# Also keep - Serialization code. Keep all fields and methods that are used for
+# serialization.
 -keepclassmembers class * extends java.io.Serializable {
     static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
     private void writeObject(java.io.ObjectOutputStream);
     private void readObject(java.io.ObjectInputStream);
     java.lang.Object writeReplace();
     java.lang.Object readResolve();
 }
 
-# Also keep - BeanInfo classes. Keep all classes that implement the
-# BeanInfo interface.
+# Also keep - BeanInfo classes. Keep all implementations of java.beans.BeanInfo.
 -keep class * implements java.beans.BeanInfo
 
-# Also keep - Bean classes. Keep all bean classes along with their getters
+# Also keep - Bean classes. Keep all specified classes, along with their getters
 # and setters.
 -keep class * {
-    void set*(%);
-    void set*(**);
-    void set*(%[]);
-    void set*(**[]);
-    void set*(int, %);
-    void set*(int, **);
-
-    %    get*();
-    **   get*();
-    %[]  get*();
-    **[] get*();
-    %    get*(int);
-    **   get*(int);
+    void set*(***);
+    void set*(int,***);
+
+    boolean is*(); 
+    boolean is*(int);
+
+    *** get*();
+    *** get*(int);
 }
 
-# Also keep - Database drivers. Keep any implementations of java.sql.Driver.
+# Also keep - Database drivers. Keep all implementations of java.sql.Driver.
 -keep class * implements java.sql.Driver
 
-# Also keep - Swing UI L&F. Keep all classes that extend the ComponentUI class,
-# along with the special static method that is required.
+# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI,
+# along with the special 'createUI' method.
 -keep class * extends javax.swing.plaf.ComponentUI {
     public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);
 }
 
-# Also keep - RMI interfaces. Keep all Remote interfaces and their methods.
+# Also keep - RMI interfaces. Keep all interfaces that extend the
+# java.rmi.Remote interface, and their methods.
 -keep interface * extends java.rmi.Remote {
     <methods>;
 }
 
-# Also keep - RMI implementations. Keep all Remote implementations. This
-# includes any explicit or implicit Activatable implementations with their
+# Also keep - RMI implementations. Keep all implementations of java.rmi.Remote,
+# including any explicit or implicit implementations of Activatable, with their
 # two-argument constructors.
 -keep class * implements java.rmi.Remote {
-    <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject);
+    <init>(java.rmi.activation.ActivationID,java.rmi.MarshalledObject);
 }
 
 # Keep names - Native method names. Keep all native class/method names.
@@ -86,11 +83,11 @@
     native <methods>;
 }
 
-# Keep names - _class method names. Keep all .class method names. Useful for
-# libraries that will be obfuscated again.
+# Keep names - _class method names. Keep all .class method names. This may be
+# useful for libraries that will be obfuscated again with different obfuscators.
 -keepclassmembernames class * {
     java.lang.Class class$(java.lang.String);
-    java.lang.Class class$(java.lang.String, boolean);
+    java.lang.Class class$(java.lang.String,boolean);
 }
 
 # Remove - System method calls. Remove all invocations of System
@@ -123,12 +120,12 @@
     public static double log10(double);
     public static double sqrt(double);
     public static double cbrt(double);
-    public static double IEEEremainder(double, double);
+    public static double IEEEremainder(double,double);
     public static double ceil(double);
     public static double floor(double);
     public static double rint(double);
-    public static double atan2(double, double);
-    public static double pow(double, double);
+    public static double atan2(double,double);
+    public static double pow(double,double);
     public static int round(float);
     public static long round(double);
     public static double random();
@@ -136,14 +133,14 @@
     public static long abs(long);
     public static float abs(float);
     public static double abs(double);
-    public static int max(int, int);
-    public static long max(long, long);
-    public static float max(float, float);
-    public static double max(double, double);
-    public static int min(int, int);
-    public static long min(long, long);
-    public static float min(float, float);
-    public static double min(double, double);
+    public static int max(int,int);
+    public static long max(long,long);
+    public static float max(float,float);
+    public static double max(double,double);
+    public static int min(int,int);
+    public static long min(long,long);
+    public static float min(float,float);
+    public static double min(double,double);
     public static double ulp(double);
     public static float ulp(float);
     public static double signum(double);
@@ -151,7 +148,7 @@
     public static double sinh(double);
     public static double cosh(double);
     public static double tanh(double);
-    public static double hypot(double, double);
+    public static double hypot(double,double);
     public static double expm1(double);
     public static double log1p(double);
 }
@@ -348,8 +345,8 @@
     public java.lang.String trim();
 }
 
-# Remove - StringBuffer method calls. Remove all invocations of
-# StringBuffer methods without side effects whose return values are not used.
+# Remove - StringBuffer method calls. Remove all invocations of StringBuffer
+# methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.StringBuffer {
     public java.lang.StringBuffer();
     public java.lang.StringBuffer(int);
@@ -368,8 +365,8 @@
     public java.lang.String substring(int,int);
 }
 
-# Remove - StringBuilder method calls. Remove all invocations of
-# StringBuilder methods without side effects whose return values are not used.
+# Remove - StringBuilder method calls. Remove all invocations of StringBuilder
+# methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.StringBuilder {
     public java.lang.StringBuilder();
     public java.lang.StringBuilder(int);
diff --git a/src/proguard/gui/default.pro b/src/proguard/gui/default.pro
index a2038e5..f9df53f 100644
--- a/src/proguard/gui/default.pro
+++ b/src/proguard/gui/default.pro
@@ -2,34 +2,33 @@
 
 -libraryjars <java.home>/lib/rt.jar
 
--verbose
-
-# Keep - Applications. Keep all application classes that have a main method.
+# Keep - Applications. Keep all application classes, along with their 'main'
+# methods.
 -keepclasseswithmembers public class * {
     public static void main(java.lang.String[]);
 }
 
-# Keep names - Native method names. Keep all native class/method names.
--keepclasseswithmembernames class * {
-    native <methods>;
-}
-
-# Also keep - Enumerations. Keep special static methods that are required in
+# Also keep - Enumerations. Keep the special static methods that are required in
 # enumeration classes.
 -keepclassmembers class * extends java.lang.Enum {
     public static **[] values();
     public static ** valueOf(java.lang.String);
 }
 
-# Also keep - Database drivers. Keep any implementations of java.sql.Driver.
--keep class * implements java.sql.Driver
+# Also keep - Database drivers. Keep all implementations of java.sql.Driver.
+-keep class * extends java.sql.Driver
 
-# Also keep - Swing UI L&F. Keep all classes that extend the ComponentUI class,
-# along with the special static method that is required.
+# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI,
+# along with the special 'createUI' method.
 -keep class * extends javax.swing.plaf.ComponentUI {
     public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);
 }
 
+# Keep names - Native method names. Keep all native class/method names.
+-keepclasseswithmembers,allowshrinking class * {
+    native <methods>;
+}
+
 # Remove - System method calls. Remove all invocations of System
 # methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.System {
@@ -60,12 +59,12 @@
     public static double log10(double);
     public static double sqrt(double);
     public static double cbrt(double);
-    public static double IEEEremainder(double, double);
+    public static double IEEEremainder(double,double);
     public static double ceil(double);
     public static double floor(double);
     public static double rint(double);
-    public static double atan2(double, double);
-    public static double pow(double, double);
+    public static double atan2(double,double);
+    public static double pow(double,double);
     public static int round(float);
     public static long round(double);
     public static double random();
@@ -73,14 +72,14 @@
     public static long abs(long);
     public static float abs(float);
     public static double abs(double);
-    public static int max(int, int);
-    public static long max(long, long);
-    public static float max(float, float);
-    public static double max(double, double);
-    public static int min(int, int);
-    public static long min(long, long);
-    public static float min(float, float);
-    public static double min(double, double);
+    public static int max(int,int);
+    public static long max(long,long);
+    public static float max(float,float);
+    public static double max(double,double);
+    public static int min(int,int);
+    public static long min(long,long);
+    public static float min(float,float);
+    public static double min(double,double);
     public static double ulp(double);
     public static float ulp(float);
     public static double signum(double);
@@ -88,7 +87,7 @@
     public static double sinh(double);
     public static double cosh(double);
     public static double tanh(double);
-    public static double hypot(double, double);
+    public static double hypot(double,double);
     public static double expm1(double);
     public static double log1p(double);
 }
@@ -104,7 +103,6 @@
     public static java.lang.Byte valueOf(java.lang.String);
     public static java.lang.Byte decode(java.lang.String);
     public int compareTo(java.lang.Byte);
-
     public static java.lang.String toString(short);
     public static short parseShort(java.lang.String);
     public static short parseShort(java.lang.String,int);
@@ -114,7 +112,6 @@
     public static java.lang.Short decode(java.lang.String);
     public static short reverseBytes(short);
     public int compareTo(java.lang.Short);
-
     public static java.lang.String toString(int,int);
     public static java.lang.String toHexString(int);
     public static java.lang.String toOctalString(int);
@@ -140,7 +137,6 @@
     public static int signum(int);
     public static int reverseBytes(int);
     public int compareTo(java.lang.Integer);
-
     public static java.lang.String toString(long,int);
     public static java.lang.String toHexString(long);
     public static java.lang.String toOctalString(long);
@@ -166,7 +162,6 @@
     public static int signum(long);
     public static long reverseBytes(long);
     public int compareTo(java.lang.Long);
-
     public static java.lang.String toString(float);
     public static java.lang.String toHexString(float);
     public static java.lang.Float valueOf(java.lang.String);
@@ -181,7 +176,6 @@
     public boolean isNaN();
     public boolean isInfinite();
     public int compareTo(java.lang.Float);
-
     public static java.lang.String toString(double);
     public static java.lang.String toHexString(double);
     public static java.lang.Double valueOf(java.lang.String);
@@ -196,7 +190,6 @@
     public boolean isNaN();
     public boolean isInfinite();
     public int compareTo(java.lang.Double);
-
     public <init>(byte);
     public <init>(short);
     public <init>(int);
@@ -210,7 +203,6 @@
     public long longValue();
     public float floatValue();
     public double doubleValue();
-
     public int compareTo(java.lang.Object);
     public boolean equals(java.lang.Object);
     public int hashCode();
@@ -220,17 +212,17 @@
 # Remove - String method calls. Remove all invocations of String
 # methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.String {
-    public java.lang.String();
-    public java.lang.String(byte[]);
-    public java.lang.String(byte[],int);
-    public java.lang.String(byte[],int,int);
-    public java.lang.String(byte[],int,int,int);
-    public java.lang.String(byte[],int,int,java.lang.String);
-    public java.lang.String(byte[],java.lang.String);
-    public java.lang.String(char[]);
-    public java.lang.String(char[],int,int);
-    public java.lang.String(java.lang.String);
-    public java.lang.String(java.lang.StringBuffer);
+    public <init>();
+    public <init>(byte[]);
+    public <init>(byte[],int);
+    public <init>(byte[],int,int);
+    public <init>(byte[],int,int,int);
+    public <init>(byte[],int,int,java.lang.String);
+    public <init>(byte[],java.lang.String);
+    public <init>(char[]);
+    public <init>(char[],int,int);
+    public <init>(java.lang.String);
+    public <init>(java.lang.StringBuffer);
     public static java.lang.String copyValueOf(char[]);
     public static java.lang.String copyValueOf(char[],int,int);
     public static java.lang.String valueOf(boolean);
@@ -285,13 +277,13 @@
     public java.lang.String trim();
 }
 
-# Remove - StringBuffer method calls. Remove all invocations of
-# StringBuffer methods without side effects whose return values are not used.
+# Remove - StringBuffer method calls. Remove all invocations of StringBuffer
+# methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.StringBuffer {
-    public java.lang.StringBuffer();
-    public java.lang.StringBuffer(int);
-    public java.lang.StringBuffer(java.lang.String);
-    public java.lang.StringBuffer(java.lang.CharSequence);
+    public <init>();
+    public <init>(int);
+    public <init>(java.lang.String);
+    public <init>(java.lang.CharSequence);
     public java.lang.String toString();
     public char charAt(int);
     public int capacity();
@@ -305,13 +297,13 @@
     public java.lang.String substring(int,int);
 }
 
-# Remove - StringBuilder method calls. Remove all invocations of
-# StringBuilder methods without side effects whose return values are not used.
+# Remove - StringBuilder method calls. Remove all invocations of StringBuilder
+# methods without side effects whose return values are not used.
 -assumenosideeffects public class java.lang.StringBuilder {
-    public java.lang.StringBuilder();
-    public java.lang.StringBuilder(int);
-    public java.lang.StringBuilder(java.lang.String);
-    public java.lang.StringBuilder(java.lang.CharSequence);
+    public <init>();
+    public <init>(int);
+    public <init>(java.lang.String);
+    public <init>(java.lang.CharSequence);
     public java.lang.String toString();
     public char charAt(int);
     public int capacity();
diff --git a/src/proguard/gui/splash/BufferedSprite.java b/src/proguard/gui/splash/BufferedSprite.java
index 8e8aa31..9cd9976 100644
--- a/src/proguard/gui/splash/BufferedSprite.java
+++ b/src/proguard/gui/splash/BufferedSprite.java
@@ -1,6 +1,6 @@
-/* $Id: BufferedSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,39 +21,83 @@
 package proguard.gui.splash;
 
 import java.awt.*;
+import java.awt.image.BufferedImage;
 
 /**
- * This Sprite encapsulates another Sprite, which is buffered on an Image.
+ * This Sprite encapsulates another Sprite, which is then buffered in an Image.
  *
  * @author Eric Lafortune
  */
 public class BufferedSprite implements Sprite
 {
-    private Image    bufferImage;
-    private Graphics bufferGraphics;
-    private Color    backgroundColor;
-    private Sprite   sprite;
+    private final int         bufferX;
+    private final int         bufferY;
+    private final Image       bufferImage;
+    private final Color       backgroundColor;
+    private final Sprite      sprite;
+    private final VariableInt x;
+    private final VariableInt y;
 
     private long cachedTime = -1;
 
 
     /**
-     * Creates a new BufferedSprite.
+     * Creates a new BufferedSprite with an ABGR image.
+     * @param bufferX the x offset of the buffer image.
+     * @param bufferY the y offset of the buffer image.
+     * @param width   the width of the buffer image.
+     * @param height  the height of the buffer image.
+     * @param sprite  the Sprite that is painted in the buffer.
+     * @param x       the variable x ordinate of the image buffer for painting.
+     * @param y       the variable y ordinate of the image buffer for painting.
+     *
+     */
+    public BufferedSprite(int         bufferX,
+                          int         bufferY,
+                          int         width,
+                          int         height,
+                          Sprite      sprite,
+                          VariableInt x,
+                          VariableInt y)
+    {
+
+        this(bufferX,
+             bufferY,
+             new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR),
+             null,
+             sprite,
+             x,
+             y);
+    }
+
+
+    /**
+     * Creates a new BufferedSprite with the given image.
+     * @param bufferX         the x offset of the buffer image.
+     * @param bufferY         the y offset of the buffer image.
      * @param bufferImage     the Image that is used for the buffering.
-     * @param bufferGraphics  the Graphics of the Image.
      * @param backgroundColor the background color that is used for the buffer.
      * @param sprite          the Sprite that is painted in the buffer.
+     * @param x               the variable x ordinate of the image buffer for
+     *                        painting.
+     * @param y               the variable y ordinate of the image buffer for
+     *                        painting.
      */
-    public BufferedSprite(Image    bufferImage,
-                          Graphics bufferGraphics,
-                          Color    backgroundColor,
-                          Sprite   sprite)
+    public BufferedSprite(int         bufferX,
+                          int         bufferY,
+                          Image       bufferImage,
+                          Color       backgroundColor,
+                          Sprite      sprite,
+                          VariableInt x,
+                          VariableInt y)
     {
-
+        this.bufferX         = bufferX;
+        this.bufferY         = bufferY;
         this.bufferImage     = bufferImage;
-        this.bufferGraphics  = bufferGraphics;
         this.backgroundColor = backgroundColor;
         this.sprite          = sprite;
+        this.x               = x;
+        this.y               = y;
     }
 
 
@@ -61,25 +105,41 @@ public class BufferedSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
-        Rectangle clip = bufferGraphics.getClipBounds();
-
-        // Do we need to repaint the sprites in the buffer image?
         if (time != cachedTime)
         {
+            Graphics bufferGraphics = bufferImage.getGraphics();
+
             // Clear the background.
             if (backgroundColor != null)
             {
+                Graphics2D bufferGraphics2D = (Graphics2D)bufferGraphics;
+                bufferGraphics2D.setComposite(AlphaComposite.Clear);
+                bufferGraphics.fillRect(0, 0, bufferImage.getWidth(null), bufferImage.getHeight(null));
+                bufferGraphics2D.setComposite(AlphaComposite.Src);
+            }
+            else
+            {
                 bufferGraphics.setColor(backgroundColor);
-                bufferGraphics.fillRect(0, 0, clip.width, clip.height);
+                bufferGraphics.fillRect(0, 0, bufferImage.getWidth(null), bufferImage.getHeight(null));
             }
 
+            // Set up the buffer graphics.
+            bufferGraphics.translate(-bufferX, -bufferY);
+            bufferGraphics.setColor(graphics.getColor());
+            bufferGraphics.setFont(graphics.getFont());
+
             // Draw the sprite.
             sprite.paint(bufferGraphics, time);
 
+            bufferGraphics.dispose();
+
             cachedTime = time;
         }
 
         // Draw the buffer image.
-        graphics.drawImage(bufferImage, 0, 0, clip.width, clip.height, null);
+        graphics.drawImage(bufferImage,
+                           bufferX + x.getInt(time),
+                           bufferY + y.getInt(time),
+                           null);
     }
 }
diff --git a/src/proguard/gui/splash/CircleSprite.java b/src/proguard/gui/splash/CircleSprite.java
index 7409b6d..7747202 100644
--- a/src/proguard/gui/splash/CircleSprite.java
+++ b/src/proguard/gui/splash/CircleSprite.java
@@ -1,6 +1,6 @@
-/* $Id: CircleSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
  * This Sprite represents an animated circle. It can optionally be filled.
@@ -29,29 +29,25 @@ import java.awt.Graphics;
  */
 public class CircleSprite implements Sprite
 {
-    private boolean       filled;
-    private VariableColor color;
-    private VariableInt   x;
-    private VariableInt   y;
-    private VariableInt   radius;
+    private final boolean     filled;
+    private final VariableInt x;
+    private final VariableInt y;
+    private final VariableInt radius;
 
 
     /**
      * Creates a new CircleSprite.
      * @param filled specifies whether the rectangle should be filled.
-     * @param color  the variable color of the circle.
      * @param x      the variable x-coordinate of the center of the circle.
      * @param y      the variable y-coordinate of the center of the circle.
      * @param radius the variable radius of the circle.
      */
-    public CircleSprite(boolean       filled,
-                        VariableColor color,
-                        VariableInt   x,
-                        VariableInt   y,
-                        VariableInt   radius)
+    public CircleSprite(boolean     filled,
+                        VariableInt x,
+                        VariableInt y,
+                        VariableInt radius)
     {
         this.filled = filled;
-        this.color  = color;
         this.x      = x;
         this.y      = y;
         this.radius = radius;
@@ -62,8 +58,6 @@ public class CircleSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
-        graphics.setColor(color.getColor(time));
-
         int xt = x.getInt(time);
         int yt = y.getInt(time);
         int r  = radius.getInt(time);
diff --git a/src/proguard/gui/splash/ClipSprite.java b/src/proguard/gui/splash/ClipSprite.java
index cf58512..d7bc269 100644
--- a/src/proguard/gui/splash/ClipSprite.java
+++ b/src/proguard/gui/splash/ClipSprite.java
@@ -1,6 +1,6 @@
-/* $Id: ClipSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,10 +29,10 @@ import java.awt.*;
  */
 public class ClipSprite implements Sprite
 {
-    private VariableColor insideClipColor;
-    private VariableColor outsideClipColor;
-    private Sprite        clipSprite;
-    private Sprite        sprite;
+    private final VariableColor insideClipColor;
+    private final VariableColor outsideClipColor;
+    private final Sprite        clipSprite;
+    private final Sprite        sprite;
 
 
     /**
diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/ColorSprite.java
similarity index 52%
copy from src/proguard/gui/splash/CompositeSprite.java
copy to src/proguard/gui/splash/ColorSprite.java
index 705db83..739b8c4 100644
--- a/src/proguard/gui/splash/CompositeSprite.java
+++ b/src/proguard/gui/splash/ColorSprite.java
@@ -1,6 +1,6 @@
-/* $Id: CompositeSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,26 +20,29 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
- * This Sprite is the composition of a list of Sprite objects.
+ * This Sprite colors another given sprite.
  *
  * @author Eric Lafortune
  */
-public class CompositeSprite implements Sprite
+public class ColorSprite implements Sprite
 {
-    private Sprite[] sprites;
+    private final VariableColor color;
+    private final Sprite        sprite;
 
 
     /**
-     * Creates a new CompositeSprite.
-     * @param sprites the array of Sprite objects to which the painting will
-     *                be delegated, starting with the first element.
+     * Creates a new ColorSprite.
+     * @param color  the variable color of the given sprite.
+     * @param sprite the sprite that will be colored and painted.
      */
-    public CompositeSprite(Sprite[] sprites)
+    public ColorSprite(VariableColor color,
+                       Sprite        sprite)
     {
-        this.sprites = sprites;
+        this.color  = color;
+        this.sprite = sprite;
     }
 
 
@@ -47,10 +50,16 @@ public class CompositeSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
-        // Draw the sprites.
-        for (int index = 0; index < sprites.length; index++)
-        {
-            sprites[index].paint(graphics, time);
-        }
+        // Save the old color.
+        Color oldColor = graphics.getColor();
+
+        // Set the new color.
+        graphics.setColor(color.getColor(time));
+
+        // Paint the actual sprite.
+        sprite.paint(graphics, time);
+
+        // Restore the old color.
+        graphics.setColor(oldColor);
     }
 }
diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/CompositeSprite.java
index 705db83..a3d4d04 100644
--- a/src/proguard/gui/splash/CompositeSprite.java
+++ b/src/proguard/gui/splash/CompositeSprite.java
@@ -1,6 +1,6 @@
-/* $Id: CompositeSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
  * This Sprite is the composition of a list of Sprite objects.
@@ -29,7 +29,7 @@ import java.awt.Graphics;
  */
 public class CompositeSprite implements Sprite
 {
-    private Sprite[] sprites;
+    private final Sprite[] sprites;
 
 
     /**
diff --git a/src/proguard/gui/splash/ConstantColor.java b/src/proguard/gui/splash/ConstantColor.java
index 9391ff2..6e25cbf 100644
--- a/src/proguard/gui/splash/ConstantColor.java
+++ b/src/proguard/gui/splash/ConstantColor.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantColor.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Color;
+import java.awt.*;
 
 /**
  * This VariableColor is constant over time.
@@ -29,7 +29,7 @@ import java.awt.Color;
  */
 public class ConstantColor implements VariableColor
 {
-    private Color value;
+    private final Color value;
 
 
     /**
diff --git a/src/proguard/gui/splash/ConstantDouble.java b/src/proguard/gui/splash/ConstantDouble.java
index da0e8fd..d66eb97 100644
--- a/src/proguard/gui/splash/ConstantDouble.java
+++ b/src/proguard/gui/splash/ConstantDouble.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantDouble.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,7 +27,7 @@ package proguard.gui.splash;
  */
 public class ConstantDouble implements VariableDouble
 {
-    private double value;
+    private final double value;
 
 
     /**
diff --git a/src/proguard/gui/splash/ConstantFont.java b/src/proguard/gui/splash/ConstantFont.java
index f69f2ad..e96a17a 100644
--- a/src/proguard/gui/splash/ConstantFont.java
+++ b/src/proguard/gui/splash/ConstantFont.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantFont.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Font;
+import java.awt.*;
 
 /**
  * This VariableFont is constant over time.
@@ -29,7 +29,7 @@ import java.awt.Font;
  */
 public class ConstantFont implements VariableFont
 {
-    private Font value;
+    private final Font value;
 
     public ConstantFont(Font value)
     {
diff --git a/src/proguard/gui/splash/ConstantInt.java b/src/proguard/gui/splash/ConstantInt.java
index c007256..913765c 100644
--- a/src/proguard/gui/splash/ConstantInt.java
+++ b/src/proguard/gui/splash/ConstantInt.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantInt.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,7 +27,7 @@ package proguard.gui.splash;
  */
 public class ConstantInt implements VariableInt
 {
-    private int value;
+    private final int value;
 
 
     /**
diff --git a/src/proguard/gui/splash/ConstantString.java b/src/proguard/gui/splash/ConstantString.java
index 3983dcf..50e50ac 100644
--- a/src/proguard/gui/splash/ConstantString.java
+++ b/src/proguard/gui/splash/ConstantString.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantString.java,v 1.5 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,7 +27,7 @@ package proguard.gui.splash;
  */
 public class ConstantString implements VariableString
 {
-    private String value;
+    private final String value;
 
 
     /**
diff --git a/src/proguard/gui/splash/ConstantTiming.java b/src/proguard/gui/splash/ConstantTiming.java
index 2c98fe4..25af3d9 100644
--- a/src/proguard/gui/splash/ConstantTiming.java
+++ b/src/proguard/gui/splash/ConstantTiming.java
@@ -1,6 +1,6 @@
-/* $Id: ConstantTiming.java,v 1.7 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,12 +27,11 @@ package proguard.gui.splash;
  */
 public class ConstantTiming implements Timing
 {
-    private double timing;
+    private final double timing;
 
 
     /**
      * Creates a new ConstantTiming with a value of 0.
-     * @param timing the constant value of the timing.
      */
     public ConstantTiming()
     {
diff --git a/src/proguard/gui/splash/CompositeSprite.java b/src/proguard/gui/splash/FontSprite.java
similarity index 52%
copy from src/proguard/gui/splash/CompositeSprite.java
copy to src/proguard/gui/splash/FontSprite.java
index 705db83..0c9f82b 100644
--- a/src/proguard/gui/splash/CompositeSprite.java
+++ b/src/proguard/gui/splash/FontSprite.java
@@ -1,6 +1,6 @@
-/* $Id: CompositeSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,26 +20,29 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
- * This Sprite is the composition of a list of Sprite objects.
+ * This Sprite sets the font for another given sprite.
  *
  * @author Eric Lafortune
  */
-public class CompositeSprite implements Sprite
+public class FontSprite implements Sprite
 {
-    private Sprite[] sprites;
+    private final VariableFont font;
+    private final Sprite       sprite;
 
 
     /**
-     * Creates a new CompositeSprite.
-     * @param sprites the array of Sprite objects to which the painting will
-     *                be delegated, starting with the first element.
+     * Creates a new FontSprite.
+     * @param font   the variable Font of the given sprite.
+     * @param sprite the sprite that will be provided of a font and painted.
      */
-    public CompositeSprite(Sprite[] sprites)
+    public FontSprite(VariableFont font,
+                      Sprite       sprite)
     {
-        this.sprites = sprites;
+        this.font   = font;
+        this.sprite = sprite;
     }
 
 
@@ -47,10 +50,16 @@ public class CompositeSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
-        // Draw the sprites.
-        for (int index = 0; index < sprites.length; index++)
-        {
-            sprites[index].paint(graphics, time);
-        }
+        // Save the old font.
+        Font oldFont = graphics.getFont();
+
+        // Set the new font.
+        graphics.setFont(font.getFont(time));
+
+        // Paint the actual sprite.
+        sprite.paint(graphics, time);
+
+        // Restore the old font.
+        graphics.setFont(oldFont);
     }
 }
diff --git a/src/proguard/gui/splash/ImageSprite.java b/src/proguard/gui/splash/ImageSprite.java
index 444795b..183460e 100644
--- a/src/proguard/gui/splash/ImageSprite.java
+++ b/src/proguard/gui/splash/ImageSprite.java
@@ -1,6 +1,6 @@
-/* $Id: ImageSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,11 +29,11 @@ import java.awt.*;
  */
 public class ImageSprite implements Sprite
 {
-    private Image          image;
-    private VariableInt    x;
-    private VariableInt    y;
-    private VariableDouble scaleX;
-    private VariableDouble scaleY;
+    private final Image          image;
+    private final VariableInt    x;
+    private final VariableInt    y;
+    private final VariableDouble scaleX;
+    private final VariableDouble scaleY;
 
 
     /**
diff --git a/src/proguard/gui/splash/LinearColor.java b/src/proguard/gui/splash/LinearColor.java
index 3d0a80b..35857f2 100644
--- a/src/proguard/gui/splash/LinearColor.java
+++ b/src/proguard/gui/splash/LinearColor.java
@@ -1,6 +1,6 @@
-/* $Id: LinearColor.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Color;
+import java.awt.*;
 
 /**
  * This VariableColor varies linearly with respect to its Timing.
@@ -29,9 +29,9 @@ import java.awt.Color;
  */
 public class LinearColor implements VariableColor
 {
-    private Color  fromValue;
-    private Color  toValue;
-    private Timing timing;
+    private final Color  fromValue;
+    private final Color  toValue;
+    private final Timing timing;
 
     private double cachedTiming = -1.0;
     private Color  cachedColor;
diff --git a/src/proguard/gui/splash/LinearDouble.java b/src/proguard/gui/splash/LinearDouble.java
index f66a5c5..ac64d05 100644
--- a/src/proguard/gui/splash/LinearDouble.java
+++ b/src/proguard/gui/splash/LinearDouble.java
@@ -1,6 +1,6 @@
-/* $Id: LinearDouble.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,9 +27,9 @@ package proguard.gui.splash;
  */
 public class LinearDouble implements VariableDouble
 {
-    private double fromValue;
-    private double toValue;
-    private Timing timing;
+    private final double fromValue;
+    private final double toValue;
+    private final Timing timing;
 
 
     /**
diff --git a/src/proguard/gui/splash/LinearInt.java b/src/proguard/gui/splash/LinearInt.java
index c60d97e..96d8c65 100644
--- a/src/proguard/gui/splash/LinearInt.java
+++ b/src/proguard/gui/splash/LinearInt.java
@@ -1,6 +1,6 @@
-/* $Id: LinearInt.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,9 +27,9 @@ package proguard.gui.splash;
  */
 public class LinearInt implements VariableInt
 {
-    private int    fromValue;
-    private int    toValue;
-    private Timing timing;
+    private final int    fromValue;
+    private final int    toValue;
+    private final Timing timing;
 
 
     /**
diff --git a/src/proguard/gui/splash/LinearTiming.java b/src/proguard/gui/splash/LinearTiming.java
index 6655952..c85e7f8 100644
--- a/src/proguard/gui/splash/LinearTiming.java
+++ b/src/proguard/gui/splash/LinearTiming.java
@@ -1,6 +1,6 @@
-/* $Id: LinearTiming.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,8 +27,8 @@ package proguard.gui.splash;
  */
 public class LinearTiming implements Timing
 {
-    private long fromTime;
-    private long toTime;
+    private final long fromTime;
+    private final long toTime;
 
 
     /**
diff --git a/src/proguard/gui/splash/OverrideGraphics2D.java b/src/proguard/gui/splash/OverrideGraphics2D.java
index 0f6fcf5..5a14087 100644
--- a/src/proguard/gui/splash/OverrideGraphics2D.java
+++ b/src/proguard/gui/splash/OverrideGraphics2D.java
@@ -1,6 +1,6 @@
-/* $Id: OverrideGraphics2D.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -35,10 +35,11 @@ import java.util.Map;
  * change those settings.
  *
  * @author Eric Lafortune
+ * @noinspection deprecation
  */
-class OverrideGraphics2D extends Graphics2D
+final class OverrideGraphics2D extends Graphics2D
 {
-    private Graphics2D graphics;
+    private final Graphics2D graphics;
 
     private Color  overrideColor;
     private Font   overrideFont;
diff --git a/src/proguard/gui/splash/RectangleSprite.java b/src/proguard/gui/splash/RectangleSprite.java
index 0dc4e83..cb09550 100644
--- a/src/proguard/gui/splash/RectangleSprite.java
+++ b/src/proguard/gui/splash/RectangleSprite.java
@@ -1,6 +1,6 @@
-/* $Id: RectangleSprite.java,v 1.7.2.3 2007/01/31 20:57:28 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
  * This Sprite represents an animated rounded rectangle. It can optionally be filled.
@@ -29,52 +29,58 @@ import java.awt.Graphics;
  */
 public class RectangleSprite implements Sprite
 {
-    private boolean     filled;
-    private VariableInt x;
-    private VariableInt y;
-    private VariableInt width;
-    private VariableInt height;
-    private VariableInt arcWidth;
-    private VariableInt arcHeight;
+    private final boolean       filled;
+    private final VariableColor color;
+    private final VariableInt   x;
+    private final VariableInt   y;
+    private final VariableInt   width;
+    private final VariableInt   height;
+    private final VariableInt   arcWidth;
+    private final VariableInt   arcHeight;
 
 
     /**
      * Creates a new rectangular RectangleSprite.
      * @param filled specifies whether the rectangle should be filled.
-     * @param x      the variable x-coordinate of the upper-left corner of the rectangle.
-     * @param y      the variable y-coordinate of the upper-left corner of the rectangle.
+     * @param color  the variable color of the rectangle.
+     * @param x      the variable x-ordinate of the upper-left corner of the rectangle.
+     * @param y      the variable y-ordinate of the upper-left corner of the rectangle.
      * @param width  the variable width of the rectangle.
      * @param height the variable height of the rectangle.
      */
-    public RectangleSprite(boolean     filled,
-                           VariableInt x,
-                           VariableInt y,
-                           VariableInt width,
-                           VariableInt height)
+    public RectangleSprite(boolean       filled,
+                           VariableColor color,
+                           VariableInt   x,
+                           VariableInt   y,
+                           VariableInt   width,
+                           VariableInt   height)
     {
-        this(filled, x, y, width, height, new ConstantInt(0), new ConstantInt(0));
+        this(filled, color, x, y, width, height, new ConstantInt(0), new ConstantInt(0));
     }
 
 
     /**
      * Creates a new RectangleSprite with rounded corners.
-     * @param filled    specifies whether the rectangle should be filled.
-     * @param x         the variable x-coordinate of the upper-left corner of the rectangle.
-     * @param y         the variable y-coordinate of the upper-left corner of the rectangle.
+     * @param filled specifies whether the rectangle should be filled.
+     * @param color     the variable color of the rectangle.
+     * @param x         the variable x-ordinate of the upper-left corner of the rectangle.
+     * @param y         the variable y-ordinate of the upper-left corner of the rectangle.
      * @param width     the variable width of the rectangle.
      * @param height    the variable height of the rectangle.
      * @param arcWidth  the variable width of the corner arcs.
      * @param arcHeight the variable height of the corner arcs.
      */
-    public RectangleSprite(boolean     filled,
-                           VariableInt x,
-                           VariableInt y,
-                           VariableInt width,
-                           VariableInt height,
-                           VariableInt arcWidth,
-                           VariableInt arcHeight)
+    public RectangleSprite(boolean       filled,
+                           VariableColor color,
+                           VariableInt   x,
+                           VariableInt   y,
+                           VariableInt   width,
+                           VariableInt   height,
+                           VariableInt   arcWidth,
+                           VariableInt   arcHeight)
     {
         this.filled    = filled;
+        this.color     = color;
         this.x         = x;
         this.y         = y;
         this.width     = width;
@@ -87,6 +93,8 @@ public class RectangleSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
+        graphics.setColor(color.getColor(time));
+
         int xt = x.getInt(time);
         int yt = y.getInt(time);
         int w  = width.getInt(time);
diff --git a/src/proguard/gui/splash/SawToothTiming.java b/src/proguard/gui/splash/SawToothTiming.java
index d61012a..6c05c44 100644
--- a/src/proguard/gui/splash/SawToothTiming.java
+++ b/src/proguard/gui/splash/SawToothTiming.java
@@ -1,6 +1,6 @@
-/* $Id: SawToothTiming.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,8 +27,8 @@ package proguard.gui.splash;
  */
 public class SawToothTiming implements Timing
 {
-    private long period;
-    private long phase;
+    private final long period;
+    private final long phase;
 
 
     /**
diff --git a/src/proguard/gui/splash/ShadowedSprite.java b/src/proguard/gui/splash/ShadowedSprite.java
index b632b55..53630fd 100644
--- a/src/proguard/gui/splash/ShadowedSprite.java
+++ b/src/proguard/gui/splash/ShadowedSprite.java
@@ -1,6 +1,6 @@
-/* $Id: ShadowedSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,11 +29,11 @@ import java.awt.*;
  */
 public class ShadowedSprite implements Sprite
 {
-    private VariableInt    xOffset;
-    private VariableInt    yOffset;
-    private VariableDouble alpha;
-    private VariableInt    blur;
-    private Sprite         sprite;
+    private final VariableInt    xOffset;
+    private final VariableInt    yOffset;
+    private final VariableDouble alpha;
+    private final VariableInt    blur;
+    private final Sprite         sprite;
 
     private float cachedAlpha = -1.0f;
     private Color cachedColor;
@@ -69,33 +69,40 @@ public class ShadowedSprite implements Sprite
         double l = alpha.getDouble(time);
         int    b = blur.getInt(time) + 1;
 
-        // Set up the shadow graphics.
-        OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics);
-
         float a = 1.0f - (float)Math.pow(1.0 - l, 1.0/(b*b));
         if (a != cachedAlpha)
         {
             cachedAlpha = a;
             cachedColor = new Color(0f, 0f, 0f, a);
         }
-        g.setOverrideColor(cachedColor);
+
+        // Set up the shadow graphics.
+        //OverrideGraphics2D g = new OverrideGraphics2D((Graphics2D)graphics);
+        //g.setOverrideColor(cachedColor);
+
+        // Set the shadow color.
+        Color actualColor = graphics.getColor();
+        graphics.setColor(cachedColor);
 
         int xo = xOffset.getInt(time) - b/2;
         int yo = yOffset.getInt(time) - b/2;
 
-        // Draw the sprite's shadow in the shadow graphics.
+        // Draw the sprite's shadow.
         for (int x = 0; x < b; x++)
         {
             for (int y = 0; y < b; y++)
             {
                 int xt = xo + x;
                 int yt = yo + y;
-                g.translate(xt, yt);
-                sprite.paint(g, time);
-                g.translate(-xt, -yt);
+                graphics.translate(xt, yt);
+                sprite.paint(graphics, time);
+                graphics.translate(-xt, -yt);
             }
         }
 
+        // Restore the actual sprite color.
+        graphics.setColor(actualColor);
+
         // Draw the sprite itself in the ordinary graphics.
         sprite.paint(graphics, time);
     }
diff --git a/src/proguard/gui/splash/SineTiming.java b/src/proguard/gui/splash/SineTiming.java
index 8312577..5b69b81 100644
--- a/src/proguard/gui/splash/SineTiming.java
+++ b/src/proguard/gui/splash/SineTiming.java
@@ -1,6 +1,6 @@
-/* $Id: SineTiming.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,8 +27,8 @@ package proguard.gui.splash;
  */
 public class SineTiming implements Timing
 {
-    private long period;
-    private long phase;
+    private final long period;
+    private final long phase;
 
 
     /**
diff --git a/src/proguard/gui/splash/SmoothTiming.java b/src/proguard/gui/splash/SmoothTiming.java
index 8e5b9b1..2a19c06 100644
--- a/src/proguard/gui/splash/SmoothTiming.java
+++ b/src/proguard/gui/splash/SmoothTiming.java
@@ -1,6 +1,6 @@
-/* $Id: SmoothTiming.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -27,8 +27,8 @@ package proguard.gui.splash;
  */
 public class SmoothTiming implements Timing
 {
-    private long fromTime;
-    private long toTime;
+    private final long fromTime;
+    private final long toTime;
 
 
     /**
diff --git a/src/proguard/gui/splash/SplashPanel.java b/src/proguard/gui/splash/SplashPanel.java
index 98b581e..871fe9c 100644
--- a/src/proguard/gui/splash/SplashPanel.java
+++ b/src/proguard/gui/splash/SplashPanel.java
@@ -1,6 +1,6 @@
-/* $Id: SplashPanel.java,v 1.11.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,10 +20,10 @@
  */
 package proguard.gui.splash;
 
+import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
-
-import javax.swing.*;
+import java.lang.reflect.InvocationTargetException;
 
 /**
  * This JPanel renders an animated Sprite.
@@ -35,12 +35,13 @@ public class SplashPanel extends JPanel
     private final MyAnimator  animator  = new MyAnimator();
     private final MyRepainter repainter = new MyRepainter();
 
-    private Sprite sprite;
-    private double sleepFactor;
+    private final Sprite sprite;
+    private final double sleepFactor;
 
     private long   startTime = Long.MAX_VALUE;
-    private long   stopTime;
-    private Thread animationThread;
+    private final long   stopTime;
+
+    private volatile Thread animationThread;
 
 
     /**
@@ -115,8 +116,13 @@ public class SplashPanel extends JPanel
         {
             SwingUtilities.invokeAndWait(repainter);
         }
-        catch (Exception ex)
+        catch (InterruptedException ex)
+        {
+            // Nothing.
+        }
+        catch (InvocationTargetException ex)
         {
+            // Nothing.
         }
     }
 
@@ -152,14 +158,25 @@ public class SplashPanel extends JPanel
 
                     // Do a repaint and time it.
                     SwingUtilities.invokeAndWait(repainter);
-                    time = System.currentTimeMillis() - time;
 
                     // Sleep for a proportional while.
-                    Thread.sleep((long)(sleepFactor * time));
+                    long repaintTime = System.currentTimeMillis() - time;
+                    long sleepTime   = (long)(sleepFactor * repaintTime);
+                    if (sleepTime < 10L)
+                    {
+                        sleepTime = 10L;
+                    }
+
+                    Thread.sleep(sleepTime);
                 }
             }
-            catch (Exception ex)
+            catch (InterruptedException ex)
+            {
+                // Nothing.
+            }
+            catch (InvocationTargetException ex)
             {
+                // Nothing.
             }
         }
     }
@@ -195,15 +212,14 @@ public class SplashPanel extends JPanel
             new ConstantColor(Color.white),
             new ConstantColor(Color.lightGray),
             new CircleSprite(true,
-                             new ConstantColor(Color.green),
                              new LinearInt(200, 600, new SineTiming(2345L, 0L)),
                              new LinearInt(200, 400, new SineTiming(3210L, 0L)),
                              new ConstantInt(150)),
+            new ColorSprite(new ConstantColor(Color.gray),
+            new FontSprite(new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
             new TextSprite(new ConstantString("ProGuard"),
-                           new ConstantFont(new Font("sansserif", Font.BOLD, 90)),
-                           new ConstantColor(Color.gray),
                            new ConstantInt(200),
-                           new ConstantInt(300)));
+                           new ConstantInt(300)))));
 
         SplashPanel panel = new SplashPanel(sprite, 0.5);
         panel.setBackground(Color.white);
diff --git a/src/proguard/gui/splash/Sprite.java b/src/proguard/gui/splash/Sprite.java
index eaf6534..9e5e842 100644
--- a/src/proguard/gui/splash/Sprite.java
+++ b/src/proguard/gui/splash/Sprite.java
@@ -1,6 +1,6 @@
-/* $Id: Sprite.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
  * This interface describes objects that can paint themselves, possibly varying
diff --git a/src/proguard/gui/splash/TextSprite.java b/src/proguard/gui/splash/TextSprite.java
index 9d7df26..2450e62 100644
--- a/src/proguard/gui/splash/TextSprite.java
+++ b/src/proguard/gui/splash/TextSprite.java
@@ -1,6 +1,6 @@
-/* $Id: TextSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,29 +29,23 @@ import java.awt.*;
  */
 public class TextSprite implements Sprite
 {
-    private VariableString[] text;
-    private VariableInt      spacing;
-    private VariableFont     font;
-    private VariableColor    color;
-    private VariableInt      x;
-    private VariableInt      y;
+    private final VariableString[] text;
+    private final VariableInt      spacing;
+    private final VariableInt      x;
+    private final VariableInt      y;
 
 
     /**
      * Creates a new TextSprite containing a single line of text.
      * @param text  the variable text string.
-     * @param font  the variable text font.
-     * @param color the variable color.
      * @param x     the variable x-coordinate of the lower-left corner of the text.
      * @param y     the variable y-coordinate of the lower-left corner of the text.
      */
     public TextSprite(VariableString text,
-                      VariableFont   font,
-                      VariableColor  color,
                       VariableInt    x,
                       VariableInt    y)
     {
-        this(new VariableString[] { text }, new ConstantInt(0), font, color, x, y);
+        this(new VariableString[] { text }, new ConstantInt(0), x, y);
     }
 
 
@@ -59,8 +53,6 @@ public class TextSprite implements Sprite
      * Creates a new TextSprite containing a multiple lines of text.
      * @param text    the variable text strings.
      * @param spacing the variable spacing between the lines of text.
-     * @param font    the variable text font.
-     * @param color   the variable color.
      * @param x       the variable x-coordinate of the lower-left corner of the
      *                first line of text.
      * @param y       the variable y-coordinate of the lower-left corner of the
@@ -68,16 +60,12 @@ public class TextSprite implements Sprite
      */
     public TextSprite(VariableString[] text,
                       VariableInt      spacing,
-                      VariableFont     font,
-                      VariableColor    color,
                       VariableInt      x,
                       VariableInt      y)
     {
 
         this.text    = text;
         this.spacing = spacing;
-        this.font    = font;
-        this.color   = color;
         this.x       = x;
         this.y       = y;
     }
@@ -91,12 +79,11 @@ public class TextSprite implements Sprite
         int xt = x.getInt(time);
         int yt = y.getInt(time);
 
-        graphics.setFont(font.getFont(time));
-        graphics.setColor(color.getColor(time));
+        int spacingt = spacing.getInt(time);
 
         for (int index = 0; index < text.length; index++)
         {
-            graphics.drawString(text[index].getString(time), xt, yt + index * spacing.getInt(time));
+            graphics.drawString(text[index].getString(time), xt, yt + index * spacingt);
         }
     }
 }
diff --git a/src/proguard/gui/splash/TimeSwitchSprite.java b/src/proguard/gui/splash/TimeSwitchSprite.java
index cde8f36..3199f14 100644
--- a/src/proguard/gui/splash/TimeSwitchSprite.java
+++ b/src/proguard/gui/splash/TimeSwitchSprite.java
@@ -1,6 +1,6 @@
-/* $Id: TimeSwitchSprite.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Graphics;
+import java.awt.*;
 
 /**
  * This Sprite displays another Sprite in a given time interval.
@@ -30,14 +30,14 @@ import java.awt.Graphics;
  */
 public class TimeSwitchSprite implements Sprite
 {
-    private long   onTime;
-    private long   offtime;
-    private Sprite sprite;
+    private final long   onTime;
+    private final long offTime;
+    private final Sprite sprite;
 
 
     /**
-     * Creates a new TimeSwitchSprite for displaying a given Sprite starting at a
-     * given time.
+     * Creates a new TimeSwitchSprite for displaying a given Sprite starting at
+     * a given time.
      * @param onTime the start time.
      * @param sprite the toggled Sprite.
      */
@@ -48,16 +48,16 @@ public class TimeSwitchSprite implements Sprite
 
 
     /**
-     * Creates a new TimeSwitchSprite for displaying a given Sprite  in a given
+     * Creates a new TimeSwitchSprite for displaying a given Sprite in a given
      * time interval.
-     * @param onTime the start time.
+     * @param onTime  the start time.
      * @param offTime the stop time.
-     * @param sprite the toggled Sprite.
+     * @param sprite  the toggled Sprite.
      */
-    public TimeSwitchSprite(long onTime, long offtime, Sprite sprite)
+    public TimeSwitchSprite(long onTime, long offTime, Sprite sprite)
     {
         this.onTime  = onTime;
-        this.offtime = offtime;
+        this.offTime = offTime;
         this.sprite  = sprite;
     }
 
@@ -66,7 +66,7 @@ public class TimeSwitchSprite implements Sprite
 
     public void paint(Graphics graphics, long time)
     {
-        if (time >= onTime && (offtime <= 0 || time <= offtime))
+        if (time >= onTime && (offTime <= 0 || time <= offTime))
         {
             sprite.paint(graphics, time - onTime);
         }
diff --git a/src/proguard/gui/splash/Timing.java b/src/proguard/gui/splash/Timing.java
index da8f783..d954deb 100644
--- a/src/proguard/gui/splash/Timing.java
+++ b/src/proguard/gui/splash/Timing.java
@@ -1,6 +1,6 @@
-/* $Id: Timing.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/gui/splash/TypeWriterString.java b/src/proguard/gui/splash/TypeWriterString.java
index 9fa5d04..d4e3641 100644
--- a/src/proguard/gui/splash/TypeWriterString.java
+++ b/src/proguard/gui/splash/TypeWriterString.java
@@ -1,6 +1,6 @@
-/* $Id: TypeWriterString.java,v 1.7.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,8 +29,8 @@ package proguard.gui.splash;
  */
 public class TypeWriterString implements VariableString
 {
-    private String string;
-    private Timing timing;
+    private final String string;
+    private final Timing timing;
 
     private int    cachedLength = -1;
     private String cachedString;
diff --git a/src/proguard/gui/splash/VariableColor.java b/src/proguard/gui/splash/VariableColor.java
index 6434718..5c60bb6 100644
--- a/src/proguard/gui/splash/VariableColor.java
+++ b/src/proguard/gui/splash/VariableColor.java
@@ -1,6 +1,6 @@
-/* $Id: VariableColor.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Color;
+import java.awt.*;
 
 /**
  * This interface represents a Color that varies with time.
diff --git a/src/proguard/gui/splash/VariableDouble.java b/src/proguard/gui/splash/VariableDouble.java
index 96c961e..86d7db0 100644
--- a/src/proguard/gui/splash/VariableDouble.java
+++ b/src/proguard/gui/splash/VariableDouble.java
@@ -1,6 +1,6 @@
-/* $Id: VariableDouble.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/gui/splash/VariableFont.java
index 3c134cb..3e6ecdd 100644
--- a/src/proguard/gui/splash/VariableFont.java
+++ b/src/proguard/gui/splash/VariableFont.java
@@ -1,6 +1,6 @@
-/* $Id: VariableFont.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/gui/splash/VariableInt.java b/src/proguard/gui/splash/VariableInt.java
index e7cda25..bbdfa76 100644
--- a/src/proguard/gui/splash/VariableInt.java
+++ b/src/proguard/gui/splash/VariableInt.java
@@ -1,6 +1,6 @@
-/* $Id: VariableInt.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/gui/splash/VariableSizeFont.java b/src/proguard/gui/splash/VariableSizeFont.java
index 0dcfe78..945ac7d 100644
--- a/src/proguard/gui/splash/VariableSizeFont.java
+++ b/src/proguard/gui/splash/VariableSizeFont.java
@@ -1,6 +1,6 @@
-/* $Id: VariableSizeFont.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.gui.splash;
 
-import java.awt.Font;
+import java.awt.*;
 
 /**
  * This VariableFont varies in size with respect to its Timing.
@@ -29,8 +29,8 @@ import java.awt.Font;
  */
 public class VariableSizeFont implements VariableFont
 {
-    private Font           font;
-    private VariableDouble size;
+    private final Font           font;
+    private final VariableDouble size;
 
     private float cachedSize = -1.0f;
     private Font  cachedFont;
diff --git a/src/proguard/gui/splash/VariableString.java b/src/proguard/gui/splash/VariableString.java
index 2481a46..f95ce6f 100644
--- a/src/proguard/gui/splash/VariableString.java
+++ b/src/proguard/gui/splash/VariableString.java
@@ -1,6 +1,6 @@
-/* $Id: VariableString.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/src/proguard/io/CascadingDataEntryWriter.java
index e754406..cd78336 100644
--- a/src/proguard/io/CascadingDataEntryWriter.java
+++ b/src/proguard/io/CascadingDataEntryWriter.java
@@ -1,6 +1,6 @@
-/* $Id: CascadingDataEntryWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,8 +20,6 @@
  */
 package proguard.io;
 
-import proguard.util.*;
-
 import java.io.*;
 
 /**
@@ -37,7 +35,7 @@ public class CascadingDataEntryWriter implements DataEntryWriter
 
 
     /**
-     * Creates a new FilteredDataEntryWriter.
+     * Creates a new CascadingDataEntryWriter.
      * @param dataEntryWriter1 the DataEntryWriter to which the writing will be
      *                         delegated first.
      * @param dataEntryWriter2 the DataEntryWriter to which the writing will be
diff --git a/src/proguard/io/ClassFileReader.java b/src/proguard/io/ClassFileReader.java
deleted file mode 100644
index d29eb99..0000000
--- a/src/proguard/io/ClassFileReader.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* $Id: ClassFileReader.java,v 1.6.2.4 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.io;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-
-import java.io.*;
-
-/**
- * This DataEntryReader applies a given ClassFileVisitor to the class file
- * definitions that it reads.
- * <p>
- * Class files are read as ProgramClassFile objects or LibraryClassFile objects,
- * depending on the <code>isLibrary</code> flag.
- * <p>
- * In case of libraries, only public class files are considered, if the
- * <code>skipNonPublicLibraryClasses</code> flag is set.
- *
- * @author Eric Lafortune
- */
-public class ClassFileReader implements DataEntryReader
-{
-    private boolean          isLibrary;
-    private boolean          skipNonPublicLibraryClasses;
-    private boolean          skipNonPublicLibraryClassMembers;
-    private WarningPrinter   warningPrinter;
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new DataEntryClassFileFilter for reading the specified
-     * ClassFile objects.
-     */
-    public ClassFileReader(boolean          isLibrary,
-                           boolean          skipNonPublicLibraryClasses,
-                           boolean          skipNonPublicLibraryClassMembers,
-                           WarningPrinter   warningPrinter,
-                           ClassFileVisitor classFileVisitor)
-    {
-        this.isLibrary                        = isLibrary;
-        this.skipNonPublicLibraryClasses      = skipNonPublicLibraryClasses;
-        this.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
-        this.warningPrinter                   = warningPrinter;
-        this.classFileVisitor                 = classFileVisitor;
-    }
-
-
-    // Implementations for DataEntryReader.
-
-    public void read(DataEntry dataEntry) throws IOException
-    {
-        try
-        {
-            // Get the input stream.
-            InputStream inputStream = dataEntry.getInputStream();
-
-            // Wrap it into a data input stream.
-            DataInputStream dataInputStream = new DataInputStream(inputStream);
-
-            // Create a ClassFile representation.
-            ClassFile classFile = isLibrary ?
-                (ClassFile)LibraryClassFile.create(dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers) :
-                (ClassFile)ProgramClassFile.create(dataInputStream);
-
-            // Apply the visitor.
-            if (classFile != null)
-            {
-                if (!dataEntry.getName().replace(File.pathSeparatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR).equals(classFile.getName()+ClassConstants.CLASS_FILE_EXTENSION) &&
-                    warningPrinter != null)
-                {
-                    warningPrinter.print("Warning: class file [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(classFile.getName()) + "]");
-                }
-
-                classFile.accept(classFileVisitor);
-            }
-
-            dataEntry.closeInputStream();
-        }
-        catch (Exception ex)
-        {
-            throw new IOException("Can't process class file ["+dataEntry.getName()+"] ("+ex.getMessage()+")");
-        }
-    }
-}
diff --git a/src/proguard/io/ClassFileFilter.java b/src/proguard/io/ClassFilter.java
similarity index 66%
rename from src/proguard/io/ClassFileFilter.java
rename to src/proguard/io/ClassFilter.java
index 4ffe175..2e255a4 100644
--- a/src/proguard/io/ClassFileFilter.java
+++ b/src/proguard/io/ClassFilter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassFileFilter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,10 +20,10 @@
  */
 package proguard.io;
 
-import proguard.classfile.*;
-import proguard.util.*;
+import proguard.classfile.ClassConstants;
+import proguard.util.ExtensionMatcher;
 
-import java.io.*;
+import java.io.IOException;
 
 
 /**
@@ -32,33 +32,33 @@ import java.io.*;
  *
  * @author Eric Lafortune
  */
-public class ClassFileFilter implements DataEntryReader
+public class ClassFilter implements DataEntryReader
 {
-    private FilteredDataEntryReader filteredDataEntryReader;
+    private final FilteredDataEntryReader filteredDataEntryReader;
 
 
     /**
-     * Creates a new ClassFileFilter that delegates reading class files to the
+     * Creates a new ClassFilter that delegates reading classes to the
      * given reader.
      */
-    public ClassFileFilter(DataEntryReader classFileReader)
+    public ClassFilter(DataEntryReader classReader)
     {
-        this(classFileReader, null);
+        this(classReader, null);
     }
 
 
     /**
-     * Creates a new ClassFileFilter that delegates to either of the two given
+     * Creates a new ClassFilter that delegates to either of the two given
      * readers.
      */
-    public ClassFileFilter(DataEntryReader classFileReader,
-                           DataEntryReader dataEntryReader)
+    public ClassFilter(DataEntryReader classReader,
+                       DataEntryReader dataEntryReader)
     {
         filteredDataEntryReader =
             new FilteredDataEntryReader(
             new DataEntryNameFilter(
             new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
-            classFileReader,
+            classReader,
             dataEntryReader);
     }
 
diff --git a/src/proguard/io/ClassReader.java b/src/proguard/io/ClassReader.java
new file mode 100644
index 0000000..01b38dd
--- /dev/null
+++ b/src/proguard/io/ClassReader.java
@@ -0,0 +1,114 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+import proguard.classfile.io.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.ClassVisitor;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader applies a given ClassVisitor to the class
+ * definitions that it reads.
+ * <p>
+ * Class files are read as ProgramClass objects or LibraryClass objects,
+ * depending on the <code>isLibrary</code> flag.
+ * <p>
+ * In case of libraries, only public classes are considered, if the
+ * <code>skipNonPublicLibraryClasses</code> flag is set.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassReader implements DataEntryReader
+{
+    private final boolean        isLibrary;
+    private final boolean        skipNonPublicLibraryClasses;
+    private final boolean        skipNonPublicLibraryClassMembers;
+    private final WarningPrinter warningPrinter;
+    private final ClassVisitor   classVisitor;
+
+
+    /**
+     * Creates a new DataEntryClassFilter for reading the specified
+     * Clazz objects.
+     */
+    public ClassReader(boolean        isLibrary,
+                       boolean        skipNonPublicLibraryClasses,
+                       boolean        skipNonPublicLibraryClassMembers,
+                       WarningPrinter warningPrinter,
+                       ClassVisitor   classVisitor)
+    {
+        this.isLibrary                        = isLibrary;
+        this.skipNonPublicLibraryClasses      = skipNonPublicLibraryClasses;
+        this.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
+        this.warningPrinter                   = warningPrinter;
+        this.classVisitor                     = classVisitor;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        try
+        {
+            // Get the input stream.
+            InputStream inputStream = dataEntry.getInputStream();
+
+            // Wrap it into a data input stream.
+            DataInputStream dataInputStream = new DataInputStream(inputStream);
+
+            // Create a Clazz representation.
+            Clazz clazz;
+            if (isLibrary)
+            {
+                clazz = new LibraryClass();
+                clazz.accept(new LibraryClassReader(dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers));
+            }
+            else
+            {
+                clazz = new ProgramClass();
+                clazz.accept(new ProgramClassReader(dataInputStream));
+            }
+
+            // Apply the visitor, if we have a real class.
+            String className = clazz.getName();
+            if (className != null)
+            {
+                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) + "]");
+                }
+
+                clazz.accept(classVisitor);
+            }
+
+            dataEntry.closeInputStream();
+        }
+        catch (Exception ex)
+        {
+            throw new IOException("Can't process class ["+dataEntry.getName()+"] ("+ex.getMessage()+")");
+        }
+    }
+}
diff --git a/src/proguard/io/ClassFileRewriter.java b/src/proguard/io/ClassRewriter.java
similarity index 73%
rename from src/proguard/io/ClassFileRewriter.java
rename to src/proguard/io/ClassRewriter.java
index 8146423..5e3ce0a 100644
--- a/src/proguard/io/ClassFileRewriter.java
+++ b/src/proguard/io/ClassRewriter.java
@@ -1,6 +1,6 @@
-/* $Id: ClassFileRewriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,24 +21,25 @@
 package proguard.io;
 
 import proguard.classfile.*;
+import proguard.classfile.io.ProgramClassWriter;
 
 import java.io.*;
 
 
 /**
- * This DataEntryReader reads class file entries and writes their corresponding
+ * This DataEntryReader reads class entries and writes their corresponding
  * versions from the ClassPool to a given DataEntryWriter.
  *
  * @author Eric Lafortune
  */
-public class ClassFileRewriter implements DataEntryReader
+public class ClassRewriter implements DataEntryReader
 {
-    private ClassPool       classPool;
-    private DataEntryWriter dataEntryWriter;
+    private final ClassPool       classPool;
+    private final DataEntryWriter dataEntryWriter;
 
 
-    public ClassFileRewriter(ClassPool       classPool,
-                             DataEntryWriter dataEntryWriter)
+    public ClassRewriter(ClassPool       classPool,
+                         DataEntryWriter dataEntryWriter)
     {
         this.classPool       = classPool;
         this.dataEntryWriter = dataEntryWriter;
@@ -53,11 +54,11 @@ public class ClassFileRewriter implements DataEntryReader
         String className = inputName.substring(0, inputName.length() - ClassConstants.CLASS_FILE_EXTENSION.length());
 
         // Find the modified class corrsponding to the input entry.
-        ProgramClassFile programClassFile = (ProgramClassFile)classPool.getClass(className);
-        if (programClassFile != null)
+        ProgramClass programClass = (ProgramClass)classPool.getClass(className);
+        if (programClass != null)
         {
             // Rename the data entry if necessary.
-            String newClassName = programClassFile.getName();
+            String newClassName = programClass.getName();
             if (!className.equals(newClassName))
             {
                 dataEntry = new RenamedDataEntry(dataEntry, newClassName + ClassConstants.CLASS_FILE_EXTENSION);
@@ -69,7 +70,9 @@ public class ClassFileRewriter implements DataEntryReader
             {
                 // Write the class to the output entry.
                 DataOutputStream classOutputStream = new DataOutputStream(outputStream);
-                programClassFile.write(classOutputStream);
+
+                new ProgramClassWriter(classOutputStream).visitProgramClass(programClass);
+
                 classOutputStream.flush();
             }
         }
diff --git a/src/proguard/io/DataEntry.java b/src/proguard/io/DataEntry.java
index 1ba62fa..e6bb0dd 100644
--- a/src/proguard/io/DataEntry.java
+++ b/src/proguard/io/DataEntry.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntry.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/io/DataEntryCopier.java b/src/proguard/io/DataEntryCopier.java
index b6703a3..7b249f6 100644
--- a/src/proguard/io/DataEntryCopier.java
+++ b/src/proguard/io/DataEntryCopier.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryCopier.java,v 1.4.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.io;
 
-import proguard.util.*;
+import proguard.util.ExtensionMatcher;
 
 import java.io.*;
 
@@ -35,8 +35,8 @@ public class DataEntryCopier implements DataEntryReader
 {
     private static final int BUFFER_SIZE = 1024;
 
-    private DataEntryWriter dataEntryWriter;
-    private byte[]          buffer = new byte[BUFFER_SIZE];
+    private final DataEntryWriter dataEntryWriter;
+    private final byte[]          buffer = new byte[BUFFER_SIZE];
 
 
 
@@ -52,7 +52,6 @@ public class DataEntryCopier implements DataEntryReader
     {
         try
         {
-
             // Get the output entry corresponding to this input entry.
             OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
             if (outputStream != null)
diff --git a/src/proguard/io/DataEntryFilter.java b/src/proguard/io/DataEntryFilter.java
index e2110e4..1e179a6 100644
--- a/src/proguard/io/DataEntryFilter.java
+++ b/src/proguard/io/DataEntryFilter.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/io/DataEntryNameFilter.java b/src/proguard/io/DataEntryNameFilter.java
index fd25b46..8263bcc 100644
--- a/src/proguard/io/DataEntryNameFilter.java
+++ b/src/proguard/io/DataEntryNameFilter.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryNameFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import proguard.util.StringMatcher;
 public class DataEntryNameFilter
 implements   DataEntryFilter
 {
-    private StringMatcher stringMatcher;
+    private final StringMatcher stringMatcher;
 
 
     /**
diff --git a/src/proguard/io/DataEntryParentFilter.java b/src/proguard/io/DataEntryParentFilter.java
index 8f2bff6..52cef2d 100644
--- a/src/proguard/io/DataEntryParentFilter.java
+++ b/src/proguard/io/DataEntryParentFilter.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryParentFilter.java,v 1.2 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -28,7 +28,7 @@ package proguard.io;
 public class DataEntryParentFilter
 implements   DataEntryFilter
 {
-    private DataEntryFilter dataEntryFilter;
+    private final DataEntryFilter dataEntryFilter;
 
 
     /**
diff --git a/src/proguard/io/DataEntryPump.java b/src/proguard/io/DataEntryPump.java
index 382b8f5..4da2eb7 100644
--- a/src/proguard/io/DataEntryPump.java
+++ b/src/proguard/io/DataEntryPump.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryPump.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,14 +20,14 @@
  */
 package proguard.io;
 
-import java.io.*;
+import java.io.IOException;
 
 
 /**
  * This interface provides a method to pump data entries. The implementation
  * determines the source and the type of the data entries. Typical examples
  * are zip entries coming from a zip file of file entries coming from a
- * directory structure. The reader can for instance collect the class files,
+ * directory structure. The reader can for instance collect the classes,
  * or copy the resource files that are presented.
  *
  * @author Eric Lafortune
diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryReader.java
index c47a9d0..0e70129 100644
--- a/src/proguard/io/DataEntryReader.java
+++ b/src/proguard/io/DataEntryReader.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryReader.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,8 +20,7 @@
  */
 package proguard.io;
 
-import java.io.*;
-import java.util.zip.*;
+import java.io.IOException;
 
 
 /**
diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryRenamer.java
new file mode 100644
index 0000000..97872f5
--- /dev/null
+++ b/src/proguard/io/DataEntryRenamer.java
@@ -0,0 +1,78 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+
+import java.io.IOException;
+
+/**
+ * This DataEntryReader delegates to another DataEntryReader, renaming the
+ * data entries based on the renamed classes in the given ClassPool.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryRenamer implements DataEntryReader
+{
+    private final ClassPool       classPool;
+    private final DataEntryReader dataEntryReader;
+
+
+    public DataEntryRenamer(ClassPool       classPool,
+                            DataEntryReader dataEntryReader)
+    {
+        this.classPool       = classPool;
+        this.dataEntryReader = dataEntryReader;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        String dataEntryName = dataEntry.getName();
+
+        int suffixIndex = dataEntryName.lastIndexOf('.');
+
+        String className = suffixIndex > 0 ?
+            dataEntryName.substring(0, suffixIndex) :
+            dataEntryName;
+
+        // Find the class corrsponding to the data entry.
+        Clazz clazz = classPool.getClass(className);
+        if (clazz != null)
+        {
+            // Rename the data entry if necessary.
+            String newClassName = clazz.getName();
+            if (!className.equals(newClassName))
+            {
+                String newDataEntryName =  suffixIndex > 0 ?
+                    newClassName + dataEntryName.substring(suffixIndex) :
+                    newClassName;
+
+                dataEntry = new RenamedDataEntry(dataEntry, newDataEntryName);
+            }
+        }
+
+        // Delegate to the actual data entry reader.
+        dataEntryReader.read(dataEntry);
+    }
+}
diff --git a/src/proguard/io/DataEntryRewriter.java b/src/proguard/io/DataEntryRewriter.java
new file mode 100644
index 0000000..c138524
--- /dev/null
+++ b/src/proguard/io/DataEntryRewriter.java
@@ -0,0 +1,164 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader writes the resource 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 DataEntryRewriter implements DataEntryReader
+{
+    private final ClassPool       classPool;
+    private final DataEntryWriter dataEntryWriter;
+
+
+    /**
+     * Creates a new DataEntryRewriter.
+     */
+    public DataEntryRewriter(ClassPool       classPool,
+                             DataEntryWriter dataEntryWriter)
+    {
+        this.classPool       = classPool;
+        this.dataEntryWriter = dataEntryWriter;
+    }
+
+
+    // Implementations for DataEntryReader.
+
+    public void read(DataEntry dataEntry) throws IOException
+    {
+        try
+        {
+            // 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);
+
+                // Close the data entries.
+                dataEntry.closeInputStream();
+            }
+        }
+        catch (IOException ex)
+        {
+            System.err.println("Warning: can't write resource [" + dataEntry.getName() + "] (" + ex.getMessage() + ")");
+        }
+    }
+
+
+    // 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)
+    throws IOException
+    {
+        Reader reader = new BufferedReader(new InputStreamReader(inputStream));
+        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+
+        StringBuffer word = new StringBuffer();
+
+        while (true)
+        {
+            int i = reader.read();
+            if (i < 0)
+            {
+                break;
+            }
+
+            // Is the character part of a word?
+            char c = (char)i;
+            if (Character.isJavaIdentifierPart(c) ||
+                c == '.' ||
+                c == '-')
+            {
+                // Collect the characters in this word.
+                word.append(c);
+            }
+            else
+            {
+                // Write out the updated word, if any.
+                writeUpdatedWord(writer, word.toString());
+                word.setLength(0);
+
+                // Write out the character that terminated it.
+                writer.write(c);
+            }
+        }
+
+        // Write out the final word.
+        writeUpdatedWord(writer, word.toString());
+
+        writer.flush();
+        outputStream.flush();
+    }
+
+
+    /**
+     * Writes the given word to the given writer, after having adapted it,
+     * based on the renamed class names.
+     */
+    private void writeUpdatedWord(Writer writer, String word)
+    throws IOException
+    {
+        if (word.length() > 0)
+        {
+            String newWord = word;
+
+            boolean containsDots = word.indexOf('.') >= 0;
+
+            // Replace dots by forward slashes.
+            String className = containsDots ?
+                word.replace('.', ClassConstants.INTERNAL_PACKAGE_SEPARATOR) :
+                word;
+
+            // Find the class corrsponding to the word.
+            Clazz clazz = classPool.getClass(className);
+            if (clazz != null)
+            {
+                // Update the word if necessary.
+                String newClassName = clazz.getName();
+                if (!className.equals(newClassName))
+                {
+                    // Replace forward slashes by dots.
+                    newWord = containsDots ?
+                        newClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, '.') :
+                        newClassName;
+                }
+            }
+
+            writer.write(newWord);
+        }
+    }
+}
diff --git a/src/proguard/io/DataEntryWriter.java b/src/proguard/io/DataEntryWriter.java
index 9b1bca8..477a434 100644
--- a/src/proguard/io/DataEntryWriter.java
+++ b/src/proguard/io/DataEntryWriter.java
@@ -1,6 +1,6 @@
-/* $Id: DataEntryWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java
index be93969..881cb6e 100644
--- a/src/proguard/io/DirectoryPump.java
+++ b/src/proguard/io/DirectoryPump.java
@@ -1,6 +1,6 @@
-/* $Id: DirectoryPump.java,v 1.4.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import java.io.*;
  */
 public class DirectoryPump implements DataEntryPump
 {
-    private File directory;
+    private final File directory;
 
 
     public DirectoryPump(File directory)
diff --git a/src/proguard/io/DirectoryWriter.java b/src/proguard/io/DirectoryWriter.java
index 3ed2d1f..0513fc0 100644
--- a/src/proguard/io/DirectoryWriter.java
+++ b/src/proguard/io/DirectoryWriter.java
@@ -1,6 +1,6 @@
-/* $Id: DirectoryWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,21 +20,21 @@
  */
 package proguard.io;
 
-import proguard.classfile.*;
+import proguard.classfile.ClassConstants;
 
 import java.io.*;
 
 
 /**
- * This DataEntryWriter writes sends data entries to individual files in a
- * given directory.
+ * This DataEntryWriter writes data entries to individual files in a given
+ * directory.
  *
  * @author Eric Lafortune
  */
 public class DirectoryWriter implements DataEntryWriter
 {
-    private File    baseFile;
-    private boolean isFile;
+    private final File    baseFile;
+    private final boolean isFile;
 
     private File         currentFile;
     private OutputStream currentOutputStream;
diff --git a/src/proguard/io/FileDataEntry.java b/src/proguard/io/FileDataEntry.java
index c8d6b43..2a7246b 100644
--- a/src/proguard/io/FileDataEntry.java
+++ b/src/proguard/io/FileDataEntry.java
@@ -1,6 +1,6 @@
-/* $Id: FileDataEntry.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,8 +31,8 @@ import java.io.*;
  */
 public class FileDataEntry implements DataEntry
 {
-    private File        directory;
-    private File        file;
+    private final File        directory;
+    private final File        file;
     private InputStream inputStream;
 
 
diff --git a/src/proguard/io/FilteredDataEntryReader.java b/src/proguard/io/FilteredDataEntryReader.java
index 4d8ae2f..d740dc2 100644
--- a/src/proguard/io/FilteredDataEntryReader.java
+++ b/src/proguard/io/FilteredDataEntryReader.java
@@ -1,6 +1,6 @@
-/* $Id: FilteredDataEntryReader.java,v 1.3.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.io;
 
-import java.io.*;
+import java.io.IOException;
 
 
 /**
@@ -32,9 +32,9 @@ import java.io.*;
  */
 public class FilteredDataEntryReader implements DataEntryReader
 {
-    private DataEntryFilter dataEntryFilter;
-    private DataEntryReader acceptedDataEntryReader;
-    private DataEntryReader rejectedDataEntryReader;
+    private final DataEntryFilter dataEntryFilter;
+    private final DataEntryReader acceptedDataEntryReader;
+    private final DataEntryReader rejectedDataEntryReader;
 
 
     /**
diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/src/proguard/io/FilteredDataEntryWriter.java
index 8bc8ae1..af6d3bf 100644
--- a/src/proguard/io/FilteredDataEntryWriter.java
+++ b/src/proguard/io/FilteredDataEntryWriter.java
@@ -1,6 +1,6 @@
-/* $Id: FilteredDataEntryWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import java.io.*;
  */
 public class FilteredDataEntryWriter implements DataEntryWriter
 {
-    private DataEntryFilter dataEntryFilter;
+    private final DataEntryFilter dataEntryFilter;
     private DataEntryWriter acceptedDataEntryWriter;
     private DataEntryWriter rejectedDataEntryWriter;
 
diff --git a/src/proguard/io/Finisher.java b/src/proguard/io/Finisher.java
index c1ade30..c1ec5ad 100644
--- a/src/proguard/io/Finisher.java
+++ b/src/proguard/io/Finisher.java
@@ -1,6 +1,6 @@
-/* $Id: Finisher.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.io;
 
-import java.io.*;
+import java.io.IOException;
 
 /**
  * This interface specifies a listener that is called to finish an output stream
diff --git a/src/proguard/io/JarReader.java b/src/proguard/io/JarReader.java
index b66ee53..c9bcf4e 100644
--- a/src/proguard/io/JarReader.java
+++ b/src/proguard/io/JarReader.java
@@ -1,6 +1,6 @@
-/* $Id: JarReader.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,7 +20,7 @@
  */
 package proguard.io;
 
-import java.io.*;
+import java.io.IOException;
 import java.util.zip.*;
 
 /**
@@ -31,7 +31,7 @@ import java.util.zip.*;
  */
 public class JarReader implements DataEntryReader
 {
-    DataEntryReader dataEntryReader;
+    private final DataEntryReader dataEntryReader;
 
 
     /**
diff --git a/src/proguard/io/JarWriter.java b/src/proguard/io/JarWriter.java
index 3277ba2..2612133 100644
--- a/src/proguard/io/JarWriter.java
+++ b/src/proguard/io/JarWriter.java
@@ -1,6 +1,6 @@
-/* $Id: JarWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,8 +21,8 @@
 package proguard.io;
 
 import java.io.*;
-import java.util.jar.*;
 import java.util.*;
+import java.util.jar.*;
 import java.util.zip.*;
 
 
@@ -34,17 +34,17 @@ import java.util.zip.*;
  */
 public class JarWriter implements DataEntryWriter, Finisher
 {
-    private DataEntryWriter dataEntryWriter;
-    private Manifest        manifest;
-    private String          comment;
+    private final DataEntryWriter dataEntryWriter;
+    private final Manifest        manifest;
+    private final String          comment;
 
     private OutputStream    currentParentOutputStream;
     private ZipOutputStream currentJarOutputStream;
     private Finisher        currentFinisher;
-    private String          currentEntryName;
+    private DataEntry       currentDataEntry;
 
     // The names of the jar entries that are already in the jar.
-    private Set jarEntryNames = new HashSet();
+    private final Set jarEntryNames = new HashSet();
 
 
     /**
@@ -108,15 +108,15 @@ public class JarWriter implements DataEntryWriter, Finisher
             }
         }
 
-        // Get the entry name.
-        String name = dataEntry.getName();
-
         // Do we need a new entry?
-        if (!name.equals(currentEntryName))
+        if (!dataEntry.equals(currentDataEntry))
         {
             // Close the previous ZIP entry, if any.
             closeEntry();
 
+            // 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).
@@ -129,7 +129,7 @@ public class JarWriter implements DataEntryWriter, Finisher
             currentJarOutputStream.putNextEntry(new ZipEntry(name));
 
             currentFinisher  = finisher;
-            currentEntryName = name;
+            currentDataEntry = dataEntry;
         }
 
         return currentJarOutputStream;
@@ -167,7 +167,7 @@ public class JarWriter implements DataEntryWriter, Finisher
      */
     private void closeEntry() throws IOException
     {
-        if (currentEntryName != null)
+        if (currentDataEntry != null)
         {
             // Let any finisher finish up first.
             if (currentFinisher != null)
@@ -177,7 +177,7 @@ public class JarWriter implements DataEntryWriter, Finisher
             }
 
             currentJarOutputStream.closeEntry();
-            currentEntryName = null;
+            currentDataEntry = null;
         }
     }
 }
diff --git a/src/proguard/io/ParentDataEntryWriter.java b/src/proguard/io/ParentDataEntryWriter.java
index 09f5674..f9fc681 100644
--- a/src/proguard/io/ParentDataEntryWriter.java
+++ b/src/proguard/io/ParentDataEntryWriter.java
@@ -1,6 +1,6 @@
-/* $Id: ParentDataEntryWriter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -63,5 +63,6 @@ public class ParentDataEntryWriter implements DataEntryWriter
     public void close() throws IOException
     {
         dataEntryWriter.close();
+        dataEntryWriter = null;
     }
 }
diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/io/RenamedDataEntry.java
index 61163f5..1d31b8d 100644
--- a/src/proguard/io/RenamedDataEntry.java
+++ b/src/proguard/io/RenamedDataEntry.java
@@ -1,6 +1,6 @@
-/* $Id: RenamedDataEntry.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -30,8 +30,8 @@ import java.io.*;
  */
 public class RenamedDataEntry implements DataEntry
 {
-    private DataEntry dataEntry;
-    private String    name;
+    private final DataEntry dataEntry;
+    private final String    name;
 
 
     public RenamedDataEntry(DataEntry dataEntry,
diff --git a/src/proguard/io/ZipDataEntry.java b/src/proguard/io/ZipDataEntry.java
index 0afd9a7..0c722e1 100644
--- a/src/proguard/io/ZipDataEntry.java
+++ b/src/proguard/io/ZipDataEntry.java
@@ -1,6 +1,6 @@
-/* $Id: ZipDataEntry.java,v 1.4.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,11 @@
  */
 package proguard.io;
 
+import proguard.classfile.ClassConstants;
+
 import java.io.*;
 import java.util.zip.*;
 
-import proguard.classfile.ClassConstants;
-
 /**
  * This <code>DataEntry</code> represents a ZIP entry.
  *
@@ -32,8 +32,8 @@ import proguard.classfile.ClassConstants;
  */
 public class ZipDataEntry implements DataEntry
 {
-    private DataEntry      parent;
-    private ZipEntry       zipEntry;
+    private final DataEntry      parent;
+    private final ZipEntry       zipEntry;
     private ZipInputStream zipInputStream;
 
 
diff --git a/src/proguard/obfuscate/AttributeShrinker.java b/src/proguard/obfuscate/AttributeShrinker.java
index b5c8851..12ab5e9 100644
--- a/src/proguard/obfuscate/AttributeShrinker.java
+++ b/src/proguard/obfuscate/AttributeShrinker.java
@@ -1,6 +1,6 @@
-/* $Id: AttributeShrinker.java,v 1.17.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,110 +22,71 @@ package proguard.obfuscate;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.*;
 
 /**
- * This ClassFileVisitor removes attributes that are not marked
- * as being used or required.
+ * This ClassVisitor removes attributes that are not marked as being used or
+ * required.
  *
  * @see AttributeUsageMarker
  *
  * @author Eric Lafortune
  */
 public class AttributeShrinker
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             AttributeVisitor
 {
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Compact the array for class attributes.
-        programClassFile.u2attributesCount =
-            shrinkArray(programClassFile.attributes,
-                        programClassFile.u2attributesCount);
+        programClass.u2attributesCount =
+            shrinkArray(programClass.attributes,
+                        programClass.u2attributesCount);
 
         // Compact the attributes in fields, methods, and class attributes,
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-        programClassFile.attributesAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+        programClass.attributesAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        // Library class files are left unchanged.
+        // Library classes are left unchanged.
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
+    // Implementations for MemberVisitor.
 
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
     {
         // Compact the attributes array.
-        programMemberInfo.u2attributesCount =
-            shrinkArray(programMemberInfo.attributes,
-                        programMemberInfo.u2attributesCount);
+        programMember.u2attributesCount =
+            shrinkArray(programMember.attributes,
+                        programMember.u2attributesCount);
 
         // Compact any attributes of the remaining attributes.
-        programMemberInfo.attributesAccept(programClassFile, this);
+        programMember.attributesAccept(programClass, this);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Library class files are left unchanged.
-    }
+    // Implementations for AttributeVisitor.
 
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Library class files are left unchanged.
-    }
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         // Compact the attributes array.
-        codeAttrInfo.u2attributesCount =
-            shrinkArray(codeAttrInfo.attributes,
-                        codeAttrInfo.u2attributesCount);
+        codeAttribute.u2attributesCount =
+            shrinkArray(codeAttribute.attributes,
+                        codeAttribute.u2attributesCount);
     }
 
 
diff --git a/src/proguard/obfuscate/AttributeUsageMarker.java b/src/proguard/obfuscate/AttributeUsageMarker.java
index 50b5037..85206c0 100644
--- a/src/proguard/obfuscate/AttributeUsageMarker.java
+++ b/src/proguard/obfuscate/AttributeUsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: AttributeUsageMarker.java,v 1.24.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,360 +21,30 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
-import proguard.util.*;
-
-import java.util.*;
-
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This ClassFileVisitor marks all attributes that should be kept in the classes
- * it visits.
+ * This ClassVisitor marks all attributes that it visits.
  *
  * @see AttributeShrinker
  *
  * @author Eric Lafortune
  */
 public class AttributeUsageMarker
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
-             InnerClassesInfoVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor
 {
     // A visitor info flag to indicate the attribute is being used.
     private static final Object USED = new Object();
 
 
-    // Flags to specify whether optional attributes should be kept anyway.
-    private boolean       keepAllAttributes;
-    private boolean       keepAllUnknownAttributes;
-    private boolean       keepAllKnownAttributes;
-    private StringMatcher keepAttributes;
-
-    private boolean       keepInnerClassNameAttribute;
-    private boolean       keepEnclosingMethodAttribute;
-    private boolean       keepLineNumberTableAttribute;
-    private boolean       keepLocalVariableTableAttribute;
-    private boolean       keepLocalVariableTypeTableAttribute;
-    private boolean       keepSourceFileAttribute;
-    private boolean       keepSourceDirAttribute;
-    private boolean       keepDeprecatedAttribute;
-    private boolean       keepSyntheticAttribute;
-    private boolean       keepSignatureAttribute;
-    private boolean       keepRuntimeVisibleAnnotationsAttribute;
-    private boolean       keepRuntimeInvisibleAnnotationsAttribute;
-    private boolean       keepRuntimeVisibleParameterAnnotationsAttribute;
-    private boolean       keepRuntimeInvisibleParameterAnnotationsAttribute;
-    private boolean       keepAnnotationDefaultAttribute;
-
-
-    /**
-     * Specifies to keep all optional attributes.
-     */
-    public void setKeepAllAttributes()
-    {
-        keepAllAttributes = true;
-    }
-
-    /**
-     * Specifies to keep all unknown attributes.
-     */
-    public void setKeepAllUnknownAttributes()
-    {
-        keepAllUnknownAttributes = true;
-    }
-
-    /**
-     * Specifies to keep all known attributes.
-     */
-    public void setKeepAllKnownAttributes()
-    {
-        keepAllKnownAttributes = true;
-    }
-
-
-    /**
-     * Specifies to keep optional attributes with the given names. The attribute
-     * names may contain "*" or "?" wildcards, and they may be preceded by the
-     * "!" negator.
-     */
-    public void setKeepAttributes(List attributeNames)
-    {
-        keepAttributes = new BasicListMatcher(attributeNames);
-
-        // Precompute whether the list of attribute names matches the supported
-        // attributes.
-        keepInnerClassNameAttribute                       = keepAttributes.matches(ClassConstants.ATTR_InnerClasses);
-        keepEnclosingMethodAttribute                      = keepAttributes.matches(ClassConstants.ATTR_EnclosingMethod);
-        keepLineNumberTableAttribute                      = keepAttributes.matches(ClassConstants.ATTR_LineNumberTable);
-        keepLocalVariableTableAttribute                   = keepAttributes.matches(ClassConstants.ATTR_LocalVariableTable);
-        keepLocalVariableTypeTableAttribute               = keepAttributes.matches(ClassConstants.ATTR_LocalVariableTypeTable);
-        keepSourceFileAttribute                           = keepAttributes.matches(ClassConstants.ATTR_SourceFile);
-        keepSourceDirAttribute                            = keepAttributes.matches(ClassConstants.ATTR_SourceDir);
-        keepDeprecatedAttribute                           = keepAttributes.matches(ClassConstants.ATTR_Deprecated);
-        keepSyntheticAttribute                            = keepAttributes.matches(ClassConstants.ATTR_Synthetic);
-        keepSignatureAttribute                            = keepAttributes.matches(ClassConstants.ATTR_Signature);
-        keepRuntimeVisibleAnnotationsAttribute            = keepAttributes.matches(ClassConstants.ATTR_RuntimeVisibleAnnotations);
-        keepRuntimeInvisibleAnnotationsAttribute          = keepAttributes.matches(ClassConstants.ATTR_RuntimeInvisibleAnnotations);
-        keepRuntimeVisibleParameterAnnotationsAttribute   = keepAttributes.matches(ClassConstants.ATTR_RuntimeVisibleParameterAnnotations);
-        keepRuntimeInvisibleParameterAnnotationsAttribute = keepAttributes.matches(ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations);
-        keepAnnotationDefaultAttribute                    = keepAttributes.matches(ClassConstants.ATTR_AnnotationDefault);
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Mark the class member attributes that should be kept.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-
-        // Mark the class attributes that should be kept.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
-    {
-        // Mark the class member attributes that should be kept.
-        programMemberInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
-    {
-        if (keepAllAttributes ||
-            keepAllUnknownAttributes ||
-            (keepAttributes != null &&
-             keepAttributes.matches(unknownAttrInfo.getAttributeName(classFile))))
-        {
-            markAsUsed(unknownAttrInfo);
-        }
-    }
-
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        markAsUsed(innerClassesAttrInfo);
-
-        if (!keepAllAttributes &&
-            !keepAllKnownAttributes &&
-            !keepInnerClassNameAttribute)
-        {
-            // Clear references to the original inner class names.
-            innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
-        }
-    }
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepEnclosingMethodAttribute)
-        {
-            markAsUsed(enclosingMethodAttrInfo);
-        }
-    }
-
-
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
-    {
-        markAsUsed(constantValueAttrInfo);
-    }
-
-
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
-    {
-        markAsUsed(exceptionsAttrInfo);
-    }
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        markAsUsed(codeAttrInfo);
-
-        // Mark the code attributes that should be kept.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepLineNumberTableAttribute)
-        {
-            markAsUsed(lineNumberTableAttrInfo);
-        }
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepLocalVariableTableAttribute)
-        {
-            markAsUsed(localVariableTableAttrInfo);
-        }
-    }
-
-
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepLocalVariableTypeTableAttribute)
-        {
-            markAsUsed(localVariableTypeTableAttrInfo);
-        }
-    }
-
-
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepSourceFileAttribute)
-        {
-            markAsUsed(sourceFileAttrInfo);
-        }
-    }
-
-
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepSourceDirAttribute)
-        {
-            markAsUsed(sourceDirAttrInfo);
-        }
-    }
-
-
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepDeprecatedAttribute)
-        {
-            markAsUsed(deprecatedAttrInfo);
-        }
-    }
-
-
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepSyntheticAttribute)
-        {
-            markAsUsed(syntheticAttrInfo);
-        }
-    }
-
-
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepSignatureAttribute)
-        {
-            markAsUsed(signatureAttrInfo);
-        }
-    }
-
-
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepRuntimeVisibleAnnotationsAttribute)
-        {
-            markAsUsed(runtimeVisibleAnnotationsAttrInfo);
-        }
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepRuntimeInvisibleAnnotationsAttribute)
-        {
-            markAsUsed(runtimeInvisibleAnnotationsAttrInfo);
-        }
-    }
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepRuntimeVisibleParameterAnnotationsAttribute)
-        {
-            markAsUsed(runtimeVisibleParameterAnnotationsAttrInfo);
-        }
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepRuntimeInvisibleParameterAnnotationsAttribute)
-        {
-            markAsUsed(runtimeInvisibleParameterAnnotationsAttrInfo);
-        }
-    }
-
-
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
-    {
-        if (keepAllAttributes      ||
-            keepAllKnownAttributes ||
-            keepAnnotationDefaultAttribute)
-        {
-            markAsUsed(annotationDefaultAttrInfo);
-        }
-    }
-
-
-    // Implementations for InnerClassesInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute)
     {
-        // Clear the reference to the original inner class name, as used in
-        // the source code.
-        innerClassesInfo.u2innerNameIndex = 0;
+        markAsUsed(attribute);
     }
 
 
@@ -382,7 +52,7 @@ public class AttributeUsageMarker
 
     /**
      * Marks the given VisitorAccepter as being used (or useful).
-     * In this context, the VisitorAccepter will be an AttrInfo object.
+     * In this context, the VisitorAccepter will be an Attribute object.
      */
     private static void markAsUsed(VisitorAccepter visitorAccepter)
     {
@@ -392,7 +62,7 @@ public class AttributeUsageMarker
 
     /**
      * Returns whether the given VisitorAccepter has been marked as being used.
-     * In this context, the VisitorAccepter will be an AttrInfo object.
+     * In this context, the VisitorAccepter will be an Attribute object.
      */
     static boolean isUsed(VisitorAccepter visitorAccepter)
     {
diff --git a/src/proguard/obfuscate/ClassFileObfuscator.java b/src/proguard/obfuscate/ClassFileObfuscator.java
deleted file mode 100644
index 43d2252..0000000
--- a/src/proguard/obfuscate/ClassFileObfuscator.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/* $Id: ClassFileObfuscator.java,v 1.24.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-
-import java.util.*;
-
-
-/**
- * This <code>ClassFileVisitor</code> comes up with obfuscated names for the
- * class files it visits, and for their class members. The actual renaming is
- * done afterward.
- *
- * @see ClassFileRenamer
- *
- * @author Eric Lafortune
- */
-public class ClassFileObfuscator
-  implements ClassFileVisitor
-{
-    private String  defaultPackageName;
-    private boolean useMixedCaseClassNames;
-
-    // Map: [package name - class name factory]
-    private final Map         packageMap = new HashMap();
-    private final NameFactory defaultPackageClassNameFactory;
-    private final Set         namesToAvoid = new HashSet();
-
-
-    /**
-     * Creates a new ClassFileObfuscator.
-     * @param programClassPool       the class pool in which class names have
-     *                               to be unique.
-     * @param defaultPackageName     the package in which all classes that don't
-     *                               have fixed names will be put, or
-     *                               <code>null</code>, if all classes can
-     *                               remain in their original packages.
-     * @param useMixedCaseClassNames specifies whether to use mixed case
-     *                               class names.
-     */
-    public ClassFileObfuscator(ClassPool programClassPool,
-                               String    defaultPackageName,
-                               boolean   useMixedCaseClassNames)
-    {
-        this.defaultPackageName             = defaultPackageName;
-        this.useMixedCaseClassNames         = useMixedCaseClassNames;
-        this.defaultPackageClassNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
-
-        // Collect all names that have been taken already.
-        programClassPool.classFilesAccept(new ClassFileVisitor()
-        {
-            public void visitProgramClassFile(ProgramClassFile programClassFile)
-            {
-                String newClassName = newClassName(programClassFile);
-                if (newClassName != null)
-                {
-                    namesToAvoid.add(newClassName);
-                }
-            }
-
-            public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-            {
-            }
-        });
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Does this class file still need a new name?
-        if (newClassName(programClassFile) == null)
-        {
-            // Figure out a new name.
-            String className   = programClassFile.getName();
-            String packageName = ClassUtil.internalPackageName(className);
-
-            String newPackageName = packageName;
-
-            // Find the right name factory for this package, or use the default.
-            NameFactory packageClassNameFactory = (NameFactory)packageMap.get(packageName);
-            if (packageClassNameFactory == null)
-            {
-                // Do we have a default package name?
-                if (defaultPackageName == null)
-                {
-                    // We haven't seen this package before. Create a new name factory
-                    // for it.
-                    packageClassNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
-                    packageMap.put(packageName, packageClassNameFactory);
-                }
-                else
-                {
-                    // Fall back on the default package class name factory and name.
-                    packageClassNameFactory = defaultPackageClassNameFactory;
-                    newPackageName          = defaultPackageName;
-                }
-            }
-
-            // Come up with class names until we get an original one.
-            String newClassName;
-            do
-            {
-                // Let the factory produce a class name.
-                newClassName = packageClassNameFactory.nextName();
-
-                // We may have to add a package part to the class name.
-                if (newPackageName.length() > 0)
-                {
-                    newClassName =
-                        newPackageName +
-                        ClassConstants.INTERNAL_PACKAGE_SEPARATOR +
-                        newClassName;
-                }
-
-            }
-            while (namesToAvoid.contains(newClassName));
-
-            setNewClassName(programClassFile, newClassName);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Assigns a new name to the given class file.
-     * @param classFile the given class file.
-     * @param name      the new name.
-     */
-    static void setNewClassName(ClassFile classFile, String name)
-    {
-        classFile.setVisitorInfo(name);
-    }
-
-
-
-    /**
-     * Retrieves the new name of the given class file.
-     * @param classFile the given class file.
-     * @return the class file's new name, or <code>null</code> if it doesn't
-     *         have one yet.
-     */
-    static String newClassName(ClassFile classFile)
-    {
-        Object visitorInfo = classFile.getVisitorInfo();
-
-        return visitorInfo instanceof String ?
-            (String)visitorInfo :
-            null;
-    }
-}
diff --git a/src/proguard/obfuscate/ClassFileOpener.java b/src/proguard/obfuscate/ClassFileOpener.java
deleted file mode 100644
index 1a2c3fb..0000000
--- a/src/proguard/obfuscate/ClassFileOpener.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/* $Id: ClassFileOpener.java,v 1.2.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.*;
-import proguard.classfile.editor.ConstantPoolEditor;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This <code>ClassFileVisitor</code> makes package visible classes and class
- * members public.
- *
- * @author Eric Lafortune
- */
-public class ClassFileOpener
-  implements ClassFileVisitor,
-             MemberInfoVisitor
-{
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Make the class public, if it is package visible.
-        makePackageVisible(programClassFile);
-
-        // Make package visible class members public.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        makePackageVisible(programFieldInfo);
-    }
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        makePackageVisible(programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Small utility methods.
-
-    /**
-     * Makes the given class file public, if it is package visible.
-     */
-    private void makePackageVisible(ProgramClassFile programClassFile)
-    {
-        if (isPackageVisible(programClassFile.u2accessFlags))
-        {
-            programClassFile.u2accessFlags = makePublic(programClassFile.u2accessFlags);
-        }
-    }
-
-    /**
-     * Makes the given class member public, if it is package visible.
-     */
-    private void makePackageVisible(ProgramMemberInfo programMemberInfo)
-    {
-        if (isPackageVisible(programMemberInfo.u2accessFlags))
-        {
-            programMemberInfo.u2accessFlags = makePublic(programMemberInfo.u2accessFlags);
-        }
-    }
-
-
-    /**
-     * Returns whether the given access flags specify a package visible class
-     * or class member (including public or protected access).
-     */
-    private boolean isPackageVisible(int accessFlags)
-    {
-        return AccessUtil.accessLevel(accessFlags) >= AccessUtil.PACKAGE_VISIBLE;
-    }
-
-
-    /**
-     * Returns the given access flags, modified such that the class or class
-     * member becomes public.
-     */
-    private int makePublic(int accessFlags)
-    {
-        return AccessUtil.replaceAccessFlags(accessFlags,
-                                             ClassConstants.INTERNAL_ACC_PUBLIC);
-    }
-}
diff --git a/src/proguard/obfuscate/ClassFileRenamer.java b/src/proguard/obfuscate/ClassFileRenamer.java
deleted file mode 100644
index 870e3bf..0000000
--- a/src/proguard/obfuscate/ClassFileRenamer.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/* $Id: ClassFileRenamer.java,v 1.42.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 library; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.*;
-import proguard.classfile.editor.ConstantPoolEditor;
-import proguard.classfile.visitor.*;
-
-/**
- * This <code>ClassFileVisitor</code> renames the class names and class member
- * names of the classes it visits, using names previously determined by the
- * obfuscator.
- *
- * @see ClassFileObfuscator
- *
- * @author Eric Lafortune
- */
-public class ClassFileRenamer
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             CpInfoVisitor
-{
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Rename this class.
-        programClassFile.constantPoolEntryAccept(programClassFile.u2thisClass, this);
-
-        // Rename the class members.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        libraryClassFile.thisClassName = ClassFileObfuscator.newClassName(libraryClassFile);
-
-        // Rename the class members.
-        libraryClassFile.fieldsAccept(this);
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        renameProgramMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        renameProgramMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        renameLibraryMemberInfo(libraryClassFile, libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        renameLibraryMemberInfo(libraryClassFile, libraryMethodInfo);
-    }
-
-
-    /**
-     * Renames the given program member info, if necessary.
-     */
-    private void renameProgramMemberInfo(ProgramClassFile  programClassFile,
-                                         ProgramMemberInfo programMemberInfo)
-    {
-        // Has the class member name changed?
-        String name    = programMemberInfo.getName(programClassFile);
-        String newName = MemberInfoObfuscator.newMemberName(programMemberInfo);
-        if (newName != null &&
-            !newName.equals(name))
-        {
-            programMemberInfo.u2nameIndex =
-                constantPoolEditor.addUtf8CpInfo(programClassFile, newName);
-        }
-    }
-
-
-    /**
-     * Renames the given library member info.
-     */
-    private void renameLibraryMemberInfo(LibraryClassFile  libraryClassFile,
-                                         LibraryMemberInfo libraryMemberInfo)
-    {
-        String newName = MemberInfoObfuscator.newMemberName(libraryMemberInfo);
-        if (newName != null)
-        {
-            libraryMemberInfo.name = newName;
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-
-
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
-    {
-        // Update the Class entry if required.
-        String newName = ClassFileObfuscator.newClassName(classFile);
-        if (newName != null)
-        {
-            // Refer to a new Utf8 entry.
-            classCpInfo.u2nameIndex =
-                constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                                 newName);
-        }
-    }
-}
diff --git a/src/proguard/obfuscate/ClassObfuscator.java b/src/proguard/obfuscate/ClassObfuscator.java
new file mode 100644
index 0000000..3833551
--- /dev/null
+++ b/src/proguard/obfuscate/ClassObfuscator.java
@@ -0,0 +1,390 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.ClassConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.ClassVisitor;
+
+import java.util.*;
+
+/**
+ * This <code>ClassVisitor</code> comes up with obfuscated names for the
+ * classes it visits, and for their class members. The actual renaming is
+ * done afterward.
+ *
+ * @see ClassRenamer
+ *
+ * @author Eric Lafortune
+ */
+public class ClassObfuscator
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             AttributeVisitor,
+             InnerClassesInfoVisitor,
+             ConstantVisitor
+{
+    private final boolean useMixedCaseClassNames;
+    private final String  flattenPackageHierarchy;
+    private final String  repackageClasses;
+    private final boolean allowAccessModification;
+
+    private final Set classNamesToAvoid                  = new HashSet();
+
+    // Map: [package prefix - new package prefix]
+    private final Map packagePrefixMap                   = new HashMap();
+
+    // Map: [package prefix - package name factory]
+    private final Map packagePrefixPackageNameFactoryMap = new HashMap();
+
+    // Map: [package prefix - 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;
+
+
+    /**
+     * Creates a new ClassObfuscator.
+     * @param programClassPool        the class pool in which class names
+     *                                have to be unique.
+     * @param useMixedCaseClassNames  specifies whether obfuscated packages
+     *                                and classes can get mixed-case names.
+     * @param flattenPackageHierarchy the base package if the obfuscated
+     *                                package hierarchy is to be flattened.
+     * @param repackageClasses        the base package if the obfuscated
+     *                                classes are to be repackaged.
+     * @param allowAccessModification specifies whether obfuscated classes
+     *                                can be freely moved between packages.
+     */
+    public ClassObfuscator(ClassPool programClassPool,
+                           boolean   useMixedCaseClassNames,
+                           String    flattenPackageHierarchy,
+                           String    repackageClasses,
+                           boolean   allowAccessModification)
+    {
+        // First append the package separator if necessary.
+        if (flattenPackageHierarchy != null &&
+            flattenPackageHierarchy.length() > 0)
+        {
+            flattenPackageHierarchy += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+        }
+
+        // First append the package separator if necessary.
+        if (repackageClasses != null &&
+            repackageClasses.length() > 0)
+        {
+            repackageClasses += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+        }
+
+        this.useMixedCaseClassNames  = useMixedCaseClassNames;
+        this.flattenPackageHierarchy = flattenPackageHierarchy;
+        this.repackageClasses        = repackageClasses;
+        this.allowAccessModification = allowAccessModification;
+
+        // Map the root package onto the root package.
+        packagePrefixMap.put("", "");
+
+        // Collect all names that have been taken already.
+        programClassPool.classesAccept(new MyKeepCollector());
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Does this class still need a new name?
+        newClassName = newClassName(programClass);
+        if (newClassName == null)
+        {
+            // Make sure the outer class has a name, if it exists. The name will
+            // be stored as the new class name, as a side effect, so we'll be
+            // able to use it as a prefix.
+            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
+            // package, if classes are to be repackaged.
+            String newPackagePrefix = newClassName != null ?
+                newClassName + ClassConstants.INNER_CLASS_SEPARATOR :
+                newPackagePrefix(ClassUtil.internalPackagePrefix(programClass.getName()));
+
+            // Come up with a new class name.
+            newClassName = generateUniqueClassName(newPackagePrefix);
+
+            setNewClassName(programClass, newClassName);
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        // Make sure the outer classes have a name, if they exist.
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        // Make sure the enclosing class has a name.
+        enclosingMethodAttribute.referencedClassAccept(this);
+    }
+
+
+    // Implementations for InnerClassesInfoVisitor.
+
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
+    {
+        // Make sure the outer class has a name, if it exists.
+        int innerClassIndex = innerClassesInfo.u2innerClassIndex;
+        int outerClassIndex = innerClassesInfo.u2outerClassIndex;
+        if (innerClassIndex != 0 &&
+            outerClassIndex != 0 &&
+            clazz.getClassName(innerClassIndex).equals(clazz.getName()))
+        {
+            clazz.constantPoolEntryAccept(outerClassIndex, this);
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Make sure the outer class has a name.
+        classConstant.referencedClassAccept(this);
+    }
+
+
+    /**
+     * This ClassVisitor collects package names and class names that have to
+     * be kept.
+     */
+    private class MyKeepCollector implements ClassVisitor
+    {
+        public void visitProgramClass(ProgramClass programClass)
+        {
+            // Does the class already have a new name?
+            String newClassName = newClassName(programClass);
+            if (newClassName != null)
+            {
+                // Remember not to use this name.
+                classNamesToAvoid.add(mixedCaseClassName(newClassName));
+
+                // Are we not aggressively repackaging all obfuscated classes?
+                if (repackageClasses == null ||
+                    !allowAccessModification)
+                {
+                    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
+                    // repackaging.
+                    mapPackageName(className,
+                                   newClassName,
+                                   repackageClasses        == null &&
+                                   flattenPackageHierarchy == null);
+                }
+            }
+        }
+
+
+        public void visitLibraryClass(LibraryClass libraryClass)
+        {
+        }
+
+
+        /**
+         * Makes sure the package name of the given class will always be mapped
+         * consistently with its new name.
+         */
+        private void mapPackageName(String  className,
+                                    String  newClassName,
+                                    boolean recursively)
+        {
+            String packagePrefix    = ClassUtil.internalPackagePrefix(className);
+            String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName);
+
+            // Put the mapping of this package prefix, and possibly of its
+            // entire hierarchy, into the package prefix map.
+            do
+            {
+                packagePrefixMap.put(packagePrefix, newPackagePrefix);
+
+                if (!recursively)
+                {
+                    break;
+                }
+
+                packagePrefix    = ClassUtil.internalPackagePrefix(packagePrefix);
+                newPackagePrefix = ClassUtil.internalPackagePrefix(newPackagePrefix);
+            }
+            while (packagePrefix.length()    > 0 &&
+                   newPackagePrefix.length() > 0);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Finds or creates the new package prefix for the given package.
+     */
+    private String newPackagePrefix(String packagePrefix)
+    {
+        // Doesn't the package prefix have a new package prefix yet?
+        String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
+        if (newPackagePrefix == null)
+        {
+            // Are we forcing a new package prefix?
+            if (repackageClasses != null)
+            {
+                return repackageClasses;
+            }
+
+            // Are we forcing a new superpackage prefix?
+            // Othewrise figure out the new superpackage prefix, recursively.
+            String newSuperPackagePrefix = flattenPackageHierarchy != null ?
+                flattenPackageHierarchy :
+                newPackagePrefix(ClassUtil.internalPackagePrefix(packagePrefix));
+
+            // Come up with a new package prefix.
+            newPackagePrefix = generateUniquePackagePrefix(newSuperPackagePrefix);
+
+            // Remember to use this mapping in the future.
+            packagePrefixMap.put(packagePrefix, newPackagePrefix);
+        }
+
+        return newPackagePrefix;
+    }
+
+
+    /**
+     * Creates a new package prefix in the given new superpackage.
+     */
+    private String generateUniquePackagePrefix(String newSuperPackagePrefix)
+    {
+        // Find the right name factory for this package.
+        NameFactory packageNameFactory =
+            (NameFactory)packagePrefixPackageNameFactoryMap.get(newSuperPackagePrefix);
+        if (packageNameFactory == null)
+        {
+            // We haven't seen packages in this superpackage before. Create
+            // a new name factory for them.
+            packageNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
+            packagePrefixPackageNameFactoryMap.put(newSuperPackagePrefix,
+                                                   packageNameFactory);
+        }
+
+        // Come up with package names until we get an original one.
+        String newPackagePrefix;
+        do
+        {
+            // Let the factory produce a package name.
+            newPackagePrefix = newSuperPackagePrefix +
+                               packageNameFactory.nextName() +
+                               ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+        }
+        while (packagePrefixMap.containsValue(newPackagePrefix));
+
+        return newPackagePrefix;
+    }
+
+
+    /**
+     * Creates a new class name in the given new package.
+     */
+    private String generateUniqueClassName(String newPackagePrefix)
+    {
+        // Find the right name factory for this package.
+        NameFactory classNameFactory =
+            (NameFactory)packagePrefixClassNameFactoryMap.get(newPackagePrefix);
+        if (classNameFactory == null)
+        {
+            // We haven't seen classes in this package before. Create a new name
+            // factory for them.
+            classNameFactory = new SimpleNameFactory(useMixedCaseClassNames);
+            packagePrefixClassNameFactoryMap.put(newPackagePrefix,
+                                                 classNameFactory);
+        }
+
+        // Come up with class names until we get an original one.
+        String newClassName;
+        do
+        {
+            // Let the factory produce a class name.
+            newClassName = newPackagePrefix +
+                           classNameFactory.nextName();
+        }
+        while (classNamesToAvoid.contains(mixedCaseClassName(newClassName)));
+
+        return newClassName;
+    }
+
+    /**
+     * Returns the given class name, unchanged if mixed-case class names are
+     * allowed, or the lower-case version otherwise.
+     */
+    private String mixedCaseClassName(String className)
+    {
+        return useMixedCaseClassNames ?
+            className :
+            className.toLowerCase();
+    }
+
+
+    /**
+     * Assigns a new name to the given class.
+     * @param clazz the given class.
+     * @param name  the new name.
+     */
+    static void setNewClassName(Clazz clazz, String name)
+    {
+        clazz.setVisitorInfo(name);
+    }
+
+
+    /**
+     * Retrieves the new name of the given class.
+     * @param clazz the given class.
+     * @return the class's new name, or <code>null</code> if it doesn't
+     *         have one yet.
+     */
+    static String newClassName(Clazz clazz)
+    {
+        Object visitorInfo = clazz.getVisitorInfo();
+
+        return visitorInfo instanceof String ?
+            (String)visitorInfo :
+            null;
+    }
+}
diff --git a/src/proguard/obfuscate/ClassOpener.java b/src/proguard/obfuscate/ClassOpener.java
new file mode 100644
index 0000000..57abe96
--- /dev/null
+++ b/src/proguard/obfuscate/ClassOpener.java
@@ -0,0 +1,127 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.*;
+
+/**
+ * This <code>ConstantVisitor</code> makes package visible classes and class
+ * members public, if they are referenced by visited references from different
+ * packages.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassOpener
+extends      SimplifiedVisitor
+implements   ConstantVisitor
+{
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        fixPackageVisibility(clazz,
+                             stringConstant.referencedClass,
+                             stringConstant.referencedMember);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        fixPackageVisibility(clazz,
+                             refConstant.referencedClass,
+                             refConstant.referencedMember);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Do we know the referenced class?
+        Clazz referencedClass = classConstant.referencedClass;
+        if (referencedClass != null)
+        {
+            int accessFlags = referencedClass.getAccessFlags();
+
+            // Make it public if necessary.
+            if (isNotPublic(accessFlags)                 &&
+                referencedClass instanceof ProgramClass  &&
+                inDifferentPackages(clazz, referencedClass))
+            {
+                ((ProgramClass)referencedClass).u2accessFlags =
+                    AccessUtil.replaceAccessFlags(accessFlags,
+                                                  ClassConstants.INTERNAL_ACC_PUBLIC);
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Fixes the package visibility of the given referenced class member,
+     * if necessary.
+     */
+    private void fixPackageVisibility(Clazz clazz, Clazz referencedClass, Member referencedMember)
+    {
+        // Do we know the referenced class member?
+        if (referencedMember != null)
+        {
+            int accessFlags = referencedMember.getAccessFlags();
+
+            // Make it public if necessary.
+            if (isNotPublic(accessFlags)                  &&
+                referencedMember instanceof ProgramMember &&
+                inDifferentPackages(clazz, referencedClass))
+            {
+                ((ProgramMember)referencedMember).u2accessFlags =
+                    AccessUtil.replaceAccessFlags(accessFlags,
+                                                  clazz.extends_(referencedClass) ?
+                                                  ClassConstants.INTERNAL_ACC_PROTECTED :
+                                                  ClassConstants.INTERNAL_ACC_PUBLIC);
+            }
+        }
+    }
+
+
+    /**
+     * Returns whether the given classes are in different packages..
+     */
+    private boolean inDifferentPackages(Clazz class1, Clazz class2)
+    {
+        return !ClassUtil.internalPackageName(class1.getName()).equals(
+                ClassUtil.internalPackageName(class2.getName()));
+    }
+
+
+    /**
+     * Returns whether the given access flags specify a non-public class
+     * or class member.
+     */
+    private boolean isNotPublic(int accessFlags)
+    {
+        return (accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) == 0;
+    }
+}
diff --git a/src/proguard/obfuscate/ClassRenamer.java b/src/proguard/obfuscate/ClassRenamer.java
new file mode 100644
index 0000000..b78bead
--- /dev/null
+++ b/src/proguard/obfuscate/ClassRenamer.java
@@ -0,0 +1,112 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.ClassConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.ConstantPoolEditor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This <code>ClassVisitor</code> renames the class names and class member
+ * names of the classes it visits, using names previously determined by the
+ * obfuscator.
+ *
+ * @see ClassObfuscator
+ *
+ * @author Eric Lafortune
+ */
+public class ClassRenamer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor
+{
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Rename this class.
+        programClass.constantPoolEntryAccept(programClass.u2thisClass, this);
+
+        // Rename the class members.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        libraryClass.thisClassName = ClassObfuscator.newClassName(libraryClass);
+
+        // Rename the class members.
+        libraryClass.fieldsAccept(this);
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass  programClass,
+                                     ProgramMember programMember)
+    {
+        // Has the class member name changed?
+        String name    = programMember.getName(programClass);
+        String newName = MemberObfuscator.newMemberName(programMember);
+        if (newName != null &&
+            !newName.equals(name))
+        {
+            programMember.u2nameIndex =
+                constantPoolEditor.addUtf8Constant(programClass, newName);
+        }
+    }
+
+    public void visitLibraryMember(LibraryClass  libraryClass,
+                                     LibraryMember libraryMember)
+    {
+        String newName = MemberObfuscator.newMemberName(libraryMember);
+        if (newName != null)
+        {
+            libraryMember.name = newName;
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Update the Class entry if required.
+        String newName = ClassObfuscator.newClassName(clazz);
+        if (newName != null)
+        {
+            // Refer to a new Utf8 entry.
+            classConstant.u2nameIndex =
+                constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                                   newName);
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/DictionaryNameFactory.java b/src/proguard/obfuscate/DictionaryNameFactory.java
index 4513d37..408e541 100644
--- a/src/proguard/obfuscate/DictionaryNameFactory.java
+++ b/src/proguard/obfuscate/DictionaryNameFactory.java
@@ -1,6 +1,6 @@
-/* $Id: DictionaryNameFactory.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/obfuscate/MapCleaner.java b/src/proguard/obfuscate/MapCleaner.java
index 6f85b98..ae81a28 100644
--- a/src/proguard/obfuscate/MapCleaner.java
+++ b/src/proguard/obfuscate/MapCleaner.java
@@ -1,6 +1,6 @@
-/* $Id: MapCleaner.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,18 +21,21 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.ClassFileVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 import java.util.Map;
 
 /**
- * This ClassFileVisitor clears a given map whenever it visits a class file.
+ * This ClassVisitor clears a given map whenever it visits a class.
  *
  * @author Eric Lafortune
  */
-public class MapCleaner implements ClassFileVisitor
+public class MapCleaner
+extends      SimplifiedVisitor
+implements   ClassVisitor
 {
-    private Map map;
+    private final Map map;
 
 
     /**
@@ -45,15 +48,9 @@ public class MapCleaner implements ClassFileVisitor
     }
 
 
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        map.clear();
-    }
-
+    // Implementations for ClassVisitor.
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitAnyClass(Clazz clazz)
     {
         map.clear();
     }
diff --git a/src/proguard/obfuscate/MappingKeeper.java b/src/proguard/obfuscate/MappingKeeper.java
index db1ba21..61e1144 100644
--- a/src/proguard/obfuscate/MappingKeeper.java
+++ b/src/proguard/obfuscate/MappingKeeper.java
@@ -1,6 +1,6 @@
-/* $Id: MappingKeeper.java,v 1.10.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -32,11 +32,11 @@ import proguard.classfile.util.*;
  */
 public class MappingKeeper implements MappingProcessor
 {
-    private ClassPool      classPool;
-    private WarningPrinter warningPrinter;
+    private final ClassPool      classPool;
+    private final WarningPrinter warningPrinter;
 
     // A field acting as a parameter.
-    private ClassFile classFile;
+    private Clazz clazz;
 
 
     /**
@@ -56,14 +56,14 @@ public class MappingKeeper implements MappingProcessor
 
     // Implementations for MappingProcessor.
 
-    public boolean processClassFileMapping(String className,
-                                           String newClassName)
+    public boolean processClassMapping(String className,
+                                       String newClassName)
     {
         // Find the class.
         String name = ClassUtil.internalClassName(className);
 
-        classFile = classPool.getClass(name);
-        if (classFile != null)
+        clazz = classPool.getClass(name);
+        if (clazz != null)
         {
             String newName = ClassUtil.internalClassName(newClassName);
 
@@ -71,7 +71,7 @@ public class MappingKeeper implements MappingProcessor
             // was set before.
             if (warningPrinter != null)
             {
-                String currentNewName = ClassFileObfuscator.newClassName(classFile);
+                String currentNewName = ClassObfuscator.newClassName(clazz);
                 if (currentNewName != null &&
                     !currentNewName.equals(newName))
                 {
@@ -84,8 +84,7 @@ public class MappingKeeper implements MappingProcessor
                 }
             }
 
-            // Make sure the mapping name will be kept.
-            ClassFileObfuscator.setNewClassName(classFile, newName);
+            ClassObfuscator.setNewClassName(clazz, newName);
 
             // The class members have to be kept as well.
             return true;
@@ -100,20 +99,20 @@ public class MappingKeeper implements MappingProcessor
                                     String fieldName,
                                     String newFieldName)
     {
-        if (classFile != null)
+        if (clazz != null)
         {
             // Find the field.
             String name       = fieldName;
             String descriptor = ClassUtil.internalType(fieldType);
 
-            FieldInfo fieldInfo = classFile.findField(name, descriptor);
-            if (fieldInfo != null)
+            Field field = clazz.findField(name, descriptor);
+            if (field != null)
             {
                 // Print out a warning if the mapping conflicts with a name that
                 // was set before.
                 if (warningPrinter != null)
                 {
-                    String currentNewName = MemberInfoObfuscator.newMemberName(fieldInfo);
+                    String currentNewName = MemberObfuscator.newMemberName(field);
                     if (currentNewName != null &&
                         !currentNewName.equals(newFieldName))
                     {
@@ -126,7 +125,7 @@ public class MappingKeeper implements MappingProcessor
                 }
 
                 // Make sure the mapping name will be kept.
-                MemberInfoObfuscator.setFixedNewMemberName(fieldInfo, newFieldName);
+                MemberObfuscator.setFixedNewMemberName(field, newFieldName);
             }
         }
     }
@@ -139,21 +138,21 @@ public class MappingKeeper implements MappingProcessor
                                      String methodNameAndArguments,
                                      String newMethodName)
     {
-        if (classFile != null)
+        if (clazz != null)
         {
             // Find the method.
             String name       = ClassUtil.externalMethodName(methodNameAndArguments);
             String descriptor = ClassUtil.internalMethodDescriptor(methodReturnType,
                                                                    methodNameAndArguments);
 
-            MethodInfo methodInfo = classFile.findMethod(name, descriptor);
-            if (methodInfo != null)
+            Method method = clazz.findMethod(name, descriptor);
+            if (method != null)
             {
                 // Print out a warning if the mapping conflicts with a name that
                 // was set before.
                 if (warningPrinter != null)
                 {
-                    String currentNewName = MemberInfoObfuscator.newMemberName(methodInfo);
+                    String currentNewName = MemberObfuscator.newMemberName(method);
                     if (currentNewName != null &&
                         !currentNewName.equals(newMethodName))
                     {
@@ -166,7 +165,7 @@ public class MappingKeeper implements MappingProcessor
                 }
 
                 // Make sure the mapping name will be kept.
-                MemberInfoObfuscator.setFixedNewMemberName(methodInfo, newMethodName);
+                MemberObfuscator.setFixedNewMemberName(method, newMethodName);
             }
         }
     }
diff --git a/src/proguard/obfuscate/MappingPrinter.java b/src/proguard/obfuscate/MappingPrinter.java
index 2717f9d..ae1cd9a 100644
--- a/src/proguard/obfuscate/MappingPrinter.java
+++ b/src/proguard/obfuscate/MappingPrinter.java
@@ -1,6 +1,6 @@
-/* $Id: MappingPrinter.java,v 1.19.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,22 +24,23 @@ import proguard.classfile.*;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
 
-import java.io.*;
+import java.io.PrintStream;
 
 
 /**
- * This ClassFileVisitor prints out the renamed class files and class members with
+ * This ClassVisitor prints out the renamed classes and class members with
  * their old names and new names.
  *
- * @see ClassFileRenamer
+ * @see ClassRenamer
  *
  * @author Eric Lafortune
  */
 public class MappingPrinter
-  implements ClassFileVisitor,
-             MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor
 {
-    private PrintStream ps;
+    private final PrintStream ps;
 
 
     /**
@@ -61,12 +62,12 @@ public class MappingPrinter
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        String name    = programClassFile.getName();
-        String newName = ClassFileObfuscator.newClassName(programClassFile);
+        String name    = programClass.getName();
+        String newName = ClassObfuscator.newClassName(programClass);
 
         ps.println(ClassUtil.externalClassName(name) +
                    " -> " +
@@ -74,75 +75,71 @@ public class MappingPrinter
                    ":");
 
         // Print out the class members.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        String newName = MemberInfoObfuscator.newMemberName(programFieldInfo);
+        String newName = MemberObfuscator.newMemberName(programField);
         if (newName != null)
         {
             ps.println("    " +
-                       //lineNumberRange(programClassFile, programFieldInfo) +
+                       //lineNumberRange(programClass, programField) +
                        ClassUtil.externalFullFieldDescription(
                            0,
-                           programFieldInfo.getName(programClassFile),
-                           programFieldInfo.getDescriptor(programClassFile)) +
+                           programField.getName(programClass),
+                           programField.getDescriptor(programClass)) +
                        " -> " +
                        newName);
         }
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
         // Special cases: <clinit> and <init> are always kept unchanged.
         // We can ignore them here.
-        String name = programMethodInfo.getName(programClassFile);
+        String name = programMethod.getName(programClass);
         if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
             name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
         {
             return;
         }
 
-        String newName = MemberInfoObfuscator.newMemberName(programMethodInfo);
+        String newName = MemberObfuscator.newMemberName(programMethod);
         if (newName != null)
         {
             ps.println("    " +
-                       lineNumberRange(programClassFile, programMethodInfo) +
+                       lineNumberRange(programClass, programMethod) +
                        ClassUtil.externalFullMethodDescription(
-                           programClassFile.getName(),
+                           programClass.getName(),
                            0,
-                           programMethodInfo.getName(programClassFile),
-                           programMethodInfo.getDescriptor(programClassFile)) +
+                           programMethod.getName(programClass),
+                           programMethod.getDescriptor(programClass)) +
                        " -> " +
                        newName);
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
     // Small utility methods.
 
     /**
      * Returns the line number range of the given class member, followed by a
      * colon, or just an empty String if no range is available.
      */
-    private static String lineNumberRange(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    private static String lineNumberRange(ProgramClass programClass, ProgramMember programMember)
     {
-        String range = programMemberInfo.getLineNumberRange(programClassFile);
+        String range = programMember.getLineNumberRange(programClass);
         return range != null ?
             (range + ":") :
             "";
diff --git a/src/proguard/obfuscate/MappingProcessor.java b/src/proguard/obfuscate/MappingProcessor.java
index 7255614..aa99e44 100644
--- a/src/proguard/obfuscate/MappingProcessor.java
+++ b/src/proguard/obfuscate/MappingProcessor.java
@@ -1,6 +1,6 @@
-/* $Id: MappingProcessor.java,v 1.5.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,7 +23,7 @@ package proguard.obfuscate;
 
 /**
  * This interface specifies methods to process name mappings between original
- * class files and their obfuscated versions. The mappings are typically read
+ * classes and their obfuscated versions. The mappings are typically read
  * from a mapping file.
  *
  * @see MappingReader
@@ -40,8 +40,8 @@ public interface MappingProcessor
      * @return whether the processor is interested in receiving mappings of the
      *         class members of this class.
      */
-    public boolean processClassFileMapping(String className,
-                                           String newClassName);
+    public boolean processClassMapping(String className,
+                                       String newClassName);
 
     /**
      * Processes the given field name mapping.
diff --git a/src/proguard/obfuscate/MappingReader.java b/src/proguard/obfuscate/MappingReader.java
index fa878af..d987919 100644
--- a/src/proguard/obfuscate/MappingReader.java
+++ b/src/proguard/obfuscate/MappingReader.java
@@ -1,6 +1,6 @@
-/* $Id: MappingReader.java,v 1.10.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -31,7 +31,7 @@ import java.io.*;
  */
 public class MappingReader
 {
-    private File mappingFile;
+    private final File mappingFile;
 
 
     public MappingReader(File mappingFile)
@@ -46,14 +46,11 @@ public class MappingReader
      */
     public void pump(MappingProcessor mappingProcessor) throws IOException
     {
-        LineNumberReader reader = null;
-
+        LineNumberReader reader = new LineNumberReader(
+                                  new BufferedReader(
+                                  new FileReader(mappingFile)));
         try
         {
-            reader = new LineNumberReader(
-                     new BufferedReader(
-                     new FileReader(mappingFile)));
-
             String className = null;
 
             // Read the subsequent class mappings and class member mappings.
@@ -66,13 +63,13 @@ public class MappingReader
                     break;
                 }
 
-                // The distinction between a class file mapping and a class
+                // The distinction between a class mapping and a class
                 // member mapping is the initial whitespace.
                 if (!line.startsWith("    "))
                 {
-                    // Process the class file mapping and remember the class's
+                    // Process the class mapping and remember the class's
                     // old name.
-                    className = processClassFileMapping(line, mappingProcessor);
+                    className = processClassMapping(line, mappingProcessor);
                 }
                 else if (className != null)
                 {
@@ -88,27 +85,25 @@ public class MappingReader
         }
         finally
         {
-            if (reader != null)
+            try
             {
-                try
-                {
-                    reader.close();
-                }
-                catch (IOException ex)
-                {
-                }
+                reader.close();
+            }
+            catch (IOException ex)
+            {
+                // This shouldn't happen.
             }
         }
     }
 
 
     /**
-     * Parses the given line with a class file mapping and processes the
+     * Parses the given line with a class mapping and processes the
      * results with the given mapping processor. Returns the old class name,
      * or null if any subsequent class member lines can be ignored.
      */
-    private String processClassFileMapping(String           line,
-                                           MappingProcessor mappingProcessor)
+    private String processClassMapping(String           line,
+                                       MappingProcessor mappingProcessor)
     {
         // See if we can parse "___ -> ___:", containing the original
         // class name and the new class name.
@@ -132,7 +127,7 @@ public class MappingReader
         String newClassName = line.substring(arrowIndex + 2, colonIndex).trim();
 
         // Process this class name mapping.
-        boolean interested = mappingProcessor.processClassFileMapping(className, newClassName);
+        boolean interested = mappingProcessor.processClassMapping(className, newClassName);
 
         return interested ? className : null;
     }
diff --git a/src/proguard/obfuscate/MemberInfoNameCleaner.java b/src/proguard/obfuscate/MemberInfoNameCleaner.java
deleted file mode 100644
index 0aeeb4c..0000000
--- a/src/proguard/obfuscate/MemberInfoNameCleaner.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/* $Id: MemberInfoNameCleaner.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
-
-import java.util.*;
-
-/**
- * This <code>MemberInfoVisitor</code> clears the new names of the class members
- * that it visits.
- *
- * @see MemberInfoLinker
- * @see MemberInfoObfuscator
- *
- * @author Eric Lafortune
- */
-public class MemberInfoNameCleaner implements MemberInfoVisitor
-{
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        MemberInfoObfuscator.setNewMemberName(programFieldInfo, null);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        MemberInfoObfuscator.setNewMemberName(programMethodInfo, null);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        MemberInfoObfuscator.setNewMemberName(libraryFieldInfo, null);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        MemberInfoObfuscator.setNewMemberName(libraryMethodInfo, null);
-    }
-}
diff --git a/src/proguard/obfuscate/MemberInfoNameConflictFixer.java b/src/proguard/obfuscate/MemberInfoNameConflictFixer.java
deleted file mode 100644
index 454550c..0000000
--- a/src/proguard/obfuscate/MemberInfoNameConflictFixer.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/* $Id: MemberInfoNameConflictFixer.java,v 1.1.2.2 2007/01/31 21:50:12 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This library is 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 library 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 Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
-import proguard.classfile.*;
-
-import java.util.*;
-
-/**
- * This MemberInfoVisitor solves obfuscation naming conflicts in all class
- * members that it visits. It avoids names from the given descriptor map,
- * delegating to the given obfuscator in order to get a new name if necessary.
- *
- * @author Eric Lafortune
- */
-public class MemberInfoNameConflictFixer implements MemberInfoVisitor
-{
-    private boolean              allowAggressiveOverloading;
-    private Map                  descriptorMap;
-    private WarningPrinter       warningPrinter;
-    private MemberInfoObfuscator memberInfoObfuscator;
-
-
-    /**
-     * Creates a new MemberInfoNameConflictFixer.
-     * @param allowAggressiveOverloading a flag that specifies whether class
-     *                                   members can be overloaded aggressively.
-     * @param descriptorMap              the map of descriptors to
-     *                                   [new name - old name] maps.
-     * @param warningPrinter             an optional warning printer to which
-     *                                   warnings about conflicting name
-     *                                   mappings can be printed.
-     * @param memberInfoObfuscator       the obfuscator that can assign new
-     *                                   names to members with conflicting
-     *                                   names.
-     */
-    public MemberInfoNameConflictFixer(boolean              allowAggressiveOverloading,
-                                       Map                  descriptorMap,
-                                       WarningPrinter       warningPrinter,
-                                       MemberInfoObfuscator memberInfoObfuscator)
-    {
-        this.allowAggressiveOverloading = allowAggressiveOverloading;
-        this.descriptorMap              = descriptorMap;
-        this.warningPrinter             = warningPrinter;
-        this.memberInfoObfuscator       = memberInfoObfuscator;
-    }
-
-
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo, true);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Special cases: <clinit> and <init> are always kept unchanged.
-        // We can ignore them here.
-        String name = programMethodInfo.getName(programClassFile);
-        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
-            name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
-        {
-            return;
-        }
-
-        visitMemberInfo(programClassFile, programMethodInfo, false);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    /**
-     * Obfuscates the given class member.
-     * @param classFile  the class file of the given member.
-     * @param memberInfo the class member to be obfuscated.
-     * @param isField    speficies whether the class member is a field.
-     */
-    private void visitMemberInfo(ClassFile  classFile,
-                                 MemberInfo memberInfo,
-                                 boolean    isField)
-    {
-        // Get the member's name and descriptor.
-        String name       = memberInfo.getName(classFile);
-        String descriptor = memberInfo.getDescriptor(classFile);
-
-        // Check whether we're allowed to overload aggressively.
-        if (!allowAggressiveOverloading)
-        {
-            // Trim the return argument from the descriptor if not.
-            // Works for fields and methods alike.
-            descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
-        }
-
-        // Get the name map, creating a new one if necessary.
-        Map nameMap = MemberInfoObfuscator.retrieveNameMap(descriptorMap, descriptor);
-
-        // Get the member's new name.
-        String newName = MemberInfoObfuscator.newMemberName(memberInfo);
-
-        String previousName = (String)nameMap.get(newName);
-        if (!name.equals(previousName))
-        {
-            // There's a conflict! A member (with a given old name) in a
-            // first namespace has received the same new name as this
-            // member (with a different old name) in a second name space,
-            // and now these two have to live together in this name space.
-            if (MemberInfoObfuscator.hasFixedNewMemberName(memberInfo) &&
-                warningPrinter != null)
-            {
-                descriptor = memberInfo.getDescriptor(classFile);
-                warningPrinter.print("Warning: " + ClassUtil.externalClassName(classFile.getName()) +
-                                     (isField ?
-                                         ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) :
-                                         ": method '" + ClassUtil.externalFullMethodDescription(classFile.getName(), 0, name, descriptor)) +
-                                     "' can't be mapped to '" + newName +
-                                     "' because it would conflict with " +
-                                     (isField ?
-                                         "field '" :
-                                         "method '" ) + previousName +
-                                     "', which is already being mapped to '" + newName + "'");
-            }
-
-            // Clear the conflicting name.
-            MemberInfoObfuscator.setNewMemberName(memberInfo, null);
-
-            // Assign a new name.
-            memberInfo.accept(classFile, memberInfoObfuscator);
-        }
-    }
-}
diff --git a/src/proguard/obfuscate/MemberInfoSpecialNameFilter.java b/src/proguard/obfuscate/MemberInfoSpecialNameFilter.java
deleted file mode 100644
index 2b2a8ed..0000000
--- a/src/proguard/obfuscate/MemberInfoSpecialNameFilter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/* $Id: MemberInfoSpecialNameFilter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.obfuscate;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
-
-import java.util.*;
-
-/**
- * This <code>MemberInfoVisitor</code> delegates its visits to another given
- * <code>MemberInfoVisitor</code>, but only when the visited member has a
- * special new name. A special name is a name that might have been produced by
- * a <code>SpecialNameFactory</code>.
- *
- * @see MemberInfoObfuscator
- * @see SpecialNameFactory
- *
- * @author Eric Lafortune
- */
-public class MemberInfoSpecialNameFilter implements MemberInfoVisitor
-{
-    private MemberInfoVisitor memberInfoVisitor;
-
-
-    /**
-     * Creates a new MemberInfoSpecialNameFilter.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
-     *                          visits will be delegated.
-     */
-    public MemberInfoSpecialNameFilter(MemberInfoVisitor memberInfoVisitor)
-    {
-        this.memberInfoVisitor = memberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        if (isSpecialName(programFieldInfo))
-        {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (isSpecialName(programMethodInfo))
-        {
-            memberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        if (isSpecialName(libraryFieldInfo))
-        {
-            memberInfoVisitor.visitLibraryFieldInfo(libraryClassFile, libraryFieldInfo);
-        }
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (isSpecialName(libraryMethodInfo))
-        {
-            memberInfoVisitor.visitLibraryMethodInfo(libraryClassFile, libraryMethodInfo);
-        }
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Returns whether the given class member has a special new name.
-     * @param memberInfo the class member.
-     */
-    private static boolean isSpecialName(MemberInfo memberInfo)
-    {
-        return SpecialNameFactory.isSpecialName(MemberInfoObfuscator.newMemberName(memberInfo));
-    }
-}
diff --git a/src/proguard/obfuscate/MemberNameCleaner.java b/src/proguard/obfuscate/MemberNameCleaner.java
new file mode 100644
index 0000000..43cc241
--- /dev/null
+++ b/src/proguard/obfuscate/MemberNameCleaner.java
@@ -0,0 +1,60 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This <code>MemberVisitor</code> clears the new names of the class members
+ * that it visits.
+ *
+ * @see MemberObfuscator
+ *
+ * @author Eric Lafortune
+ */
+public class MemberNameCleaner implements MemberVisitor
+{
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        MemberObfuscator.setNewMemberName(programField, null);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        MemberObfuscator.setNewMemberName(programMethod, null);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        MemberObfuscator.setNewMemberName(libraryField, null);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        MemberObfuscator.setNewMemberName(libraryMethod, null);
+    }
+}
diff --git a/src/proguard/obfuscate/MemberInfoNameCollector.java b/src/proguard/obfuscate/MemberNameCollector.java
similarity index 54%
rename from src/proguard/obfuscate/MemberInfoNameCollector.java
rename to src/proguard/obfuscate/MemberNameCollector.java
index 76640c7..9d23879 100644
--- a/src/proguard/obfuscate/MemberInfoNameCollector.java
+++ b/src/proguard/obfuscate/MemberNameCollector.java
@@ -1,6 +1,6 @@
-/* $Id: MemberInfoNameCollector.java,v 1.3.2.4 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,77 +21,49 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
-import proguard.classfile.util.MethodInfoLinker;
-import proguard.classfile.visitor.MemberInfoVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
 
 import java.util.Map;
 
 /**
- * This MemberInfoVisitor collects all new (obfuscation) names of the members
+ * This MemberVisitor collects all new (obfuscation) names of the members
  * that it visits.
  *
- * @see MethodInfoLinker
- * @see MemberInfoObfuscator
+ * @see MemberObfuscator
  *
  * @author Eric Lafortune
  */
-public class MemberInfoNameCollector implements MemberInfoVisitor
+public class MemberNameCollector
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private boolean allowAggressiveOverloading;
-    private Map     descriptorMap;
+    private final boolean allowAggressiveOverloading;
+    private final Map     descriptorMap;
 
 
     /**
-     * Creates a new MemberInfoNameCollector.
+     * Creates a new MemberNameCollector.
      * @param allowAggressiveOverloading a flag that specifies whether class
      *                                   members can be overloaded aggressively.
      * @param descriptorMap              the map of descriptors to
      *                                   [new name - old name] maps.
      */
-    public MemberInfoNameCollector(boolean allowAggressiveOverloading,
-                                   Map     descriptorMap)
+    public MemberNameCollector(boolean allowAggressiveOverloading,
+                               Map     descriptorMap)
     {
         this.allowAggressiveOverloading = allowAggressiveOverloading;
         this.descriptorMap              = descriptorMap;
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        collectName(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        collectName(programClassFile, programMethodInfo);
-    }
+    // Implementations for MemberVisitor.
 
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        collectName(libraryClassFile, libraryFieldInfo);
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        collectName(libraryClassFile, libraryMethodInfo);
-    }
-
-
-    /**
-     * Inserts the new name of the given class member into the map.
-     * @param classFile  the class file of the given member.
-     * @param memberInfo the class member to be linked.
-     */
-    private void collectName(ClassFile classFile, MemberInfo memberInfo)
+    public void visitAnyMember(Clazz clazz, Member member)
     {
         // Special cases: <clinit> and <init> are always kept unchanged.
         // We can ignore them here.
-        String name = memberInfo.getName(classFile);
+        String name = member.getName(clazz);
         if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
             name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
         {
@@ -99,13 +71,13 @@ public class MemberInfoNameCollector implements MemberInfoVisitor
         }
 
         // Get the member's new name.
-        String newName = MemberInfoObfuscator.newMemberName(memberInfo);
+        String newName = MemberObfuscator.newMemberName(member);
 
         // Remember it, if it has already been set.
         if (newName != null)
         {
             // Get the member's descriptor.
-            String descriptor = memberInfo.getDescriptor(classFile);
+            String descriptor = member.getDescriptor(clazz);
 
             // Check whether we're allowed to do aggressive overloading
             if (!allowAggressiveOverloading)
@@ -117,13 +89,13 @@ public class MemberInfoNameCollector implements MemberInfoVisitor
 
             // Put the [descriptor - new name] in the map,
             // creating a new [new name - old name] map if necessary.
-            Map nameMap = MemberInfoObfuscator.retrieveNameMap(descriptorMap, descriptor);
+            Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor);
 
             // Isn't there another original name for this new name, or should
             // this original name get priority?
-            Object otherName = nameMap.get(newName);
-            if (otherName == null                                      ||
-                MemberInfoObfuscator.hasFixedNewMemberName(memberInfo) ||
+            String otherName = (String)nameMap.get(newName);
+            if (otherName == null                              ||
+                MemberObfuscator.hasFixedNewMemberName(member) ||
                 name.compareTo(otherName) < 0)
             {
                 // Remember not to use the new name again in this name space.
diff --git a/src/proguard/obfuscate/MemberNameConflictFixer.java b/src/proguard/obfuscate/MemberNameConflictFixer.java
new file mode 100644
index 0000000..7921486
--- /dev/null
+++ b/src/proguard/obfuscate/MemberNameConflictFixer.java
@@ -0,0 +1,158 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+import java.util.Map;
+
+/**
+ * This MemberInfoVisitor solves obfuscation naming conflicts in all class
+ * members that it visits. It avoids names from the given descriptor map,
+ * delegating to the given obfuscator in order to get a new name if necessary.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberNameConflictFixer implements MemberVisitor
+{
+    private final boolean          allowAggressiveOverloading;
+    private final Map              descriptorMap;
+    private final WarningPrinter   warningPrinter;
+    private final MemberObfuscator memberObfuscator;
+
+
+    /**
+     * Creates a new MemberNameConflictFixer.
+     * @param allowAggressiveOverloading a flag that specifies whether class
+     *                                   members can be overloaded aggressively.
+     * @param descriptorMap              the map of descriptors to
+     *                                   [new name - old name] maps.
+     * @param warningPrinter             an optional warning printer to which
+     *                                   warnings about conflicting name
+     *                                   mappings can be printed.
+     * @param memberObfuscator           the obfuscator that can assign new
+     *                                   names to members with conflicting
+     *                                   names.
+     */
+    public MemberNameConflictFixer(boolean          allowAggressiveOverloading,
+                                   Map              descriptorMap,
+                                   WarningPrinter   warningPrinter,
+                                   MemberObfuscator memberObfuscator)
+    {
+        this.allowAggressiveOverloading = allowAggressiveOverloading;
+        this.descriptorMap              = descriptorMap;
+        this.warningPrinter             = warningPrinter;
+        this.memberObfuscator           = memberObfuscator;
+    }
+
+
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        visitMember(programClass, programField, true);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Special cases: <clinit> and <init> are always kept unchanged.
+        // We can ignore them here.
+        String name = programMethod.getName(programClass);
+        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
+            name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+        {
+            return;
+        }
+
+        visitMember(programClass, programMethod, false);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {}
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {}
+
+
+    /**
+     * Obfuscates the given class member.
+     * @param clazz   the class  of the given member.
+     * @param member  the class member to be obfuscated.
+     * @param isField specifies whether the class member is a field.
+     */
+    private void visitMember(Clazz   clazz,
+                             Member  member,
+                             boolean isField)
+    {
+        // Get the member's name and descriptor.
+        String name       = member.getName(clazz);
+        String descriptor = member.getDescriptor(clazz);
+
+        // Check whether we're allowed to overload aggressively.
+        if (!allowAggressiveOverloading)
+        {
+            // Trim the return argument from the descriptor if not.
+            // Works for fields and methods alike.
+            descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
+        }
+
+        // Get the name map.
+        Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor);
+
+        // Get the member's new name.
+        String newName = MemberObfuscator.newMemberName(member);
+
+        // Get the expected old name for this new name.
+        String previousName = (String)nameMap.get(newName);
+        if (previousName != null &&
+            !name.equals(previousName))
+        {
+            // There's a conflict! A member (with a given old name) in a
+            // first namespace has received the same new name as this
+            // member (with a different old name) in a second name space,
+            // and now these two have to live together in this name space.
+            if (MemberObfuscator.hasFixedNewMemberName(member) &&
+                warningPrinter != null)
+            {
+                descriptor = member.getDescriptor(clazz);
+                warningPrinter.print("Warning: " + ClassUtil.externalClassName(clazz.getName()) +
+                                                   (isField ?
+                                                       ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) :
+                                                       ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) +
+                                     "' can't be mapped to '" + newName +
+                                     "' because it would conflict with " +
+                                     (isField ?
+                                         "field '" :
+                                         "method '" ) + previousName +
+                                     "', which is already being mapped to '" + newName + "'");
+            }
+
+            // Clear the conflicting name.
+            MemberObfuscator.setNewMemberName(member, null);
+
+            // Assign a new name.
+            member.accept(clazz, memberObfuscator);
+        }
+    }
+}
diff --git a/src/proguard/obfuscate/MemberInfoObfuscator.java b/src/proguard/obfuscate/MemberObfuscator.java
similarity index 62%
rename from src/proguard/obfuscate/MemberInfoObfuscator.java
rename to src/proguard/obfuscate/MemberObfuscator.java
index a928cb8..113480c 100644
--- a/src/proguard/obfuscate/MemberInfoObfuscator.java
+++ b/src/proguard/obfuscate/MemberObfuscator.java
@@ -1,6 +1,6 @@
-/* $Id: MemberInfoObfuscator.java,v 1.14.2.5 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,30 +22,32 @@ package proguard.obfuscate;
 
 import proguard.classfile.*;
 import proguard.classfile.util.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
+import proguard.classfile.visitor.MemberVisitor;
 
 import java.util.*;
 
 /**
- * This MemberInfoVisitor obfuscates all class members that it visits.
+ * This MemberVisitor obfuscates all class members that it visits.
  * It uses names from the given name factory. At the same time, it avoids names
  * from the given descriptor map.
  * <p>
  * The class members must have been linked before applying this visitor.
  *
- * @see MethodInfoLinker
+ * @see MethodLinker
  *
  * @author Eric Lafortune
  */
-public class MemberInfoObfuscator implements MemberInfoVisitor
+public class MemberObfuscator
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private boolean        allowAggressiveOverloading;
-    private NameFactory    nameFactory;
-    private Map            descriptorMap;
+    private final boolean        allowAggressiveOverloading;
+    private final NameFactory    nameFactory;
+    private final Map            descriptorMap;
 
 
     /**
-     * Creates a new MemberInfoObfuscator.
+     * Creates a new MemberObfuscator.
      * @param allowAggressiveOverloading a flag that specifies whether class
      *                                   members can be overloaded aggressively.
      * @param nameFactory                the factory that can produce
@@ -53,9 +55,9 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
      * @param descriptorMap              the map of descriptors to
      *                                   [new name - old name] maps.
      */
-    public MemberInfoObfuscator(boolean        allowAggressiveOverloading,
-                                NameFactory    nameFactory,
-                                Map            descriptorMap)
+    public MemberObfuscator(boolean        allowAggressiveOverloading,
+                            NameFactory    nameFactory,
+                            Map            descriptorMap)
     {
         this.allowAggressiveOverloading = allowAggressiveOverloading;
         this.nameFactory                = nameFactory;
@@ -63,48 +65,23 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
     }
 
 
+    // Implementations for MemberVisitor.
 
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitAnyMember(Clazz clazz, Member member)
     {
         // Special cases: <clinit> and <init> are always kept unchanged.
         // We can ignore them here.
-        String name = programMethodInfo.getName(programClassFile);
+        String name = member.getName(clazz);
         if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
             name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
         {
             return;
         }
 
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    /**
-     * Obfuscates the given class member.
-     * @param classFile  the class file of the given member.
-     * @param memberInfo the class member to be obfuscated.
-     */
-    private void visitMemberInfo(ClassFile  classFile,
-                                 MemberInfo memberInfo)
-    {
-        // Get the member's name and descriptor.
-        String name       = memberInfo.getName(classFile);
-        String descriptor = memberInfo.getDescriptor(classFile);
+        // Get the member's descriptor.
+        String descriptor = member.getDescriptor(clazz);
 
-        // Check whether we're allowed to overload aggressively.
+        // Check whether we're allowed to do aggressive overloading
         if (!allowAggressiveOverloading)
         {
             // Trim the return argument from the descriptor if not.
@@ -116,7 +93,7 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
         Map nameMap = retrieveNameMap(descriptorMap, descriptor);
 
         // Get the member's new name.
-        String newName = newMemberName(memberInfo);
+        String newName = newMemberName(member);
 
         // Assign a new one, if necessary.
         if (newName == null)
@@ -134,7 +111,7 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
             nameMap.put(newName, name);
 
             // Assign the new name.
-            setNewMemberName(memberInfo, newName);
+            setNewMemberName(member, newName);
         }
     }
 
@@ -166,14 +143,14 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
 
     /**
      * Assigns a fixed new name to the given class member.
-     * @param memberInfo the class member.
-     * @param name       the new name.
+     * @param member the class member.
+     * @param name   the new name.
      */
-    static void setFixedNewMemberName(MemberInfo memberInfo, String name)
+    static void setFixedNewMemberName(Member member, String name)
     {
-        VisitorAccepter lastVisitorAccepter = MethodInfoLinker.lastVisitorAccepter(memberInfo);
+        VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
 
-        if (!(lastVisitorAccepter instanceof LibraryMemberInfo) &&
+        if (!(lastVisitorAccepter instanceof LibraryMember) &&
             !(lastVisitorAccepter instanceof MyFixedName))
         {
             lastVisitorAccepter.setVisitorInfo(new MyFixedName(name));
@@ -187,38 +164,38 @@ public class MemberInfoObfuscator implements MemberInfoVisitor
 
     /**
      * Assigns a new name to the given class member.
-     * @param memberInfo the class member.
-     * @param name       the new name.
+     * @param member the class member.
+     * @param name   the new name.
      */
-    static void setNewMemberName(MemberInfo memberInfo, String name)
+    static void setNewMemberName(Member member, String name)
     {
-        MethodInfoLinker.lastVisitorAccepter(memberInfo).setVisitorInfo(name);
+        MethodLinker.lastVisitorAccepter(member).setVisitorInfo(name);
     }
 
 
     /**
      * Returns whether the new name of the given class member is fixed.
-     * @param memberInfo the class member.
+     * @param member the class member.
      * @return whether its new name is fixed.
      */
-    static boolean hasFixedNewMemberName(MemberInfo memberInfo)
+    static boolean hasFixedNewMemberName(Member member)
     {
-        VisitorAccepter lastVisitorAccepter = MethodInfoLinker.lastVisitorAccepter(memberInfo);
+        VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
 
-        return lastVisitorAccepter instanceof LibraryMemberInfo ||
+        return lastVisitorAccepter instanceof LibraryMember ||
                lastVisitorAccepter instanceof MyFixedName;
     }
 
 
     /**
      * Retrieves the new name of the given class member.
-     * @param memberInfo the given class member.
+     * @param member the class member.
      * @return the class member's new name, or <code>null</code> if it doesn't
      *         have one yet.
      */
-    static String newMemberName(MemberInfo memberInfo)
+    static String newMemberName(Member member)
     {
-        return (String)MethodInfoLinker.lastVisitorAccepter(memberInfo).getVisitorInfo();
+        return (String)MethodLinker.lastVisitorAccepter(member).getVisitorInfo();
     }
 
 
diff --git a/src/proguard/obfuscate/MemberSpecialNameFilter.java b/src/proguard/obfuscate/MemberSpecialNameFilter.java
new file mode 100644
index 0000000..8a11bca
--- /dev/null
+++ b/src/proguard/obfuscate/MemberSpecialNameFilter.java
@@ -0,0 +1,101 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.obfuscate;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member has a
+ * special new name. A special name is a name that might have been produced by
+ * a <code>SpecialNameFactory</code>.
+ *
+ * @see MemberObfuscator
+ * @see SpecialNameFactory
+ *
+ * @author Eric Lafortune
+ */
+public class MemberSpecialNameFilter implements MemberVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new MemberSpecialNameFilter.
+     * @param memberVisitor the <code>MemberVisitor</code> to which
+     *                      visits will be delegated.
+     */
+    public MemberSpecialNameFilter(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (isSpecialName(programField))
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (isSpecialName(programMethod))
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (isSpecialName(libraryField))
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (isSpecialName(libraryMethod))
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the given class member has a special new name.
+     * @param member the class member.
+     */
+    private static boolean isSpecialName(Member member)
+    {
+        return SpecialNameFactory.isSpecialName(MemberObfuscator.newMemberName(member));
+    }
+}
diff --git a/src/proguard/obfuscate/MultiMappingProcessor.java b/src/proguard/obfuscate/MultiMappingProcessor.java
index 50789a5..bccf587 100644
--- a/src/proguard/obfuscate/MultiMappingProcessor.java
+++ b/src/proguard/obfuscate/MultiMappingProcessor.java
@@ -1,6 +1,6 @@
-/* $Id: MultiMappingProcessor.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,10 +20,6 @@
  */
 package proguard.obfuscate;
 
-import proguard.classfile.*;
-import proguard.classfile.util.ClassUtil;
-
-
 /**
  * This MappingKeeper delegates all method calls to each MappingProcessor
  * in a given list.
@@ -32,7 +28,7 @@ import proguard.classfile.util.ClassUtil;
  */
 public class MultiMappingProcessor implements MappingProcessor
 {
-    private MappingProcessor[] mappingProcessors;
+    private final MappingProcessor[] mappingProcessors;
 
 
     /**
@@ -48,15 +44,15 @@ public class MultiMappingProcessor implements MappingProcessor
 
     // Implementations for MappingProcessor.
 
-    public boolean processClassFileMapping(String className,
-                                           String newClassName)
+    public boolean processClassMapping(String className,
+                                       String newClassName)
     {
         boolean result = false;
 
         for (int index = 0; index < mappingProcessors.length; index++)
         {
-            result |= mappingProcessors[index].processClassFileMapping(className,
-                                                                       newClassName);
+            result |= mappingProcessors[index].processClassMapping(className,
+                                                                   newClassName);
         }
 
         return result;
diff --git a/src/proguard/obfuscate/NameAndTypeShrinker.java b/src/proguard/obfuscate/NameAndTypeShrinker.java
index c46b3c0..c2672a9 100644
--- a/src/proguard/obfuscate/NameAndTypeShrinker.java
+++ b/src/proguard/obfuscate/NameAndTypeShrinker.java
@@ -1,6 +1,6 @@
-/* $Id: NameAndTypeShrinker.java,v 1.24.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,55 +21,43 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.constant.Constant;
 import proguard.classfile.editor.ConstantPoolRemapper;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.visitor.ClassVisitor;
 
 
 /**
- * This ClassFileVisitor removes NameAndType constant pool entries
+ * This ClassVisitor removes NameAndType constant pool entries
  * that are not marked as being used.
  *
  * @see NameAndTypeUsageMarker
  *
  * @author Eric Lafortune
  */
-public class NameAndTypeShrinker implements ClassFileVisitor
+public class NameAndTypeShrinker implements ClassVisitor
 {
-    private int[]                cpIndexMap;
-    private ConstantPoolRemapper constantPoolRemapper;
-
-
-    /**
-     * Creates a new NameAndTypeShrinker.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public NameAndTypeShrinker(int codeLength)
-    {
-        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
-    }
+    private int[]                constantIndexMap;
+    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Shift the used constant pool entries together, filling out the
         // index map.
-        programClassFile.u2constantPoolCount =
-            shrinkConstantPool(programClassFile.constantPool,
-                               programClassFile.u2constantPoolCount);
+        programClass.u2constantPoolCount =
+            shrinkConstantPool(programClass.constantPool,
+                               programClass.u2constantPoolCount);
 
 
         // Remap all constant pool references.
-        constantPoolRemapper.setCpIndexMap(cpIndexMap);
-        constantPoolRemapper.visitProgramClassFile(programClassFile);
+        constantPoolRemapper.setConstantIndexMap(constantIndexMap);
+        constantPoolRemapper.visitProgramClass(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
     }
 
@@ -81,13 +69,13 @@ public class NameAndTypeShrinker implements ClassFileVisitor
      * from the given constant pool.
      * @return the new number of entries.
      */
-    private int shrinkConstantPool(CpInfo[] constantPool, int length)
+    private int shrinkConstantPool(Constant[] constantPool, int length)
     {
         // Create a new index map, if necessary.
-        if (cpIndexMap == null ||
-            cpIndexMap.length < length)
+        if (constantIndexMap == null ||
+            constantIndexMap.length < length)
         {
-            cpIndexMap = new int[length];
+            constantIndexMap = new int[length];
         }
 
         int     counter = 1;
@@ -96,20 +84,20 @@ public class NameAndTypeShrinker implements ClassFileVisitor
         // Shift the used constant pool entries together.
         for (int index = 1; index < length; index++)
         {
-            cpIndexMap[index] = counter;
+            constantIndexMap[index] = counter;
 
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
             // Don't update the flag if this is the second half of a long entry.
-            if (cpInfo != null)
+            if (constant != null)
             {
-                isUsed = cpInfo.getTag() != ClassConstants.CONSTANT_NameAndType ||
-                         NameAndTypeUsageMarker.isUsed(cpInfo);
+                isUsed = constant.getTag() != ClassConstants.CONSTANT_NameAndType ||
+                         NameAndTypeUsageMarker.isUsed(constant);
             }
 
             if (isUsed)
             {
-                constantPool[counter++] = cpInfo;
+                constantPool[counter++] = constant;
             }
         }
 
diff --git a/src/proguard/obfuscate/NameAndTypeUsageMarker.java b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
index 777c20c..37b261b 100644
--- a/src/proguard/obfuscate/NameAndTypeUsageMarker.java
+++ b/src/proguard/obfuscate/NameAndTypeUsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: NameAndTypeUsageMarker.java,v 1.14.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,108 +22,83 @@ package proguard.obfuscate;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassFileVisitor marks all NameAndType constant pool entries that are
- * being used in the classes it visits.
+ * This ClassVisitor marks all NameAndType constant pool entries that are
+ * being used in the program classes it visits.
  *
  * @see NameAndTypeShrinker
  *
  * @author Eric Lafortune
  */
 public class NameAndTypeUsageMarker
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             AttributeVisitor
 {
     // A visitor info flag to indicate the NameAndType constant pool entry is being used.
     private static final Object USED = new Object();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Mark the NameAndType entries referenced by all other constant pool
         // entries.
-        programClassFile.constantPoolEntriesAccept(this);
+        programClass.constantPoolEntriesAccept(this);
 
         // Mark the NameAndType entries referenced by all EnclosingMethod
         // attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
+        programClass.attributesAccept(this);
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
-        visitRefCpInfo(classFile, fieldrefCpInfo);
+        visitRefConstant(clazz, fieldrefConstant);
     }
 
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
-        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+        visitRefConstant(clazz, interfaceMethodrefConstant);
     }
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
-        visitRefCpInfo(classFile, methodrefCpInfo);
+        visitRefConstant(clazz, methodrefConstant);
     }
 
 
-    private void visitRefCpInfo(ClassFile classFile, RefCpInfo refCpInfo)
+    private void visitRefConstant(Clazz clazz, RefConstant refConstant)
     {
-        markNameAndTypeCpEntry(classFile, refCpInfo.u2nameAndTypeIndex);
+        markNameAndTypeConstant(clazz, refConstant.u2nameAndTypeIndex);
     }
 
 
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
-        if (enclosingMethodAttrInfo.u2nameAndTypeIndex != 0)
+        if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
         {
-            markNameAndTypeCpEntry(classFile, enclosingMethodAttrInfo.u2nameAndTypeIndex);
+            markNameAndTypeConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex);
         }
     }
 
@@ -133,15 +108,15 @@ public class NameAndTypeUsageMarker
     /**
      * Marks the given UTF-8 constant pool entry of the given class.
      */
-    private void markNameAndTypeCpEntry(ClassFile classFile, int index)
+    private void markNameAndTypeConstant(Clazz clazz, int index)
     {
-         markAsUsed((NameAndTypeCpInfo)((ProgramClassFile)classFile).getCpEntry(index));
+         markAsUsed((NameAndTypeConstant)((ProgramClass)clazz).getConstant(index));
     }
 
 
     /**
      * Marks the given VisitorAccepter as being used.
-     * In this context, the VisitorAccepter will be a NameAndTypeCpInfo object.
+     * In this context, the VisitorAccepter will be a NameAndTypeConstant object.
      */
     private static void markAsUsed(VisitorAccepter visitorAccepter)
     {
@@ -151,7 +126,7 @@ public class NameAndTypeUsageMarker
 
     /**
      * Returns whether the given VisitorAccepter has been marked as being used.
-     * In this context, the VisitorAccepter will be a NameAndTypeCpInfo object.
+     * In this context, the VisitorAccepter will be a NameAndTypeConstant object.
      */
     static boolean isUsed(VisitorAccepter visitorAccepter)
     {
diff --git a/src/proguard/obfuscate/NameFactory.java b/src/proguard/obfuscate/NameFactory.java
index a480bb5..bfd43d0 100644
--- a/src/proguard/obfuscate/NameFactory.java
+++ b/src/proguard/obfuscate/NameFactory.java
@@ -1,6 +1,6 @@
-/* $Id: NameFactory.java,v 1.14.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,8 +20,6 @@
  */
 package proguard.obfuscate;
 
-import java.util.*;
-
 /**
  * This interfaces provides methods to generate unique sequences of names.
  * The names must be valid Java identifiers.
diff --git a/src/proguard/obfuscate/NameFactoryResetter.java b/src/proguard/obfuscate/NameFactoryResetter.java
index 6d832a3..cda368c 100644
--- a/src/proguard/obfuscate/NameFactoryResetter.java
+++ b/src/proguard/obfuscate/NameFactoryResetter.java
@@ -1,6 +1,6 @@
-/* $Id: NameFactoryResetter.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,24 +21,22 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.ClassFileVisitor;
-
-import java.util.Map;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassFileVisitor resets a given name factory whenever it visits a class
+ * This ClassVisitor resets a given name factory whenever it visits a class
  * file.
  *
  * @author Eric Lafortune
  */
-public class NameFactoryResetter implements ClassFileVisitor
+public class NameFactoryResetter implements ClassVisitor
 {
-    private NameFactory nameFactory;
+    private final NameFactory nameFactory;
 
 
     /**
-     * Creates a new MapCleaner.
-     * @param map the map to be cleared.
+     * Creates a new NameFactoryResetter.
+     * @param nameFactory the name factory to be reset.
      */
     public NameFactoryResetter(NameFactory nameFactory)
     {
@@ -46,15 +44,15 @@ public class NameFactoryResetter implements ClassFileVisitor
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         nameFactory.reset();
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         nameFactory.reset();
     }
diff --git a/src/proguard/obfuscate/NameMarker.java b/src/proguard/obfuscate/NameMarker.java
index 21b51d7..e889df6 100644
--- a/src/proguard/obfuscate/NameMarker.java
+++ b/src/proguard/obfuscate/NameMarker.java
@@ -1,6 +1,6 @@
-/* $Id: NameMarker.java,v 1.17.2.3 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -25,56 +25,56 @@ import proguard.classfile.visitor.*;
 
 
 /**
- * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
- * marks names of the class files and class members it visits. The marked names
+ * This <code>ClassVisitor</code> and <code>MemberVisitor</code>
+ * marks names of the classes and class members it visits. The marked names
  * will remain unchanged in the obfuscation step.
  *
- * @see ClassFileObfuscator
- * @see MemberInfoObfuscator
+ * @see ClassObfuscator
+ * @see MemberObfuscator
  *
  * @author Eric Lafortune
  */
-public class NameMarker
-  implements ClassFileVisitor,
-             MemberInfoVisitor
+class      NameMarker
+implements ClassVisitor,
+           MemberVisitor
 {
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        keepClassName(programClassFile);
+        keepClassName(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        keepClassName(libraryClassFile);
+        keepClassName(libraryClass);
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        keepFieldName(programClassFile, programFieldInfo);
+        keepFieldName(programClass, programField);
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        keepMethodName(programClassFile, programMethodInfo);
+        keepMethodName(programClass, programMethod);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
-        keepFieldName(libraryClassFile, libraryFieldInfo);
+        keepFieldName(libraryClass, libraryField);
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        keepMethodName(libraryClassFile, libraryMethodInfo);
+        keepMethodName(libraryClass, libraryMethod);
     }
 
 
@@ -83,35 +83,35 @@ public class NameMarker
     /**
      * Ensures the name of the given class name will be kept.
      */
-    public void keepClassName(ClassFile classFile)
+    public void keepClassName(Clazz clazz)
     {
-        ClassFileObfuscator.setNewClassName(classFile,
-                                            classFile.getName());
+        ClassObfuscator.setNewClassName(clazz,
+                                        clazz.getName());
     }
 
 
     /**
      * Ensures the name of the given field name will be kept.
      */
-    private void keepFieldName(ClassFile classFile, FieldInfo fieldInfo)
+    private void keepFieldName(Clazz clazz, Field field)
     {
-        MemberInfoObfuscator.setFixedNewMemberName(fieldInfo,
-                                                   fieldInfo.getName(classFile));
+        MemberObfuscator.setFixedNewMemberName(field,
+                                               field.getName(clazz));
     }
 
 
     /**
      * Ensures the name of the given method name will be kept.
      */
-    private void keepMethodName(ClassFile classFile, MethodInfo methodInfo)
+    private void keepMethodName(Clazz clazz, Method method)
     {
-        String name = methodInfo.getName(classFile);
+        String name = method.getName(clazz);
 
         if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) &&
             !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
         {
-            MemberInfoObfuscator.setFixedNewMemberName(methodInfo,
-                                                       methodInfo.getName(classFile));
+            MemberObfuscator.setFixedNewMemberName(method,
+                                                   method.getName(clazz));
         }
     }
 }
diff --git a/src/proguard/obfuscate/Obfuscator.java b/src/proguard/obfuscate/Obfuscator.java
index 1e3b899..6faaca6 100644
--- a/src/proguard/obfuscate/Obfuscator.java
+++ b/src/proguard/obfuscate/Obfuscator.java
@@ -1,6 +1,6 @@
-/* $Id: Obfuscator.java,v 1.2.2.6 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,9 +23,12 @@ package proguard.obfuscate;
 
 import proguard.*;
 import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.visitor.AllConstantVisitor;
 import proguard.classfile.editor.*;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
+import proguard.util.*;
 
 import java.io.*;
 import java.util.*;
@@ -38,7 +41,7 @@ import java.util.*;
  */
 public class Obfuscator
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -58,7 +61,6 @@ public class Obfuscator
     {
         // Check if we have at least some keep commands.
         if (configuration.keep         == null &&
-            configuration.keepNames    == null &&
             configuration.applyMapping == null &&
             configuration.printMapping == null)
         {
@@ -66,40 +68,36 @@ public class Obfuscator
         }
 
         // Clean up any old visitor info.
-        programClassPool.classFilesAccept(new ClassFileCleaner());
-        libraryClassPool.classFilesAccept(new ClassFileCleaner());
+        programClassPool.classesAccept(new ClassCleaner());
+        libraryClassPool.classesAccept(new ClassCleaner());
 
         // 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.
-        ClassFileVisitor memberInfoLinker =
+        ClassVisitor memberInfoLinker =
             configuration.useUniqueClassMemberNames ?
-                (ClassFileVisitor)new AllMemberInfoVisitor(new MethodInfoLinker()) :
-                (ClassFileVisitor)new BottomClassFileFilter(new MethodInfoLinker());
+                (ClassVisitor)new AllMemberVisitor(new MethodLinker()) :
+                (ClassVisitor)new BottomClassFilter(new MethodLinker());
 
-        programClassPool.classFilesAccept(memberInfoLinker);
-        libraryClassPool.classFilesAccept(memberInfoLinker);
+        programClassPool.classesAccept(memberInfoLinker);
+        libraryClassPool.classesAccept(memberInfoLinker);
 
         // Create a visitor for marking the seeds.
         NameMarker nameMarker = new NameMarker();
         ClassPoolVisitor classPoolvisitor =
-            new MultiClassPoolVisitor(new ClassPoolVisitor[]
-            {
-                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
-                                                                        nameMarker,
-                                                                        nameMarker),
-                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
-                                                                        nameMarker,
-                                                                        nameMarker)
-            });
-
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                    nameMarker,
+                                                                    nameMarker,
+                                                                    false,
+                                                                    false,
+                                                                    true);
         // Mark the seeds.
         programClassPool.accept(classPoolvisitor);
         libraryClassPool.accept(classPoolvisitor);
 
         // All library classes and library class members keep their names.
-        libraryClassPool.classFilesAccept(nameMarker);
-        libraryClassPool.classFilesAccept(new AllMemberInfoVisitor(nameMarker));
+        libraryClassPool.classesAccept(nameMarker);
+        libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker));
 
         // Apply the mapping, if one has been specified. The mapping can
         // override the names of library classes and of library class members.
@@ -120,7 +118,7 @@ public class Obfuscator
 
             reader.pump(keeper);
 
-            if (configuration.warn)
+            if (warningPrinter != null)
             {
                 // Print out a summary of the warnings if necessary.
                 int mappingWarningCount = warningPrinter.getWarningCount();
@@ -141,28 +139,31 @@ public class Obfuscator
         }
 
         // Mark attributes that have to be kept.
-        AttributeUsageMarker attributeUsageMarker = new AttributeUsageMarker();
-        if (configuration.keepAttributes != null)
-        {
-            if (!configuration.keepAttributes.isEmpty())
-            {
-                attributeUsageMarker.setKeepAttributes(configuration.keepAttributes);
-            }
-            else
-            {
-                attributeUsageMarker.setKeepAllAttributes();
-            }
-        }
-        programClassPool.classFilesAccept(attributeUsageMarker);
+        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.classFilesAccept(new AttributeShrinker());
+        programClassPool.classesAccept(new AttributeShrinker());
 
         // Come up with new names for all classes.
-        programClassPool.classFilesAccept(
-            new ClassFileObfuscator(programClassPool,
-                                    configuration.defaultPackage,
-                                    configuration.useMixedCaseClassNames));
+        programClassPool.classesAccept(
+            new ClassObfuscator(programClassPool,
+                                configuration.useMixedCaseClassNames,
+                                configuration.flattenPackageHierarchy,
+                                configuration.repackageClasses,
+                                configuration.allowAccessModification));
 
         // Come up with new names for all class members.
         NameFactory nameFactory = new SimpleNameFactory();
@@ -184,72 +185,72 @@ public class Obfuscator
         if (configuration.useUniqueClassMemberNames)
         {
             // Collect all member names in all classes.
-            programClassPool.classFilesAccept(
-                new AllMemberInfoVisitor(
-                new MemberInfoNameCollector(configuration.overloadAggressively,
-                                            descriptorMap)));
+            programClassPool.classesAccept(
+                new AllMemberVisitor(
+                new MemberNameCollector(configuration.overloadAggressively,
+                                        descriptorMap)));
 
             // Assign new names to all members in all classes.
-            programClassPool.classFilesAccept(
-                new AllMemberInfoVisitor(
-                new MemberInfoObfuscator(configuration.overloadAggressively,
-                                         nameFactory,
-                                         descriptorMap)));
+            programClassPool.classesAccept(
+                new AllMemberVisitor(
+                new MemberObfuscator(configuration.overloadAggressively,
+                                     nameFactory,
+                                     descriptorMap)));
         }
         else
         {
             // Come up with new names for all non-private class members.
-            programClassPool.classFilesAccept(
-                new MultiClassFileVisitor(new ClassFileVisitor[]
+            programClassPool.classesAccept(
+                new MultiClassVisitor(new ClassVisitor[]
                 {
                     // Collect all private member names in this class and down
                     // the hierarchy.
-                    new ClassFileHierarchyTraveler(true, false, false, true,
-                    new AllMemberInfoVisitor(
-                    new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
-                    new MemberInfoNameCollector(configuration.overloadAggressively,
-                                                descriptorMap)))),
+                    new ClassHierarchyTraveler(true, false, false, true,
+                    new AllMemberVisitor(
+                    new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
+                    new MemberNameCollector(configuration.overloadAggressively,
+                                            descriptorMap)))),
 
                     // Collect all non-private member names anywhere in the hierarchy.
-                    new ClassFileHierarchyTraveler(true, true, true, true,
-                    new AllMemberInfoVisitor(
-                    new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                    new MemberInfoNameCollector(configuration.overloadAggressively,
-                                                descriptorMap)))),
+                    new ClassHierarchyTraveler(true, true, true, true,
+                    new AllMemberVisitor(
+                    new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                    new MemberNameCollector(configuration.overloadAggressively,
+                                            descriptorMap)))),
 
                     // Assign new names to all non-private members in this class.
-                    new AllMemberInfoVisitor(
-                    new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                    new MemberInfoObfuscator(configuration.overloadAggressively,
-                                             nameFactory,
-                                             descriptorMap))),
+                    new AllMemberVisitor(
+                    new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                    new MemberObfuscator(configuration.overloadAggressively,
+                                         nameFactory,
+                                         descriptorMap))),
 
                     // Clear the collected names.
                     new MapCleaner(descriptorMap)
                 }));
 
             // Come up with new names for all private class members.
-            programClassPool.classFilesAccept(
-                new MultiClassFileVisitor(new ClassFileVisitor[]
+            programClassPool.classesAccept(
+                new MultiClassVisitor(new ClassVisitor[]
                 {
                     // Collect all member names in this class.
-                    new AllMemberInfoVisitor(
-                    new MemberInfoNameCollector(configuration.overloadAggressively,
-                                                descriptorMap)),
+                    new AllMemberVisitor(
+                    new MemberNameCollector(configuration.overloadAggressively,
+                                            descriptorMap)),
 
                     // Collect all non-private member names higher up the hierarchy.
-                    new ClassFileHierarchyTraveler(false, true, true, false,
-                    new AllMemberInfoVisitor(
-                    new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                    new MemberInfoNameCollector(configuration.overloadAggressively,
-                                                descriptorMap)))),
+                    new ClassHierarchyTraveler(false, true, true, false,
+                    new AllMemberVisitor(
+                    new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                    new MemberNameCollector(configuration.overloadAggressively,
+                                            descriptorMap)))),
 
                     // Assign new names to all private members in this class.
-                    new AllMemberInfoVisitor(
-                    new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
-                    new MemberInfoObfuscator(configuration.overloadAggressively,
-                                             nameFactory,
-                                             descriptorMap))),
+                    new AllMemberVisitor(
+                    new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
+                    new MemberObfuscator(configuration.overloadAggressively,
+                                         nameFactory,
+                                         descriptorMap))),
 
                     // Clear the collected names.
                     new MapCleaner(descriptorMap)
@@ -265,47 +266,49 @@ public class Obfuscator
         // [descriptor - new name - old name].
         Map specialDescriptorMap = new HashMap();
 
-        programClassPool.classFilesAccept(
-            new AllMemberInfoVisitor(
-            new MemberInfoSpecialNameFilter(
-            new MemberInfoNameCollector(configuration.overloadAggressively,
-                                        specialDescriptorMap))));
+        programClassPool.classesAccept(
+            new AllMemberVisitor(
+            new MemberSpecialNameFilter(
+            new MemberNameCollector(configuration.overloadAggressively,
+                                    specialDescriptorMap))));
 
-        libraryClassPool.classFilesAccept(
-            new AllMemberInfoVisitor(
-            new MemberInfoSpecialNameFilter(
-            new MemberInfoNameCollector(configuration.overloadAggressively,
-                                        specialDescriptorMap))));
+        libraryClassPool.classesAccept(
+            new AllMemberVisitor(
+            new MemberSpecialNameFilter(
+            new MemberNameCollector(configuration.overloadAggressively,
+                                    specialDescriptorMap))));
 
         // Replace conflicting non-private member names with special names.
-        programClassPool.classFilesAccept(
-            new MultiClassFileVisitor(new ClassFileVisitor[]
+        programClassPool.classesAccept(
+            new MultiClassVisitor(new ClassVisitor[]
             {
                 // Collect all private member names in this class and down
                 // the hierarchy.
-                new ClassFileHierarchyTraveler(true, false, false, true,
-                new AllMemberInfoVisitor(
-                new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
-                new MemberInfoNameCollector(configuration.overloadAggressively,
-                                            descriptorMap)))),
-
-                // Collect all non-private member names anywhere in the hierarchy.
-                new ClassFileHierarchyTraveler(true, true, true, true,
-                new AllMemberInfoVisitor(
-                new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                new MemberInfoNameCollector(configuration.overloadAggressively,
-                                            descriptorMap)))),
-
-                // Assign new names to all conflicting non-private members in
-                // this class.
-                new AllMemberInfoVisitor(
-                new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                new MemberInfoNameConflictFixer(configuration.overloadAggressively,
-                                                descriptorMap,
-                                                warningPrinter,
-                new MemberInfoObfuscator(configuration.overloadAggressively,
-                                         specialNameFactory,
-                                         specialDescriptorMap)))),
+                new ClassHierarchyTraveler(true, false, false, true,
+                new AllMemberVisitor(
+                new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
+                new MemberNameCollector(configuration.overloadAggressively,
+                                        descriptorMap)))),
+
+                // Collect all non-private member names in this class and
+                // higher up the hierarchy.
+                new ClassHierarchyTraveler(true, true, true, false,
+                new AllMemberVisitor(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MemberNameCollector(configuration.overloadAggressively,
+                                        descriptorMap)))),
+
+                // Assign new names to all conflicting non-private members
+                // in this class and higher up the hierarchy.
+                new ClassHierarchyTraveler(true, true, true, false,
+                new AllMemberVisitor(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MemberNameConflictFixer(configuration.overloadAggressively,
+                                            descriptorMap,
+                                            warningPrinter,
+                new MemberObfuscator(configuration.overloadAggressively,
+                                     specialNameFactory,
+                                     specialDescriptorMap))))),
 
                 // Clear the collected names.
                 new MapCleaner(descriptorMap)
@@ -313,38 +316,38 @@ public class Obfuscator
 
         // Replace conflicting private member names with special names.
         // This is only possible if those names were kept or mapped.
-        programClassPool.classFilesAccept(
-            new MultiClassFileVisitor(new ClassFileVisitor[]
+        programClassPool.classesAccept(
+            new MultiClassVisitor(new ClassVisitor[]
             {
                 // Collect all member names in this class.
-                new AllMemberInfoVisitor(
-                new MemberInfoNameCollector(configuration.overloadAggressively,
-                                            descriptorMap)),
+                new AllMemberVisitor(
+                new MemberNameCollector(configuration.overloadAggressively,
+                                        descriptorMap)),
 
                 // Collect all non-private member names higher up the hierarchy.
-                new ClassFileHierarchyTraveler(false, true, true, false,
-                new AllMemberInfoVisitor(
-                new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                new MemberInfoNameCollector(configuration.overloadAggressively,
-                                            descriptorMap)))),
+                new ClassHierarchyTraveler(false, true, true, false,
+                new AllMemberVisitor(
+                new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
+                new MemberNameCollector(configuration.overloadAggressively,
+                                        descriptorMap)))),
 
                 // Assign new names to all conflicting private members in this
                 // class.
-                new AllMemberInfoVisitor(
-                new MemberInfoAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
-                new MemberInfoNameConflictFixer(configuration.overloadAggressively,
-                                                descriptorMap,
-                                                warningPrinter,
-                new MemberInfoObfuscator(configuration.overloadAggressively,
-                                         specialNameFactory,
-                                         specialDescriptorMap)))),
+                new AllMemberVisitor(
+                new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
+                new MemberNameConflictFixer(configuration.overloadAggressively,
+                                            descriptorMap,
+                                            warningPrinter,
+                new MemberObfuscator(configuration.overloadAggressively,
+                                     specialNameFactory,
+                                     specialDescriptorMap)))),
 
                 // Clear the collected names.
                 new MapCleaner(descriptorMap)
             }));
 
         // Print out any warnings about member name conflicts.
-        if (configuration.warn)
+        if (warningPrinter != null)
         {
             int warningCount = warningPrinter.getWarningCount();
             if (warningCount > 0)
@@ -370,7 +373,7 @@ public class Obfuscator
                 System.out;
 
             // Print out items that will be removed.
-            programClassPool.classFilesAcceptAlphabetically(new MappingPrinter(ps));
+            programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps));
 
             if (ps != System.out)
             {
@@ -379,35 +382,39 @@ public class Obfuscator
         }
 
         // Actually apply the new names.
-        programClassPool.classFilesAccept(new ClassFileRenamer());
-        libraryClassPool.classFilesAccept(new ClassFileRenamer());
+        programClassPool.classesAccept(new ClassRenamer());
+        libraryClassPool.classesAccept(new ClassRenamer());
 
         // Update all references to these new names.
-        programClassPool.classFilesAccept(new ClassFileReferenceFixer(false));
-        libraryClassPool.classFilesAccept(new ClassFileReferenceFixer(false));
-        programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
-
-        // Make package visible elements public, if necessary.
-        if (configuration.defaultPackage != null)
+        programClassPool.classesAccept(new ClassReferenceFixer(false));
+        libraryClassPool.classesAccept(new ClassReferenceFixer(false));
+        programClassPool.classesAccept(new MemberReferenceFixer());
+
+        // Make package visible elements public or protected, if obfuscated
+        // classes are being repackaged aggressively.
+        if (configuration.repackageClasses != null &&
+            configuration.allowAccessModification)
         {
-            programClassPool.classFilesAccept(new ClassFileOpener());
+            programClassPool.classesAccept(
+                new AllConstantVisitor(
+                new ClassOpener()));
         }
 
         // Rename the source file attributes, if requested.
         if (configuration.newSourceFileAttribute != null)
         {
-            programClassPool.classFilesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
+            programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
         }
 
         // Mark NameAndType constant pool entries that have to be kept
         // and remove the other ones.
-        programClassPool.classFilesAccept(new NameAndTypeUsageMarker());
-        programClassPool.classFilesAccept(new NameAndTypeShrinker(1024));
+        programClassPool.classesAccept(new NameAndTypeUsageMarker());
+        programClassPool.classesAccept(new NameAndTypeShrinker());
 
         // Mark Utf8 constant pool entries that have to be kept
         // and remove the other ones.
-        programClassPool.classFilesAccept(new Utf8UsageMarker());
-        programClassPool.classFilesAccept(new Utf8Shrinker(1024));
+        programClassPool.classesAccept(new Utf8UsageMarker());
+        programClassPool.classesAccept(new Utf8Shrinker());
     }
 
 
diff --git a/src/proguard/obfuscate/SimpleNameFactory.java b/src/proguard/obfuscate/SimpleNameFactory.java
index f5c518c..23b0441 100644
--- a/src/proguard/obfuscate/SimpleNameFactory.java
+++ b/src/proguard/obfuscate/SimpleNameFactory.java
@@ -1,6 +1,6 @@
-/* $Id: SimpleNameFactory.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -36,7 +36,7 @@ public class SimpleNameFactory implements NameFactory
     private static final List cachedMixedCaseNames = new ArrayList();
     private static final List cachedLowerCaseNames = new ArrayList();
 
-    private boolean generateMixedCaseNames;
+    private final boolean generateMixedCaseNames;
     private int     index = 0;
 
 
diff --git a/src/proguard/obfuscate/SourceFileRenamer.java b/src/proguard/obfuscate/SourceFileRenamer.java
index 75280b0..562fb46 100644
--- a/src/proguard/obfuscate/SourceFileRenamer.java
+++ b/src/proguard/obfuscate/SourceFileRenamer.java
@@ -1,6 +1,6 @@
-/* $Id: SourceFileRenamer.java,v 1.2.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,24 +22,26 @@ package proguard.obfuscate;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 import proguard.classfile.editor.ConstantPoolEditor;
-import proguard.classfile.visitor.ClassFileVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassFileVisitor changes the name stored in the source file attributes
- * and source dir attributes of the class files that it visits, if the
- * atributes are present.
+ * This ClassVisitor changes the name stored in the source file attributes
+ * and source dir attributes of the classes that it visits, if the
+ * attributes are present.
  *
  * @author Eric Lafortune
  */
 public class SourceFileRenamer
-implements   ClassFileVisitor,
-             AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             AttributeVisitor
 {
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
 
-    private String newSourceFileAttribute;
+    private final String newSourceFileAttribute;
 
 
     /**
@@ -53,56 +55,40 @@ implements   ClassFileVisitor,
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        // Only visit the class file attributes.
-        programClassFile.attributesAccept(this);
+        // Only visit the class attributes.
+        programClass.attributesAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        // Library class files don't have attributes.
+        // Library classes don't have attributes.
     }
 
 
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
     {
         // Fix the source file attribute.
-        sourceFileAttrInfo.u2sourceFileIndex =
-            constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                             newSourceFileAttribute);
+        sourceFileAttribute.u2sourceFileIndex =
+            constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                               newSourceFileAttribute);
     }
 
 
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
     {
         // Fix the source file attribute.
-        sourceDirAttrInfo.u2sourceDirIndex =
-            constantPoolEditor.addUtf8CpInfo((ProgramClassFile)classFile,
-                                             newSourceFileAttribute);
+        sourceDirAttribute.u2sourceDirIndex =
+            constantPoolEditor.addUtf8Constant((ProgramClass)clazz,
+                                               newSourceFileAttribute);
     }
 }
diff --git a/src/proguard/obfuscate/SpecialNameFactory.java b/src/proguard/obfuscate/SpecialNameFactory.java
index 0425cca..4e1b42d 100644
--- a/src/proguard/obfuscate/SpecialNameFactory.java
+++ b/src/proguard/obfuscate/SpecialNameFactory.java
@@ -1,6 +1,6 @@
-/* $Id: SpecialNameFactory.java,v 1.3.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,9 +20,6 @@
  */
 package proguard.obfuscate;
 
-import java.io.*;
-import java.util.*;
-
 /**
  * This <code>NameFactory</code> generates names that are special, by appending
  * a suffix.
diff --git a/src/proguard/obfuscate/Utf8Shrinker.java b/src/proguard/obfuscate/Utf8Shrinker.java
index 5a48d37..85d16bc 100644
--- a/src/proguard/obfuscate/Utf8Shrinker.java
+++ b/src/proguard/obfuscate/Utf8Shrinker.java
@@ -1,6 +1,6 @@
-/* $Id: Utf8Shrinker.java,v 1.27.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,52 +21,42 @@
 package proguard.obfuscate;
 
 import proguard.classfile.*;
+import proguard.classfile.constant.Constant;
 import proguard.classfile.editor.ConstantPoolRemapper;
-import proguard.classfile.visitor.ClassFileVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 
 /**
- * This ClassFileVisitor removes UTF-8 constant pool entries that are not marked
+ * This ClassVisitor removes UTF-8 constant pool entries that are not marked
  * as being used.
  *
  * @see Utf8UsageMarker
  *
  * @author Eric Lafortune
  */
-public class Utf8Shrinker implements ClassFileVisitor
+public class Utf8Shrinker implements ClassVisitor
 {
-    private int[]                cpIndexMap;
-    private ConstantPoolRemapper constantPoolRemapper;
-
-
-    /**
-     * Creates a new Utf8Shrinker.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public Utf8Shrinker(int codeLength)
-    {
-        constantPoolRemapper = new ConstantPoolRemapper(codeLength);
-    }
+    private int[]                constantIndexMap     = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
+    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Shift the used constant pool entries together, filling out the
         // index map.
-        programClassFile.u2constantPoolCount =
-            shrinkConstantPool(programClassFile.constantPool,
-                               programClassFile.u2constantPoolCount);
+        programClass.u2constantPoolCount =
+            shrinkConstantPool(programClass.constantPool,
+                               programClass.u2constantPoolCount);
 
         // Remap all constant pool references.
-        constantPoolRemapper.setCpIndexMap(cpIndexMap);
-        constantPoolRemapper.visitProgramClassFile(programClassFile);
+        constantPoolRemapper.setConstantIndexMap(constantIndexMap);
+        constantPoolRemapper.visitProgramClass(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
     }
 
@@ -78,13 +68,12 @@ public class Utf8Shrinker implements ClassFileVisitor
      * from the given constant pool.
      * @return the new number of entries.
      */
-    private int shrinkConstantPool(CpInfo[] constantPool, int length)
+    private int shrinkConstantPool(Constant[] constantPool, int length)
     {
         // Create a new index map, if necessary.
-        if (cpIndexMap == null ||
-            cpIndexMap.length < length)
+        if (constantIndexMap.length < length)
         {
-            cpIndexMap = new int[length];
+            constantIndexMap = new int[length];
         }
 
         int     counter = 1;
@@ -93,20 +82,20 @@ public class Utf8Shrinker implements ClassFileVisitor
         // Shift the used constant pool entries together.
         for (int index = 1; index < length; index++)
         {
-            cpIndexMap[index] = counter;
+            constantIndexMap[index] = counter;
 
-            CpInfo cpInfo = constantPool[index];
+            Constant constant = constantPool[index];
 
             // Don't update the flag if this is the second half of a long entry.
-            if (cpInfo != null)
+            if (constant != null)
             {
-                isUsed = cpInfo.getTag() != ClassConstants.CONSTANT_Utf8 ||
-                         Utf8UsageMarker.isUsed(cpInfo);
+                isUsed = constant.getTag() != ClassConstants.CONSTANT_Utf8 ||
+                         Utf8UsageMarker.isUsed(constant);
             }
 
             if (isUsed)
             {
-                constantPool[counter++] = cpInfo;
+                constantPool[counter++] = constant;
             }
         }
 
diff --git a/src/proguard/obfuscate/Utf8UsageMarker.java b/src/proguard/obfuscate/Utf8UsageMarker.java
index 7943fd1..23a55e9 100644
--- a/src/proguard/obfuscate/Utf8UsageMarker.java
+++ b/src/proguard/obfuscate/Utf8UsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: Utf8UsageMarker.java,v 1.23.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,21 +23,28 @@ package proguard.obfuscate;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.*;
 
 /**
- * This ClassFileVisitor marks all UTF-8 constant pool entries that are
- * being used in the classes it visits.
+ * This ClassVisitor marks all UTF-8 constant pool entries that are
+ * being used in the program classes it visits.
  *
  * @see Utf8Shrinker
  *
  * @author Eric Lafortune
  */
 public class Utf8UsageMarker
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             ConstantVisitor,
+             AttributeVisitor,
              InnerClassesInfoVisitor,
              LocalVariableInfoVisitor,
              LocalVariableTypeInfoVisitor,
@@ -48,342 +55,308 @@ public class Utf8UsageMarker
     private static final Object USED = new Object();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Mark the UTF-8 entries referenced by the other constant pool entries.
-        programClassFile.constantPoolEntriesAccept(this);
+        programClass.constantPoolEntriesAccept(this);
 
         // Mark the UTF-8 entries referenced by the fields and methods.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
 
         // Mark the UTF-8 entries referenced by the attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
+        programClass.attributesAccept(this);
     }
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
+    // Implementations for MemberVisitor.
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
     {
         // Mark the name and descriptor UTF-8 entries.
-        markCpUtf8Entry(programClassFile, programMemberInfo.u2nameIndex);
-        markCpUtf8Entry(programClassFile, programMemberInfo.u2descriptorIndex);
+        markCpUtf8Entry(programClass, programMember.u2nameIndex);
+        markCpUtf8Entry(programClass, programMember.u2descriptorIndex);
 
         // Mark the UTF-8 entries referenced by the attributes.
-        programMemberInfo.attributesAccept(programClassFile, this);
+        programMember.attributesAccept(programClass, this);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
+    // Implementations for ConstantVisitor.
 
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
-    // Implementations for CpInfoVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-
-
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-        markCpUtf8Entry(classFile, stringCpInfo.u2stringIndex);
+        markCpUtf8Entry(clazz, stringConstant.u2stringIndex);
     }
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        markCpUtf8Entry(classFile, classCpInfo.u2nameIndex);
+        markCpUtf8Entry(clazz, classConstant.u2nameIndex);
     }
 
 
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
     {
-        markCpUtf8Entry(classFile, nameAndTypeCpInfo.u2nameIndex);
-        markCpUtf8Entry(classFile, nameAndTypeCpInfo.u2descriptorIndex);
+        markCpUtf8Entry(clazz, nameAndTypeConstant.u2nameIndex);
+        markCpUtf8Entry(clazz, nameAndTypeConstant.u2descriptorIndex);
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
     {
         // This is the best we can do for unknown attributes.
-        markCpUtf8Entry(classFile, unknownAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, unknownAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
     {
-        markCpUtf8Entry(classFile, innerClassesAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, sourceFileAttribute.u2attributeNameIndex);
 
-        // Mark the UTF-8 entries referenced by the inner classes.
-        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+        markCpUtf8Entry(clazz, sourceFileAttribute.u2sourceFileIndex);
     }
 
 
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
     {
-        markCpUtf8Entry(classFile, enclosingMethodAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, sourceDirAttribute.u2attributeNameIndex);
 
-        // These entries have already been marked in the constant pool.
-        //classFile.constantPoolEntryAccept(this, enclosingMethodAttrInfo.u2classIndex);
-        //classFile.constantPoolEntryAccept(this, enclosingMethodAttrInfo.u2nameAndTypeIndex);
+        markCpUtf8Entry(clazz, sourceDirAttribute.u2sourceDirIndex);
     }
 
 
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
     {
-        markCpUtf8Entry(classFile, constantValueAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, innerClassesAttribute.u2attributeNameIndex);
+
+        // Mark the UTF-8 entries referenced by the inner classes.
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
     }
 
 
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
-        markCpUtf8Entry(classFile, exceptionsAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, enclosingMethodAttribute.u2attributeNameIndex);
+
+        // These entries have already been marked in the constant pool.
+        //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2classIndex);
+        //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2nameAndTypeIndex);
     }
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
     {
-        markCpUtf8Entry(classFile, codeAttrInfo.u2attrNameIndex);
-
-        // Mark the UTF-8 entries referenced by the attributes.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+        markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
     {
-        markCpUtf8Entry(classFile, lineNumberTableAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, syntheticAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
     {
-        markCpUtf8Entry(classFile, localVariableTableAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, signatureAttribute.u2attributeNameIndex);
 
-        // Mark the UTF-8 entries referenced by the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        markCpUtf8Entry(clazz, signatureAttribute.u2signatureIndex);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
     {
-        markCpUtf8Entry(classFile, localVariableTypeTableAttrInfo.u2attrNameIndex);
-
-        // Mark the UTF-8 entries referenced by the local variable types.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        markCpUtf8Entry(clazz, constantValueAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
     {
-        markCpUtf8Entry(classFile, sourceFileAttrInfo.u2attrNameIndex);
-
-        markCpUtf8Entry(classFile, sourceFileAttrInfo.u2sourceFileIndex);
+        markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        markCpUtf8Entry(classFile, sourceDirAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex);
 
-        markCpUtf8Entry(classFile, sourceDirAttrInfo.u2sourceDirIndex);
+        // Mark the UTF-8 entries referenced by the attributes.
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
     {
-        markCpUtf8Entry(classFile, deprecatedAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
     {
-        markCpUtf8Entry(classFile, syntheticAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
-        markCpUtf8Entry(classFile, signatureAttrInfo.u2attrNameIndex);
-
-        markCpUtf8Entry(classFile, signatureAttrInfo.u2signatureIndex);
+        markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-        markCpUtf8Entry(classFile, runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex);
 
-        // Mark the UTF-8 entries referenced by the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Mark the UTF-8 entries referenced by the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-        markCpUtf8Entry(classFile, runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex);
 
-        // Mark the UTF-8 entries referenced by the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Mark the UTF-8 entries referenced by the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
     {
-        markCpUtf8Entry(classFile, runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex);
 
         // Mark the UTF-8 entries referenced by the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        annotationsAttribute.annotationsAccept(clazz, this);
     }
 
 
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
     {
-        markCpUtf8Entry(classFile, runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
 
         // Mark the UTF-8 entries referenced by the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
     }
 
 
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
     {
-        markCpUtf8Entry(classFile, annotationDefaultAttrInfo.u2attrNameIndex);
+        markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex);
 
         // Mark the UTF-8 entries referenced by the element value.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
     }
 
 
     // Implementations for InnerClassesInfoVisitor.
 
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
     {
         if (innerClassesInfo.u2innerNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, innerClassesInfo.u2innerNameIndex);
+            markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex);
         }
     }
 
 
     // Implementations for LocalVariableInfoVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
     {
-        markCpUtf8Entry(classFile, localVariableInfo.u2nameIndex);
-        markCpUtf8Entry(classFile, localVariableInfo.u2descriptorIndex);
+        markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex);
+        markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex);
     }
 
 
     // Implementations for LocalVariableTypeInfoVisitor.
 
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
     {
-        markCpUtf8Entry(classFile, localVariableTypeInfo.u2nameIndex);
-        markCpUtf8Entry(classFile, localVariableTypeInfo.u2signatureIndex);
+        markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex);
+        markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex);
     }
 
 
     // Implementations for AnnotationVisitor.
 
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
     {
-        markCpUtf8Entry(classFile, annotation.u2typeIndex);
+        markCpUtf8Entry(clazz, annotation.u2typeIndex);
 
         // Mark the UTF-8 entries referenced by the element values.
-        annotation.elementValuesAccept(classFile, this);
+        annotation.elementValuesAccept(clazz, this);
     }
 
 
     // Implementations for ElementValueVisitor.
 
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
     {
-        if (constantElementValue.u2elementName != 0)
+        if (constantElementValue.u2elementNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, constantElementValue.u2elementName);
+            markCpUtf8Entry(clazz, constantElementValue.u2elementNameIndex);
         }
 
         // Only the string constant element value refers to a UTF-8 entry.
         if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT)
         {
-            markCpUtf8Entry(classFile, constantElementValue.u2constantValueIndex);
+            markCpUtf8Entry(clazz, constantElementValue.u2constantValueIndex);
         }
     }
 
 
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
     {
-        if (enumConstantElementValue.u2elementName != 0)
+        if (enumConstantElementValue.u2elementNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, enumConstantElementValue.u2elementName);
+            markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex);
         }
 
-        markCpUtf8Entry(classFile, enumConstantElementValue.u2typeNameIndex);
-        markCpUtf8Entry(classFile, enumConstantElementValue.u2constantNameIndex);
+        markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex);
+        markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex);
     }
 
 
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
     {
-        if (classElementValue.u2elementName != 0)
+        if (classElementValue.u2elementNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, classElementValue.u2elementName);
+            markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex);
         }
 
-        markCpUtf8Entry(classFile, classElementValue.u2classInfoIndex);
+        markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex);
     }
 
 
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
     {
-        if (annotationElementValue.u2elementName != 0)
+        if (annotationElementValue.u2elementNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, annotationElementValue.u2elementName);
+            markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex);
         }
 
         // Mark the UTF-8 entries referenced by the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
+        annotationElementValue.annotationAccept(clazz, this);
     }
 
 
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
     {
-        if (arrayElementValue.u2elementName != 0)
+        if (arrayElementValue.u2elementNameIndex != 0)
         {
-            markCpUtf8Entry(classFile, arrayElementValue.u2elementName);
+            markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex);
         }
 
         // Mark the UTF-8 entries referenced by the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
     }
 
 
@@ -392,15 +365,15 @@ public class Utf8UsageMarker
     /**
      * Marks the given UTF-8 constant pool entry of the given class.
      */
-    private void markCpUtf8Entry(ClassFile classFile, int index)
+    private void markCpUtf8Entry(Clazz clazz, int index)
     {
-         markAsUsed((Utf8CpInfo)((ProgramClassFile)classFile).getCpEntry(index));
+         markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index));
     }
 
 
     /**
      * Marks the given VisitorAccepter as being used.
-     * In this context, the VisitorAccepter will be a Utf8CpInfo object.
+     * In this context, the VisitorAccepter will be a Utf8Constant object.
      */
     private static void markAsUsed(VisitorAccepter visitorAccepter)
     {
@@ -410,7 +383,7 @@ public class Utf8UsageMarker
 
     /**
      * Returns whether the given VisitorAccepter has been marked as being used.
-     * In this context, the VisitorAccepter will be a Utf8CpInfo object.
+     * In this context, the VisitorAccepter will be a Utf8Constant object.
      */
     static boolean isUsed(VisitorAccepter visitorAccepter)
     {
diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/src/proguard/optimize/ChangedCodePrinter.java
index 6af8159..8086eae 100644
--- a/src/proguard/optimize/ChangedCodePrinter.java
+++ b/src/proguard/optimize/ChangedCodePrinter.java
@@ -1,6 +1,6 @@
-/* $Id: ChangedCodePrinter.java,v 1.7.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,154 +23,242 @@ package proguard.optimize;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
 import proguard.classfile.util.ClassUtil;
-import proguard.classfile.visitor.*;
 
 /**
- * This AttrInfoVisitor delegates its call to another AttrInfoVisitor, and
+ * This AttributeVisitor delegates its call to another AttributeVisitor, and
  * prints out the code if the other visitor has changed it.
  *
  * @author Eric Lafortune
  */
-public class ChangedCodePrinter implements AttrInfoVisitor
+public class ChangedCodePrinter
+implements   AttributeVisitor
 {
-    private AttrInfoVisitor attrInfoVisitor;
+    private final AttributeVisitor attributeVisitor;
+
+
+    public ChangedCodePrinter(AttributeVisitor attributeVisitor)
+    {
+        this.attributeVisitor = attributeVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
+    {
+        attributeVisitor.visitUnknownAttribute(clazz, unknownAttribute);
+    }
+
+
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
+    {
+        attributeVisitor.visitSourceFileAttribute(clazz, sourceFileAttribute);
+    }
+
+
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
+    {
+        attributeVisitor.visitSourceDirAttribute(clazz, sourceDirAttribute);
+    }
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        attributeVisitor.visitInnerClassesAttribute(clazz, innerClassesAttribute);
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        attributeVisitor.visitEnclosingMethodAttribute(clazz, enclosingMethodAttribute);
+    }
+
+
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
+    {
+        attributeVisitor.visitDeprecatedAttribute(clazz, deprecatedAttribute);
+    }
+
+
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
+    {
+        attributeVisitor.visitSyntheticAttribute(clazz, syntheticAttribute);
+    }
 
 
-    public ChangedCodePrinter(AttrInfoVisitor attrInfoVisitor)
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute syntheticAttribute)
     {
-        this.attrInfoVisitor = attrInfoVisitor;
+        attributeVisitor.visitSignatureAttribute(clazz, syntheticAttribute);
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute)
+    {
+        attributeVisitor.visitDeprecatedAttribute(clazz, field, deprecatedAttribute);
+    }
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+
+    public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute)
     {
-      attrInfoVisitor.visitUnknownAttrInfo(classFile, unknownAttrInfo);
+        attributeVisitor.visitSyntheticAttribute(clazz, field, syntheticAttribute);
     }
 
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+
+    public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute syntheticAttribute)
     {
-      attrInfoVisitor.visitInnerClassesAttrInfo(classFile, innerClassesAttrInfo);
+        attributeVisitor.visitSignatureAttribute(clazz, field, syntheticAttribute);
     }
 
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+
+    public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute)
     {
-        attrInfoVisitor.visitEnclosingMethodAttrInfo(classFile, enclosingMethodAttrInfo);
+        attributeVisitor.visitDeprecatedAttribute(clazz, method, deprecatedAttribute);
     }
 
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+
+    public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute)
     {
-      attrInfoVisitor.visitConstantValueAttrInfo(classFile, fieldInfo, constantValueAttrInfo);
+        attributeVisitor.visitSyntheticAttribute(clazz, method, syntheticAttribute);
     }
 
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+
+    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute syntheticAttribute)
     {
-      attrInfoVisitor.visitExceptionsAttrInfo(classFile, methodInfo, exceptionsAttrInfo);
+        attributeVisitor.visitSignatureAttribute(clazz, method, syntheticAttribute);
     }
 
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
+    {
+        attributeVisitor.visitConstantValueAttribute(clazz, field, constantValueAttribute);
+    }
+
+
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
+    {
+        attributeVisitor.visitExceptionsAttribute(clazz, method, exceptionsAttribute);
+    }
+
+
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
     {
-      attrInfoVisitor.visitLineNumberTableAttrInfo(classFile, methodInfo, codeAttrInfo, lineNumberTableAttrInfo);
+        attributeVisitor.visitStackMapAttribute(clazz, method, codeAttribute, stackMapAttribute);
     }
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
     {
-      attrInfoVisitor.visitLocalVariableTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTableAttrInfo);
+        attributeVisitor.visitStackMapTableAttribute(clazz, method, codeAttribute, stackMapTableAttribute);
     }
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
-        attrInfoVisitor.visitLocalVariableTypeTableAttrInfo(classFile, methodInfo, codeAttrInfo, localVariableTypeTableAttrInfo);
+        attributeVisitor.visitLineNumberTableAttribute(clazz, method, codeAttribute, lineNumberTableAttribute);
     }
 
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-      attrInfoVisitor.visitSourceFileAttrInfo(classFile, sourceFileAttrInfo);
+        attributeVisitor.visitLocalVariableTableAttribute(clazz, method, codeAttribute, localVariableTableAttribute);
     }
 
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-      attrInfoVisitor.visitSourceDirAttrInfo(classFile, sourceDirAttrInfo);
+        attributeVisitor.visitLocalVariableTypeTableAttribute(clazz, method, codeAttribute, localVariableTypeTableAttribute);
     }
 
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
     {
-      attrInfoVisitor.visitDeprecatedAttrInfo(classFile, deprecatedAttrInfo);
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute);
     }
 
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
     {
-      attrInfoVisitor.visitSyntheticAttrInfo(classFile, syntheticAttrInfo);
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute);
     }
 
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
     {
-      attrInfoVisitor.visitSignatureAttrInfo(classFile, signatureAttrInfo);
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, field, runtimeVisibleAnnotationsAttribute);
     }
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
     {
-        attrInfoVisitor.visitRuntimeVisibleAnnotationAttrInfo(classFile, runtimeVisibleAnnotationsAttrInfo);
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, field, runtimeInvisibleAnnotationsAttribute);
     }
 
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+
+    public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute)
     {
-        attrInfoVisitor.visitRuntimeInvisibleAnnotationAttrInfo(classFile, runtimeInvisibleAnnotationsAttrInfo);
+        attributeVisitor.visitRuntimeVisibleAnnotationsAttribute(clazz, method, runtimeVisibleAnnotationsAttribute);
     }
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+
+    public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute)
     {
-        attrInfoVisitor.visitRuntimeVisibleParameterAnnotationAttrInfo(classFile, runtimeVisibleParameterAnnotationsAttrInfo);
+        attributeVisitor.visitRuntimeInvisibleAnnotationsAttribute(clazz, method, runtimeInvisibleAnnotationsAttribute);
     }
 
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+
+    public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute)
     {
-        attrInfoVisitor.visitRuntimeInvisibleParameterAnnotationAttrInfo(classFile, runtimeInvisibleParameterAnnotationsAttrInfo);
+        attributeVisitor.visitRuntimeVisibleParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute);
     }
 
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+
+    public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute)
     {
-        attrInfoVisitor.visitAnnotationDefaultAttrInfo(classFile, annotationDefaultAttrInfo);
+        attributeVisitor.visitRuntimeInvisibleParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute);
     }
 
 
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute);
+    }
+
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        byte[] code    = codeAttrInfo.code;
+        byte[] code    = codeAttribute.code;
         byte[] oldCode = new byte[code.length];
 
         // Copy the current code.
-        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
-        {
-            oldCode[index] = code[index];
-        }
+        System.arraycopy(code, 0, oldCode, 0, codeAttribute.u4codeLength);
 
         // Delegate to the real visitor.
-        attrInfoVisitor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+        attributeVisitor.visitCodeAttribute(clazz, method, codeAttribute);
 
         // Check if the code has changed.
-        if (codeHasChanged(codeAttrInfo, oldCode))
+        if (codeHasChanged(codeAttribute, oldCode))
         {
-            printChangedCode(classFile, methodInfo, codeAttrInfo, oldCode);
+            printChangedCode(clazz, method, codeAttribute, oldCode);
         }
     }
 
 
     // Small utility methods.
 
-    private boolean codeHasChanged(CodeAttrInfo codeAttrInfo, byte[] oldCode)
+    private boolean codeHasChanged(CodeAttribute codeAttribute, byte[] oldCode)
     {
-        if (oldCode.length != codeAttrInfo.u4codeLength)
+        if (oldCode.length != codeAttribute.u4codeLength)
         {
             return true;
         }
 
-        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
+        for (int index = 0; index < codeAttribute.u4codeLength; index++)
         {
-            if (oldCode[index] != codeAttrInfo.code[index])
+            if (oldCode[index] != codeAttribute.code[index])
             {
                 return true;
             }
@@ -180,24 +268,24 @@ public class ChangedCodePrinter implements AttrInfoVisitor
     }
 
 
-    private void printChangedCode(ClassFile    classFile,
-                                  MethodInfo   methodInfo,
-                                  CodeAttrInfo codeAttrInfo,
-                                  byte[]       oldCode)
+    private void printChangedCode(Clazz         clazz,
+                                  Method        method,
+                                  CodeAttribute codeAttribute,
+                                  byte[]        oldCode)
     {
-        System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
-        System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
+        System.out.println("Class "+ClassUtil.externalClassName(clazz.getName()));
+        System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
                                                                              0,
-                                                                             methodInfo.getName(classFile),
-                                                                             methodInfo.getDescriptor(classFile)));
+                                                                             method.getName(clazz),
+                                                                             method.getDescriptor(clazz)));
 
-        for (int index = 0; index < codeAttrInfo.u4codeLength; index++)
+        for (int index = 0; index < codeAttribute.u4codeLength; index++)
         {
             System.out.println(
-                (oldCode[index] == codeAttrInfo.code[index]? "  -- ":"  => ")+
+                (oldCode[index] == codeAttribute.code[index]? "  -- ":"  => ")+
                 index+": "+
-                Integer.toHexString(0x100|oldCode[index]          &0xff).substring(1)+" "+
-                Integer.toHexString(0x100|codeAttrInfo.code[index]&0xff).substring(1));
+                Integer.toHexString(0x100|oldCode[index]           &0xff).substring(1)+" "+
+                Integer.toHexString(0x100|codeAttribute.code[index]&0xff).substring(1));
         }
     }
 }
diff --git a/src/proguard/optimize/ConstantMemberFilter.java b/src/proguard/optimize/ConstantMemberFilter.java
new file mode 100644
index 0000000..5b8ff64
--- /dev/null
+++ b/src/proguard/optimize/ConstantMemberFilter.java
@@ -0,0 +1,77 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.evaluation.value.Value;
+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.
+ *
+ * @see StoringInvocationUnit
+ * @author Eric Lafortune
+ */
+public class ConstantMemberFilter
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private final MemberVisitor constantMemberVisitor;
+
+
+    /**
+     * Creates a new ConstantMemberFilter.
+     * @param constantMemberVisitor the <code>MemberVisitor</code> to which
+     *                              visits to constant members will be delegated.
+     */
+    public ConstantMemberFilter(MemberVisitor constantMemberVisitor)
+    {
+        this.constantMemberVisitor = constantMemberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        Value value = StoringInvocationUnit.getFieldValue(programField);
+        if (value != null &&
+            value.isSpecific())
+        {
+            constantMemberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        Value value = StoringInvocationUnit.getMethodReturnValue(programMethod);
+        if (value != null &&
+            value.isSpecific())
+        {
+            constantMemberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+}
diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java
index b29c5cc..161c53a 100644
--- a/src/proguard/optimize/KeepMarker.java
+++ b/src/proguard/optimize/KeepMarker.java
@@ -1,6 +1,6 @@
-/* $Id: KeepMarker.java,v 1.7.2.3 2006/06/07 22:36:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,68 +21,68 @@
 package proguard.optimize;
 
 import proguard.classfile.*;
-import proguard.classfile.util.MethodInfoLinker;
+import proguard.classfile.util.MethodLinker;
 import proguard.classfile.visitor.*;
 
 
 /**
- * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
- * marks class files and class members it visits. The marked elements
+ * This <code>ClassVisitor</code> and <code>MemberVisitor</code>
+ * marks classes and class members it visits. The marked elements
  * will remain unchanged as necessary in the optimization step.
  *
  * @author Eric Lafortune
  */
 public class KeepMarker
-  implements ClassFileVisitor,
-             MemberInfoVisitor
+implements   ClassVisitor,
+             MemberVisitor
 {
     // A visitor info flag to indicate the visitor accepter is being kept.
     private static final Object KEPT = new Object();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        markAsKept(programClassFile);
+        markAsKept(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        markAsKept(libraryClassFile);
+        markAsKept(libraryClass);
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        markAsKept(programFieldInfo);
+        markAsKept(programField);
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        markAsKept(MethodInfoLinker.lastMemberInfo(programMethodInfo));
+        markAsKept(MethodLinker.lastMember(programMethod));
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
-        markAsKept(libraryFieldInfo);
+        markAsKept(libraryField);
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        markAsKept(MethodInfoLinker.lastMemberInfo(libraryMethodInfo));
+        markAsKept(MethodLinker.lastMember(libraryMethod));
     }
 
 
     // Small utility methods.
 
-    public static void markAsKept(VisitorAccepter visitorAccepter)
+    private static void markAsKept(VisitorAccepter visitorAccepter)
     {
         visitorAccepter.setVisitorInfo(KEPT);
     }
@@ -90,6 +90,6 @@ public class KeepMarker
 
     public static boolean isKept(VisitorAccepter visitorAccepter)
     {
-        return MethodInfoLinker.lastVisitorAccepter(visitorAccepter).getVisitorInfo() == KEPT;
+        return MethodLinker.lastVisitorAccepter(visitorAccepter).getVisitorInfo() == KEPT;
     }
 }
diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/src/proguard/optimize/MemberDescriptorSpecializer.java
new file mode 100644
index 0000000..184dd8e
--- /dev/null
+++ b/src/proguard/optimize/MemberDescriptorSpecializer.java
@@ -0,0 +1,138 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.editor.ClassReferenceFixer;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.evaluation.value.Value;
+import proguard.optimize.evaluation.StoringInvocationUnit;
+
+/**
+ * This MemberVisitor specializes parameters in the descriptors of the
+ * methods that it visits.
+ *
+ * @see StoringInvocationUnit
+ * @see ClassReferenceFixer
+ * @author Eric Lafortune
+ */
+public class MemberDescriptorSpecializer
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private static final boolean DEBUG = true;
+
+
+    private final MemberVisitor extraParameterMemberVisitor;
+
+
+    /**
+     * Creates a new MethodDescriptorShrinker.
+     */
+    public MemberDescriptorSpecializer()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new MethodDescriptorShrinker with an extra visitor.
+     * @param extraParameterMemberVisitor an optional extra visitor for all
+     *                                    class members whose parameters have
+     *                                    been specialized.
+     */
+    public MemberDescriptorSpecializer(MemberVisitor extraParameterMemberVisitor)
+    {
+        this.extraParameterMemberVisitor = extraParameterMemberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        Value parameterValue = StoringInvocationUnit.getFieldValue(programField);
+        if (parameterValue.computationalType() == Value.TYPE_REFERENCE)
+        {
+            Clazz referencedClass = parameterValue.referenceValue().getReferencedClass();
+            if (programField.referencedClass != referencedClass)
+            {
+                if (DEBUG)
+                {
+                    System.out.println("MemberDescriptorSpecializer: "+programClass.getName()+"."+programField.getName(programClass)+" "+programField.getDescriptor(programClass));
+                    System.out.println("  "+programField.referencedClass.getName()+" -> "+referencedClass.getName());
+                }
+
+                programField.referencedClass = referencedClass;
+
+                // Visit the field, if required.
+                if (extraParameterMemberVisitor != null)
+                {
+                    extraParameterMemberVisitor.visitProgramField(programClass, programField);
+                }
+            }
+        }
+    }
+
+
+    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));
+
+        int classIndex = 0;
+
+        // Go over the parameters.
+        for (int parameterIndex = firstParameterIndex; parameterIndex < parameterCount; parameterIndex++)
+        {
+            Value parameterValue = StoringInvocationUnit.getMethodParameterValue(programMethod, parameterIndex);
+             if (parameterValue.computationalType() == Value.TYPE_REFERENCE)
+             {
+                 Clazz referencedClass = parameterValue.referenceValue().getReferencedClass();
+                 if (programMethod.referencedClasses[classIndex] != referencedClass)
+                 {
+                     if (DEBUG)
+                     {
+                         System.out.println("MemberDescriptorSpecializer: "+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass));
+                         System.out.println("  "+programMethod.referencedClasses[classIndex].getName()+" -> "+referencedClass.getName());
+                     }
+
+                     programMethod.referencedClasses[classIndex] = referencedClass;
+
+                     // Visit the method, if required.
+                     if (extraParameterMemberVisitor != null)
+                     {
+                         extraParameterMemberVisitor.visitProgramMethod(programClass, programMethod);
+                     }
+                 }
+
+                 classIndex++;
+             }
+        }
+    }
+}
diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/src/proguard/optimize/MethodDescriptorShrinker.java
new file mode 100644
index 0000000..7216d17
--- /dev/null
+++ b/src/proguard/optimize/MethodDescriptorShrinker.java
@@ -0,0 +1,312 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.ConstantPoolEditor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.info.*;
+import proguard.optimize.peephole.VariableShrinker;
+
+/**
+ * This MemberVisitor removes unused parameters in the descriptors of the
+ * methods that it visits.
+ *
+ * @see ParameterUsageMarker
+ * @see VariableUsageMarker
+ * @see VariableShrinker
+ * @author Eric Lafortune
+ */
+public class MethodDescriptorShrinker
+extends      SimplifiedVisitor
+implements   MemberVisitor,
+             AttributeVisitor
+{
+    private static final boolean DEBUG = false;
+
+
+    private final MemberVisitor extraParameterMemberVisitor;
+
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+
+
+    /**
+     * Creates a new MethodDescriptorShrinker.
+     */
+    public MethodDescriptorShrinker()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new MethodDescriptorShrinker with an extra visitor.
+     * @param extraParameterMemberVisitor an optional extra visitor for all
+     *                                    methods whose parameters have been
+     *                                    simplified.
+     */
+    public MethodDescriptorShrinker(MemberVisitor extraParameterMemberVisitor)
+    {
+        this.extraParameterMemberVisitor = extraParameterMemberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Update the descriptor if it has any unused parameters.
+        String descriptor    = programMethod.getDescriptor(programClass);
+        String newDescriptor = shrinkDescriptor(programClass, programMethod);
+        if (!descriptor.equals(newDescriptor))
+        {
+            // Shrink the parameter annotations.
+            programMethod.attributesAccept(programClass, this);
+
+            String name = programMethod.getName(programClass);
+            String newName;
+
+            // Is it a class instance initializer?
+            if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+            {
+                // Is there already another initializer with the same descriptor?
+                if (programClass.findMethod(name, newDescriptor) != null)
+                {
+                    // Cancel the shrinking. Mark all parameters.
+                    ParameterUsageMarker.markUsedParameters(programMethod, -1L);
+
+                    return;
+                }
+
+                // We have to keep the initializer's name.
+                newName = name;
+            }
+            else
+            {
+                // Create a unique name.
+                newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
+            }
+
+            if (DEBUG)
+            {
+                System.out.println("MethodDescriptorShrinker:");
+                System.out.println("  Class file        = "+programClass.getName());
+                System.out.println("  Method name       = "+name);
+                System.out.println("                   -> "+newName);
+                System.out.println("  Method descriptor = "+descriptor);
+                System.out.println("                   -> "+newDescriptor);
+            }
+
+            // Update the name, if necessary.
+            if (!newName.equals(name))
+            {
+                programMethod.u2nameIndex =
+                constantPoolEditor.addUtf8Constant(programClass, newName);
+            }
+
+            // Clear the unused referenced classes.
+            shrinkReferencedClasses(programClass, programMethod);
+
+            // Update the descriptor.
+            programMethod.u2descriptorIndex =
+                constantPoolEditor.addUtf8Constant(programClass, newDescriptor);
+
+            // Visit the method, if required.
+            if (extraParameterMemberVisitor != null)
+            {
+                extraParameterMemberVisitor.visitProgramMethod(programClass, programMethod);
+            }
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        int[]          annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
+        Annotation[][] annotations       = parameterAnnotationsAttribute.parameterAnnotations;
+
+        // All parameters of non-static methods are shifted by one in the local
+        // variable frame.
+        int parameterIndex =
+            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
+                0 : 1;
+
+        int annotationIndex    = 0;
+        int newAnnotationIndex = 0;
+
+        // Go over the parameters.
+        String descriptor = method.getDescriptor(clazz);
+        InternalTypeEnumeration internalTypeEnumeration =
+            new InternalTypeEnumeration(descriptor);
+
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            String type = internalTypeEnumeration.nextType();
+            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+            {
+                annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
+                annotations[newAnnotationIndex++]     = annotations[annotationIndex];
+            }
+
+            annotationIndex++;
+
+            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
+        }
+
+        // Update the number of parameters.
+        parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;
+
+        // Clear the unused entries.
+        while (newAnnotationIndex < annotationIndex)
+        {
+            annotationsCounts[newAnnotationIndex] = 0;
+            annotations[newAnnotationIndex++]     = null;
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns a shrunk descriptor of the given method.
+     */
+    public static String shrinkDescriptor(ProgramClass  clazz,
+                                          ProgramMethod method)
+    {
+        // All parameters of non-static methods are shifted by one in the local
+        // variable frame.
+        int parameterIndex =
+            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
+                0 : 1;
+
+        // Go over the parameters.
+        InternalTypeEnumeration internalTypeEnumeration =
+            new InternalTypeEnumeration(method.getDescriptor(clazz));
+
+        StringBuffer newDescriptorBuffer = new StringBuffer();
+        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
+
+        if (DEBUG)
+        {
+            System.out.println("MethodDescriptorShrinker: "+method.getName(clazz)+method.getDescriptor(clazz));
+            System.out.println("  parameter size = " + ParameterUsageMarker.getParameterSize(method));
+        }
+
+        while (internalTypeEnumeration.hasMoreTypes())
+        {
+            String type = internalTypeEnumeration.nextType();
+            if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+            {
+                newDescriptorBuffer.append(type);
+            }
+            else if (DEBUG)
+            {
+                System.out.println("  Deleting parameter #"+parameterIndex+" ("+type+")");
+            }
+
+            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
+        }
+
+        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
+        newDescriptorBuffer.append(internalTypeEnumeration.returnType());
+
+        return newDescriptorBuffer.toString();
+    }
+
+
+    /**
+     * Shrinks the array of referenced classes of the given method.
+     */
+    private static void shrinkReferencedClasses(ProgramClass  clazz,
+                                                ProgramMethod method)
+    {
+        Clazz[] referencedClasses = method.referencedClasses;
+
+        if (referencedClasses != null)
+        {
+            // All parameters of non-static methods are shifted by one in the local
+            // variable frame.
+            int parameterIndex =
+                (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
+                    0 : 1;
+
+            int referencedClassIndex    = 0;
+            int newReferencedClassIndex = 0;
+
+            // Go over the parameters.
+            String descriptor = method.getDescriptor(clazz);
+            InternalTypeEnumeration internalTypeEnumeration =
+                new InternalTypeEnumeration(descriptor);
+
+            while (internalTypeEnumeration.hasMoreTypes())
+            {
+                String type = internalTypeEnumeration.nextType();
+                if (ClassUtil.isInternalArrayType(type))
+                {
+                    type = ClassUtil.internalTypeFromArrayType(type);
+                }
+
+                if (ClassUtil.isInternalClassType(type))
+                {
+                    if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
+                    {
+                        referencedClasses[newReferencedClassIndex++] =
+                            referencedClasses[referencedClassIndex];
+                    }
+
+                    referencedClassIndex++;
+                }
+
+                parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
+            }
+
+            // Also look at the return value.
+            String type = internalTypeEnumeration.returnType();
+            if (ClassUtil.isInternalArrayType(type))
+            {
+                type = ClassUtil.internalTypeFromArrayType(type);
+            }
+
+            if (ClassUtil.isInternalClassType(type))
+            {
+                referencedClasses[newReferencedClassIndex++] =
+                    referencedClasses[referencedClassIndex];
+
+                referencedClassIndex++;
+            }
+
+            // Clear the unused entries.
+            while (newReferencedClassIndex < referencedClassIndex)
+            {
+                referencedClasses[newReferencedClassIndex++] = null;
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/MethodOptimizationInfo.java b/src/proguard/optimize/MethodOptimizationInfo.java
deleted file mode 100644
index 5d4b34e..0000000
--- a/src/proguard/optimize/MethodOptimizationInfo.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/* $Id: MethodOptimizationInfo.java,v 1.4.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.util.MethodInfoLinker;
-
-/**
- * This class stores some optimization information that can be attached to
- * a method.
- *
- * @author Eric Lafortune
- */
-public class MethodOptimizationInfo
-{
-    private boolean hasNoSideEffects = false;
-    private boolean hasSideEffects   = false;
-    private boolean canBeMadePrivate = true;
-    private long    usedVariables    = 0;
-
-
-    public void setNoSideEffects()
-    {
-        hasNoSideEffects = true;
-    }
-
-
-    public boolean hasNoSideEffects()
-    {
-        return hasNoSideEffects;
-    }
-
-
-    public void setSideEffects()
-    {
-        hasSideEffects = true;
-    }
-
-
-    public boolean hasSideEffects()
-    {
-        return hasSideEffects;
-    }
-
-
-    public void setCanNotBeMadePrivate()
-    {
-        canBeMadePrivate = false;
-    }
-
-
-    public boolean canBeMadePrivate()
-    {
-        return canBeMadePrivate;
-    }
-
-
-    public void setUsedVariables(long usedVariables)
-    {
-        this.usedVariables = usedVariables;
-    }
-
-
-    public long getUsedVariables()
-    {
-        return usedVariables;
-    }
-
-
-    public void setVariableUsed(int variableIndex)
-    {
-        usedVariables |= 1 << variableIndex;
-    }
-
-
-    public boolean isVariableUsed(int variableIndex)
-    {
-        return variableIndex >= 64 || (usedVariables & (1 << variableIndex)) != 0;
-    }
-
-
-    public static void setMethodOptimizationInfo(MethodInfo methodInfo)
-    {
-        MethodInfoLinker.lastMemberInfo(methodInfo).setVisitorInfo(new MethodOptimizationInfo());
-    }
-
-
-    public static MethodOptimizationInfo getMethodOptimizationInfo(MethodInfo methodInfo)
-    {
-        Object visitorInfo = MethodInfoLinker.lastMemberInfo(methodInfo).getVisitorInfo();
-
-        return visitorInfo instanceof MethodOptimizationInfo ?
-            (MethodOptimizationInfo)visitorInfo :
-            null;
-    }
-}
diff --git a/src/proguard/optimize/MethodOptimizationInfoSetter.java b/src/proguard/optimize/MethodOptimizationInfoSetter.java
deleted file mode 100644
index 594bd85..0000000
--- a/src/proguard/optimize/MethodOptimizationInfoSetter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/* $Id: MethodOptimizationInfoSetter.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This MemberInfoVisitor attaches a MethodOptimizationInfo instance to every
- * method that is not being kept that it visits.
- *
- * @author Eric Lafortune
- */
-public class MethodOptimizationInfoSetter implements MemberInfoVisitor
-{
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (!KeepMarker.isKept(programMethodInfo))
-        {
-            MethodOptimizationInfo.setMethodOptimizationInfo(programMethodInfo);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        if (!KeepMarker.isKept(libraryMethodInfo))
-        {
-            MethodOptimizationInfo.setMethodOptimizationInfo(libraryMethodInfo);
-        }
-    }
-}
diff --git a/src/proguard/optimize/MethodStaticizer.java b/src/proguard/optimize/MethodStaticizer.java
new file mode 100644
index 0000000..fbc97b3
--- /dev/null
+++ b/src/proguard/optimize/MethodStaticizer.java
@@ -0,0 +1,87 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.MethodInvocationFixer;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.info.ParameterUsageMarker;
+import proguard.optimize.peephole.VariableShrinker;
+
+/**
+ * This MemberVisitor makes all methods that it visits static, if their 'this'
+ * parameters are unused.
+ *
+ * @see ParameterUsageMarker
+ * @see MethodInvocationFixer
+ * @see VariableShrinker
+ * @author Eric Lafortune
+ */
+public class MethodStaticizer
+extends      SimplifiedVisitor
+implements   MemberVisitor,
+             AttributeVisitor
+{
+    private final MemberVisitor extraStaticMemberVisitor;
+
+
+    /**
+     * Creates a new MethodStaticizer.
+     */
+    public MethodStaticizer()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new MethodStaticizer with an extra visitor.
+     * @param extraStaticMemberVisitor an optional extra visitor for all
+     *                                 methods that have been made static.
+     */
+    public MethodStaticizer(MemberVisitor extraStaticMemberVisitor)
+    {
+        this.extraStaticMemberVisitor = extraStaticMemberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Is the 'this' parameter being used?
+        if (!ParameterUsageMarker.isParameterUsed(programMethod, 0))
+        {
+            // Make the method static.
+            programMethod.u2accessFlags =
+                (programMethod.getAccessFlags() & ~ClassConstants.INTERNAL_ACC_FINAL) |
+                ClassConstants.INTERNAL_ACC_STATIC;
+
+            // Visit the method, if required.
+            if (extraStaticMemberVisitor != null)
+            {
+                extraStaticMemberVisitor.visitProgramMethod(programClass, programMethod);
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/NonPrivateMethodMarker.java b/src/proguard/optimize/NonPrivateMethodMarker.java
deleted file mode 100644
index 2956bac..0000000
--- a/src/proguard/optimize/NonPrivateMethodMarker.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/* $Id: NonPrivateMethodMarker.java,v 1.7.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassFileVisitor marks all methods that can not be made private in the
- * classes that it visits, and in the classes to which they refer.
- *
- * @author Eric Lafortune
- */
-public class NonPrivateMethodMarker
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor
-{
-    private MethodImplementationFilter filteredMethodMarker = new MethodImplementationFilter(this);
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Explicitly mark the <clinit> method.
-        programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
-                                      ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
-                                      this);
-
-        // Explicitly mark the parameterless <init> method.
-        programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT,
-                                      ClassConstants.INTERNAL_METHOD_TYPE_INIT,
-                                      this);
-
-        // Go over all referenced methods.
-        programClassFile.constantPoolEntriesAccept(this);
-
-        // Go over all methods that may have implementations.
-        programClassFile.methodsAccept(filteredMethodMarker);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Go over all methods.
-        libraryClassFile.methodsAccept(this);
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        // Implementations of the referenced method can never be made private.
-        interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        ClassFile referencedClassFile = methodrefCpInfo.referencedClassFile;
-
-        // Is it refering to a method in another class file?
-        if (referencedClassFile != null &&
-            !referencedClassFile.equals(classFile))
-        {
-            // The referenced method can never be made private.
-            methodrefCpInfo.referencedMemberInfoAccept(this);
-        }
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        markCanNotBeMadePrivate(programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        markCanNotBeMadePrivate(libraryMethodInfo);
-    }
-
-
-    // Small utility methods.
-
-    public static void markCanNotBeMadePrivate(MethodInfo methodInfo)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        if (info != null)
-        {
-            info.setCanNotBeMadePrivate();
-        }
-    }
-
-
-    public static boolean canBeMadePrivate(MethodInfo methodInfo)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        return info != null &&
-               info.canBeMadePrivate();
-    }
-}
diff --git a/src/proguard/optimize/OptimizationInfoMemberFilter.java b/src/proguard/optimize/OptimizationInfoMemberFilter.java
new file mode 100644
index 0000000..329d195
--- /dev/null
+++ b/src/proguard/optimize/OptimizationInfoMemberFilter.java
@@ -0,0 +1,94 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.visitor.MemberVisitor;
+import proguard.optimize.info.*;
+
+/**
+ * This <code>MemberVisitor</code> delegates its visits to another given
+ * <code>MemberVisitor</code>, but only when the visited member has optimization
+ * info.
+ *
+ * @see FieldOptimizationInfo
+ * @see MethodOptimizationInfo
+ * @author Eric Lafortune
+ */
+public class OptimizationInfoMemberFilter
+implements   MemberVisitor
+{
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new OptimizationInfoMemberFilter.
+     * @param memberVisitor the <code>MemberVisitor</code> to which visits will
+     *                      be delegated.
+     */
+    public OptimizationInfoMemberFilter(MemberVisitor memberVisitor)
+    {
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Does the field have optimization info?
+        if (FieldOptimizationInfo.getFieldOptimizationInfo(programField) != null)
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        // Does the field have optimization info?
+        if (FieldOptimizationInfo.getFieldOptimizationInfo(libraryField) != null)
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Does the method have optimization info?
+        if (MethodOptimizationInfo.getMethodOptimizationInfo(programMethod) != null)
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        // Does the method have optimization info?
+        if (MethodOptimizationInfo.getMethodOptimizationInfo(libraryMethod) != null)
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+}
diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java
index a929c7c..6e40409 100644
--- a/src/proguard/optimize/Optimizer.java
+++ b/src/proguard/optimize/Optimizer.java
@@ -1,6 +1,6 @@
-/* $Id: Optimizer.java,v 1.9.2.6 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,12 +23,15 @@ package proguard.optimize;
 
 import proguard.*;
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.visitor.AllConstantVisitor;
 import proguard.classfile.editor.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.util.MethodInfoLinker;
+import proguard.classfile.instruction.visitor.*;
+import proguard.classfile.util.MethodLinker;
 import proguard.classfile.visitor.*;
-import proguard.optimize.evaluation.EvaluationSimplifier;
+import proguard.evaluation.value.SpecificValueFactory;
+import proguard.optimize.evaluation.*;
+import proguard.optimize.info.*;
 import proguard.optimize.peephole.*;
 
 import java.io.IOException;
@@ -40,7 +43,7 @@ import java.io.IOException;
  */
 public class Optimizer
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -55,139 +58,80 @@ public class Optimizer
     /**
      * Performs optimization of the given program class pool.
      */
-    public void execute(ClassPool programClassPool,
-                        ClassPool libraryClassPool) throws IOException
+    public boolean execute(ClassPool programClassPool,
+                           ClassPool libraryClassPool) throws IOException
     {
-        // Create counters to count the numbers of optimizations, if required.
-        ClassCounter       singleImplementationCounter = null;
-        ClassCounter       finalClassCounter           = null;
-        MemberCounter      writeOnlyFieldCounter       = null;
-        MemberCounter      finalMethodCounter          = null;
-        MemberCounter      privateMethodCounter        = null;
-        MemberCounter      staticMethodCounter         = null;
-        MemberCounter      parameterShrinkCounter      = null;
-        InstructionCounter getterSetterCounter         = null;
-        InstructionCounter commonCodeCounter           = null;
-        InstructionCounter pushCounter                 = null;
-        InstructionCounter branchCounter               = null;
-        InstructionCounter deletedCounter              = null;
-//      InstructionCounter nopCounter                  = null;
-        InstructionCounter pushPopCounter              = null;
-        InstructionCounter loadStoreCounter            = null;
-        InstructionCounter storeLoadCounter            = null;
-        InstructionCounter gotoGotoCounter             = null;
-        InstructionCounter gotoReturnCounter           = null;
-
-        if (configuration.verbose)
+        // Check if we have at least some keep commands.
+        if (configuration.keep         == null &&
+            configuration.applyMapping == null &&
+            configuration.printMapping == null)
         {
-            singleImplementationCounter = new ClassCounter();
-            finalClassCounter           = new ClassCounter();
-            writeOnlyFieldCounter       = new MemberCounter();
-            finalMethodCounter          = new MemberCounter();
-            privateMethodCounter        = new MemberCounter();
-            staticMethodCounter         = new MemberCounter();
-            parameterShrinkCounter      = new MemberCounter();
-            getterSetterCounter         = new InstructionCounter();
-            commonCodeCounter           = new InstructionCounter();
-            pushCounter                 = new InstructionCounter();
-            branchCounter               = new InstructionCounter();
-            deletedCounter              = new InstructionCounter();
-//          nopCounter                  = new InstructionCounter();
-            pushPopCounter              = new InstructionCounter();
-            loadStoreCounter            = new InstructionCounter();
-            storeLoadCounter            = new InstructionCounter();
-            gotoGotoCounter             = new InstructionCounter();
-            gotoReturnCounter           = new InstructionCounter();
+            throw new IOException("You have to specify '-keep' options for the optimization step.");
         }
 
+        // Create counters to count the numbers of optimizations.
+        ClassCounter       singleImplementationCounter = 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      parameterShrinkCounter      = new MemberCounter();
+        MemberCounter      variableShrinkCounter       = new MemberCounter();
+        ExceptionCounter   exceptionCounter            = new ExceptionCounter();
+        InstructionCounter inliningCounter             = 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();
+
         // Clean up any old visitor info.
-        programClassPool.classFilesAccept(new ClassFileCleaner());
-        libraryClassPool.classFilesAccept(new ClassFileCleaner());
+        programClassPool.classesAccept(new ClassCleaner());
+        libraryClassPool.classesAccept(new ClassCleaner());
 
         // Link all methods that should get the same optimization info.
-        programClassPool.classFilesAccept(new BottomClassFileFilter(
-                                          new MethodInfoLinker()));
-        libraryClassPool.classFilesAccept(new BottomClassFileFilter(
-                                          new MethodInfoLinker()));
+        programClassPool.classesAccept(new BottomClassFilter(
+                                       new MethodLinker()));
+        libraryClassPool.classesAccept(new BottomClassFilter(
+                                       new MethodLinker()));
 
         // Create a visitor for marking the seeds.
         KeepMarker keepMarker = new KeepMarker();
         ClassPoolVisitor classPoolvisitor =
-            new MultiClassPoolVisitor(new ClassPoolVisitor[]
-            {
-                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
-                                                                        keepMarker,
-                                                                        keepMarker),
-                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keepNames,
-                                                                        keepMarker,
-                                                                        keepMarker)
-            });
-
+            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
+                                                                    keepMarker,
+                                                                    keepMarker,
+                                                                    false,
+                                                                    true,
+                                                                    false);
         // Mark the seeds.
         programClassPool.accept(classPoolvisitor);
         libraryClassPool.accept(classPoolvisitor);
 
         // All library classes and library class members remain unchanged.
-        libraryClassPool.classFilesAccept(keepMarker);
-        libraryClassPool.classFilesAccept(new AllMemberInfoVisitor(keepMarker));
+        libraryClassPool.classesAccept(keepMarker);
+        libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker));
 
         // We also keep all classes that are involved in .class constructs.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new AllAttrInfoVisitor(
-                                          new AllInstructionVisitor(
-                                          new DotClassClassFileVisitor(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.classFilesAccept(new AllCpInfoVisitor(
-                                          new ClassForNameClassFileVisitor(keepMarker)));
+        programClassPool.classesAccept(new AllConstantVisitor(
+                                       new ClassForNameClassVisitor(keepMarker)));
 
-        // Attach some optimization info to all methods, so it can be filled
-        // out later.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new MethodOptimizationInfoSetter()));
-        libraryClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new MethodOptimizationInfoSetter()));
-
-        // Mark all interfaces that have single implementations.
-        programClassPool.classFilesAccept(new SingleImplementationMarker(configuration.allowAccessModification, singleImplementationCounter));
-
-        // Make class files and methods final, as far as possible.
-        programClassPool.classFilesAccept(new ClassFileFinalizer(finalClassCounter, finalMethodCounter));
-
-        // Mark all fields that are write-only, and mark the used local variables.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new AllAttrInfoVisitor(
-                                          new AllInstructionVisitor(
-                                          new MultiInstructionVisitor(
-                                          new InstructionVisitor[]
-                                          {
-                                              new WriteOnlyFieldMarker(),
-                                              new VariableUsageMarker(),
-                                          })))));
-
-        // Mark all fields that are write-only, and mark the used local variables.
-        if (configuration.verbose)
-        {
-            programClassPool.classFilesAccept(new AllFieldVisitor(
-                                              new WriteOnlyFieldFilter(
-                                              writeOnlyFieldCounter)));
-        }
-
-        // Mark all methods that can not be made private.
-        programClassPool.classFilesAccept(new NonPrivateMethodMarker());
-        libraryClassPool.classFilesAccept(new NonPrivateMethodMarker());
-
-        // Make all non-private and unmarked methods in final classes private.
-        programClassPool.classFilesAccept(new ClassFileAccessFilter(ClassConstants.INTERNAL_ACC_FINAL, 0,
-                                          new AllMethodVisitor(
-                                          new MemberInfoAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
-                                          new MethodPrivatizer(privateMethodCounter)))));
-
-        // Mark all used parameters, including the 'this' parameters.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new ParameterUsageMarker()));
-        libraryClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new ParameterUsageMarker()));
+        // Attach some optimization info to all class members, so it can be
+        // filled out later.
+        programClassPool.classesAccept(new AllMemberVisitor(
+                                       new MemberOptimizationInfoSetter()));
 
         if (configuration.assumeNoSideEffects != null)
         {
@@ -203,107 +147,277 @@ public class Optimizer
             libraryClassPool.accept(noClassPoolvisitor);
         }
 
+        // Mark all interfaces that have single implementations.
+        programClassPool.classesAccept(new SingleImplementationMarker(configuration.allowAccessModification, singleImplementationCounter));
+
+        // Make classes and methods final, as far as possible.
+        programClassPool.classesAccept(new ClassFinalizer(finalClassCounter, finalMethodCounter));
+
+        // Mark all fields that are write-only.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new AllInstructionVisitor(
+                                       new MultiInstructionVisitor(
+                                       new InstructionVisitor[]
+                                       {
+                                           new ReadWriteFieldMarker(),
+                                       })))));
+
+        // Mark all used parameters, including the 'this' parameters.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new OptimizationInfoMemberFilter(
+                                       new ParameterUsageMarker())));
+
+        // 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)))));
+
+        // Remove all unused parameters and variables from the code, for
+        // MethodDescriptorShrinker, MethodStaticizer.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new ParameterShrinker(parameterShrinkCounter))));
+
+        // Fix invocations of methods that have become static, for
+        // MethodStaticizer.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new MethodInvocationFixer())));
+
+        // Fix all references to class members, for MethodDescriptorShrinker.
+        programClassPool.classesAccept(new MemberReferenceFixer());
+
         // Mark all methods that have side effects.
         programClassPool.accept(new SideEffectMethodMarker());
 
-        // Perform partial evaluation.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new EvaluationSimplifier(pushCounter, branchCounter, deletedCounter)));
+//        System.out.println("Optimizer.execute: before evaluation simplification");
+//        programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter()));
+
+        // Perform partial evaluation for filling out fields, method parameters,
+        // and method return values.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new PartialEvaluator(new SpecificValueFactory(), new UnusedParameterInvocationUnit(new StoringInvocationUnit()), false))));
+
+        // 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)),
+                                       }));
+
+        // Simplify based on partial evaluation.
+        // Also remove unused parameters from the stack before method invocations,
+        // for MethodDescriptorShrinker, MethodStaticizer.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new EvaluationSimplifier(
+                                       new PartialEvaluator(new SpecificValueFactory(), new UnusedParameterInvocationUnit(new LoadingInvocationUnit()), false),
+                                       pushCounter, branchCounter, deletedCounter, addedCounter))));
+
+//        // Specializing the class member descriptors seems to increase the
+//        // class file size, on average.
+//        // Specialize all class member descriptors.
+//        programClassPool.classesAccept(new AllMemberVisitor(
+//                                       new OptimizationInfoMemberFilter(
+//                                       new MemberDescriptorSpecializer())));
+//
+//        // Fix all references to classes, for MemberDescriptorSpecializer.
+//        programClassPool.classesAccept(new AllMemberVisitor(
+//                                       new OptimizationInfoMemberFilter(
+//                                       new ClassReferenceFixer(true))));
 
         // Inline interfaces with single implementations.
-        programClassPool.classFilesAccept(new SingleImplementationInliner());
+        programClassPool.classesAccept(new SingleImplementationInliner());
 
         // Restore the interface references from these single implementations.
-        programClassPool.classFilesAccept(new SingleImplementationFixer());
+        programClassPool.classesAccept(new SingleImplementationFixer());
 
-        // Shrink the method parameters and make methods static.
-        programClassPool.classFilesAccept(new AllMethodVisitor(
-                                          new ParameterShrinker(1024, 64, parameterShrinkCounter, staticMethodCounter)));
+        if (configuration.allowAccessModification)
+        {
+            // Fix the access flags of referenced classes and class members,
+            // for SingleImplementationInliner.
+            programClassPool.classesAccept(new AllConstantVisitor(
+                                           new AccessFixer()));
+        }
 
-        // Fix all references to class files.
-        programClassPool.classFilesAccept(new ClassFileReferenceFixer(true));
+        // Fix all references to classes, for SingleImplementationInliner.
+        programClassPool.classesAccept(new ClassReferenceFixer(true));
+
+        // Fix all references to class members, for SingleImplementationInliner.
+        programClassPool.classesAccept(new MemberReferenceFixer());
+
+        // Count all method invocations.
+        // Mark super invocations and other access of methods.
+        // Mark all exception catches of methods.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new MultiAttributeVisitor(
+                                       new AttributeVisitor[]
+                                       {
+                                           new AllInstructionVisitor(
+                                           new MultiInstructionVisitor(
+                                           new InstructionVisitor[]
+                                           {
+                                               new MethodInvocationMarker(),
+                                               new SuperInvocationMarker(),
+                                               new BackwardBranchMarker(),
+                                               new AccessMethodMarker(),
+                                           })),
+                                           new CatchExceptionMarker(),
+                                       }))));
+
+        // Inline methods that are only invoked once.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new MethodInliner(configuration.allowAccessModification, true, inliningCounter))));
+
+        // Inline short methods.
+        programClassPool.classesAccept(new AllMethodVisitor(
+                                       new AllAttributeVisitor(
+                                       new MethodInliner(configuration.allowAccessModification, false, inliningCounter))));
+
+        // Mark all class members that can not be made private.
+        programClassPool.classesAccept(new NonPrivateMemberMarker());
+
+        // 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 (configuration.allowAccessModification)
+        {
+            // Fix the access flags of referenced classes and class members,
+            // for MethodInliner.
+            programClassPool.classesAccept(new AllConstantVisitor(
+                                           new AccessFixer()));
+        }
 
-        // Fix all references to class members.
-        programClassPool.classFilesAccept(new MemberReferenceFixer(1024));
+        // Fix invocations of methods that have become non-abstract or private,
+        // for SingleImplementationInliner, MemberPrivatizer, AccessFixer.
+        programClassPool.classesAccept(new AllMemberVisitor(
+                                       new AllAttributeVisitor(
+                                       new MethodInvocationFixer())));
 
         // Create a branch target marker and a code attribute editor that can
         // be reused for all code attributes.
-        BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
-        CodeAttrInfoEditor codeAttrInfoEditor = new CodeAttrInfoEditor(1024);
-
-        // Visit all code attributes.
-        // First let the branch marker mark all branch targets.
-        // Then share common blocks of code at branches.
-        // Finally apply all changes to the code.
-        programClassPool.classFilesAccept(
-            new AllMethodVisitor(
-            new AllAttrInfoVisitor(
-            new MultiAttrInfoVisitor(
-            new AttrInfoVisitor[]
-            {
-                branchTargetFinder,
-                new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
-                new AllInstructionVisitor(
-                new GotoCommonCodeReplacer(branchTargetFinder, codeAttrInfoEditor, commonCodeCounter)),
-                codeAttrInfoEditor
-            }))));
-
-        // Visit all code attributes.
-        // First let the branch marker mark all branch targets.
-        // Then perform peephole optimisations on the instructions:
-        // - Fix invocations of methods that have become private, static,...
-        // - Remove nop instructions.
-        // - Remove push/pop instruction pairs.
-        // - Remove load/store instruction pairs.
-        // - Replace store/load instruction pairs by dup/store instructions.
-        // - Simplify branches to branch instructions.
-        // - Replace branches to return instructions by return instructions.
-        // - Inline simple getters and setters.
-        // Finally apply all changes to the code.
-        programClassPool.classFilesAccept(
-            new AllMethodVisitor(
-            new AllAttrInfoVisitor(
-            new MultiAttrInfoVisitor(
-            new AttrInfoVisitor[]
-            {
-                branchTargetFinder,
-                new CodeAttrInfoEditorResetter(codeAttrInfoEditor),
-                new AllInstructionVisitor(
-                new MultiInstructionVisitor(
-                new InstructionVisitor[]
-                {
-                    new MethodInvocationFixer(                                     codeAttrInfoEditor),
-//                  new NopRemover(                                                codeAttrInfoEditor, nopCounter),
-                    new PushPopRemover(        branchTargetFinder,                 codeAttrInfoEditor, pushPopCounter),
-                    new LoadStoreRemover(      branchTargetFinder,                 codeAttrInfoEditor, loadStoreCounter),
-                    new StoreLoadReplacer(     branchTargetFinder,                 codeAttrInfoEditor, storeLoadCounter),
-                    new GotoGotoReplacer(                                          codeAttrInfoEditor, gotoGotoCounter),
-                    new GotoReturnReplacer(                                        codeAttrInfoEditor, gotoReturnCounter),
-                    new GetterSetterInliner(configuration.allowAccessModification, codeAttrInfoEditor, getterSetterCounter),
-                })),
-                codeAttrInfoEditor
-            }))));
+        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 singleImplementationCount = singleImplementationCounter.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();
+        int parameterShrinkCount      = parameterShrinkCounter     .getCount();
+        int variableShrinkCount       = variableShrinkCounter      .getCount();
+        int exceptionCount            = exceptionCounter           .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();
 
         if (configuration.verbose)
         {
-            System.out.println("  Number of inlined interfaces:             "+singleImplementationCounter.getCount());
-            System.out.println("  Number of finalized classes:              "+finalClassCounter          .getCount());
-            System.out.println("  Number of removed write-only fields:      "+writeOnlyFieldCounter      .getCount());
-            System.out.println("  Number of finalized methods:              "+finalMethodCounter         .getCount());
-            System.out.println("  Number of privatized methods:             "+privateMethodCounter       .getCount());
-            System.out.println("  Number of staticized methods:             "+staticMethodCounter        .getCount());
-            System.out.println("  Number of simplified method declarations: "+parameterShrinkCounter     .getCount());
-            System.out.println("  Number of inlined getters/setter calls:   "+getterSetterCounter        .getCount());
-            System.out.println("  Number of merged code blocks:             "+commonCodeCounter          .getCount());
-            System.out.println("  Number of simplified push instructions:   "+pushCounter                .getCount());
-            System.out.println("  Number of simplified branches:            "+branchCounter              .getCount());
-            System.out.println("  Number of removed instructions:           "+deletedCounter             .getCount());
-//          System.out.println("  Number of removed nop instructions:       "+nopCounter                 .getCount());
-            System.out.println("  Number of removed push/pop pairs:         "+pushPopCounter             .getCount());
-            System.out.println("  Number of removed load/store pairs:       "+loadStoreCounter           .getCount());
-            System.out.println("  Number of simplified store/load pairs:    "+storeLoadCounter           .getCount());
-            System.out.println("  Number of simplified goto/goto pairs:     "+gotoGotoCounter            .getCount());
-            System.out.println("  Number of simplified goto/return pairs:   "+gotoReturnCounter          .getCount());
+            System.out.println("  Number of inlined interfaces:             "+singleImplementationCount);
+            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 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 singleImplementationCount > 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 ||
+               exceptionCount            > 0 ||
+               commonCodeCount           > 0 ||
+               pushCount                 > 0 ||
+               branchCount               > 0 ||
+               removedCount              > 0 ||
+               peepholeCount             > 0;
     }
 }
diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java
index 980ac3b..e56941c 100644
--- a/src/proguard/optimize/ParameterShrinker.java
+++ b/src/proguard/optimize/ParameterShrinker.java
@@ -1,377 +1,122 @@
-/* $Id: ParameterShrinker.java,v 1.6.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
- * This program is free software; you can redistribute it and/or modify it
+ * This library is 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
+ * This library 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.
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 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
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 package proguard.optimize;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.editor.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.VariableEditor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.info.ParameterUsageMarker;
 
 /**
- * This MemberInfoVisitor removes unused parameters from the descriptors and
- * the code of the methods that it visits. It also makes methods static if the
- * 'this' parameter is unused.
+ * This MemberVisitor removes unused parameters from the code of the methods
+ * that it visits.
  *
  * @see ParameterUsageMarker
+ * @see MethodStaticizer
+ * @see MethodDescriptorShrinker
  * @author Eric Lafortune
  */
 public class ParameterShrinker
-  implements MemberInfoVisitor,
-             AttrInfoVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor
 {
     private static final boolean DEBUG = false;
 
 
-    private MemberInfoVisitor extraParameterMemberInfoVisitor;
-    private MemberInfoVisitor extraStaticMemberInfoVisitor;
-
-    private VariableEditor     variableEditor;
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private final MemberVisitor extraVariableMemberVisitor;
 
-    // A parameter for the parameter annotation visiting methods.
-    private MethodInfo methodInfo;
+    private final VariableEditor variableEditor = new VariableEditor();
 
 
     /**
      * Creates a new ParameterShrinker.
-     * @param codeLength an estimate of the maximum length of all the code
-     *                   that will be edited.
-     * @param maxLocals  an estimate of the maximum length of all the local
-     *                   variable frames that will be edited.
      */
-    public ParameterShrinker(int codeLength, int maxLocals)
+    public ParameterShrinker()
     {
-        this(codeLength, maxLocals, null, null);
+        this(null);
     }
 
 
     /**
-     * Creates a new ParameterShrinker.
-     * @param codeLength an estimate of the maximum length of all the code
-     *                   that will be edited.
-     * @param maxLocals  an estimate of the maximum length of all the local
-     *                   variable frames that will be edited.
-     * @param extraParameterMemberInfoVisitor an optional extra visitor for all
-     *                                        methods whose parameters have been
-     *                                        simplified.
-     * @param extraStaticMemberInfoVisitor    an optional extra visitor for all
-     *                                        methods that have been made static.
+     * Creates a new ParameterShrinker with an extra visitor.
+     * @param extraVariableMemberVisitor an optional extra visitor for all
+     *                                   removed parameters.
      */
-    public ParameterShrinker(int codeLength, int maxLocals,
-                             MemberInfoVisitor extraParameterMemberInfoVisitor,
-                             MemberInfoVisitor extraStaticMemberInfoVisitor)
+    public ParameterShrinker(MemberVisitor extraVariableMemberVisitor)
     {
-        this.variableEditor = new VariableEditor(codeLength, maxLocals);
-
-        this.extraParameterMemberInfoVisitor = extraParameterMemberInfoVisitor;
-        this.extraStaticMemberInfoVisitor    = extraStaticMemberInfoVisitor;
+        this.extraVariableMemberVisitor = extraVariableMemberVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        // Update the descriptor if it has any unused parameters.
-        String descriptor    = programMethodInfo.getDescriptor(programClassFile);
-        String newDescriptor = shrinkDescriptor(programClassFile, programMethodInfo);
-        if (!descriptor.equals(newDescriptor))
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
         {
-            // Update the parameter annotations.
-            this.methodInfo = programMethodInfo;
-            programMethodInfo.attributesAccept(programClassFile, this);
+            // Get the total size of the local variable frame.
+            int variablesSize = codeAttribute.u2maxLocals;
 
-            // TODO: Avoid duplicate constructors.
-            String name    = programMethodInfo.getName(programClassFile);
-            String newName = name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
-                                 ClassConstants.INTERNAL_METHOD_NAME_INIT :
-                                 name + '$' + Long.toHexString(Math.abs((descriptor).hashCode()));
+            // The descriptor may have been been shrunk already, so get the
+            // original parameter size.
+            int parameterSize = ParameterUsageMarker.getParameterSize(method);
 
             if (DEBUG)
             {
-                System.out.println("ParameterShrinker:");
-                System.out.println("  Class file        = "+programClassFile.getName());
-                System.out.println("  Method name       = "+name);
-                System.out.println("                   -> "+newName);
-                System.out.println("  Method descriptor = "+descriptor);
-                System.out.println("                   -> "+newDescriptor);
-            }
-
-            // Update the name, if necessary.
-            if (!newName.equals(name))
-            {
-                programMethodInfo.u2nameIndex =
-                constantPoolEditor.addUtf8CpInfo(programClassFile, newName);
+                System.out.println("ParameterShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+                System.out.println("  parameter size = " + parameterSize);
             }
 
-            // Clear the unused referenced class files.
-            shrinkReferencedClassFiles(programClassFile, programMethodInfo);
+            // Delete unused variables from the local variable frame.
+            variableEditor.reset(variablesSize);
 
-            // Update the descriptor.
-            programMethodInfo.u2descriptorIndex =
-                constantPoolEditor.addUtf8CpInfo(programClassFile, newDescriptor);
-
-            // Visit the method, if required.
-            if (extraParameterMemberInfoVisitor != null)
-            {
-                extraParameterMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-            }
-        }
-
-        // Delete unused variables from the local variable frame.
-        variableEditor.reset(64);
-
-        for (int variableIndex = 0; variableIndex < 64; variableIndex++)
-        {
-            if (!VariableUsageMarker.isVariableUsed(programMethodInfo, variableIndex))
-            {
-                // Delete the unused variable.
-                variableEditor.deleteVariable(variableIndex);
-            }
-        }
-
-        // Shift all remaining variables in the byte code.
-        programMethodInfo.attributesAccept(programClassFile, variableEditor);
-
-        // Is the method not static, and can it be made static?
-        int accessFlags = programMethodInfo.getAccessFlags();
-        if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0 &&
-            !VariableUsageMarker.isVariableUsed(programMethodInfo, 0))
-        {
-            if (DEBUG)
-            {
-                System.out.println("ParameterShrinker:");
-                System.out.println("  Class file = "+programClassFile.getName());
-                System.out.println("  Method     = "+programMethodInfo.getName(programClassFile)+programMethodInfo.getDescriptor(programClassFile));
-                System.out.println("    -> static");
-            }
-
-            // Make the method static.
-            programMethodInfo.u2accessFlags =
-                (accessFlags & ~ClassConstants.INTERNAL_ACC_FINAL) |
-                ClassConstants.INTERNAL_ACC_STATIC;
-
-            // Visit the method, if required.
-            if (extraStaticMemberInfoVisitor != null)
-            {
-                extraStaticMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-            }
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
-    {
-        // Update the parameter annotations.
-        shrinkParameterAnnotations(classFile, runtimeVisibleParameterAnnotationsAttrInfo);
-    }
-
-
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
-    {
-        // Update the parameter annotations.
-        shrinkParameterAnnotations(classFile, runtimeInvisibleParameterAnnotationsAttrInfo);
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Shrinks the given parameter annotations.
-     */
-    private void shrinkParameterAnnotations(ClassFile                           classFile,
-                                            RuntimeParameterAnnotationsAttrInfo runtimeParameterAnnotationsAttrInfo)
-    {
-        Annotation[][] annotations = runtimeParameterAnnotationsAttrInfo.parameterAnnotations;
-
-        // All parameters of non-static methods are shifted by one in the local
-        // variable frame.
-        int parameterIndex =
-            (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
-                0 : 1;
-
-        int annotationIndex    = 0;
-        int newAnnotationIndex = 0;
-
-        // Go over the parameters.
-        String descriptor = methodInfo.getDescriptor(classFile);
-        InternalTypeEnumeration internalTypeEnumeration =
-            new InternalTypeEnumeration(descriptor);
-
-        while (internalTypeEnumeration.hasMoreTypes())
-        {
-            String type = internalTypeEnumeration.nextType();
-            if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
+            for (int parameterIndex = 0; parameterIndex < parameterSize; parameterIndex++)
             {
-                annotations[newAnnotationIndex++] = annotations[annotationIndex];
-            }
-
-            annotationIndex++;
-
-            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
-        }
-
-        runtimeParameterAnnotationsAttrInfo.u2numberOfParameters = newAnnotationIndex;
-
-        // Clear the unused entries.
-        while (newAnnotationIndex < annotationIndex)
-        {
-            annotations[newAnnotationIndex++] = null;
-        }
-    }
-
-
-    /**
-     * Returns a shrunk descriptor of the given method.
-     */
-    public static String shrinkDescriptor(ProgramClassFile  classFile,
-                                          ProgramMethodInfo methodInfo)
-    {
-        // All parameters of non-static methods are shifted by one in the local
-        // variable frame.
-        int parameterIndex =
-            (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
-                0 : 1;
-
-        // Go over the parameters.
-        InternalTypeEnumeration internalTypeEnumeration =
-            new InternalTypeEnumeration(methodInfo.getDescriptor(classFile));
-
-        StringBuffer newDescriptorBuffer = new StringBuffer();
-        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
-
-        while (internalTypeEnumeration.hasMoreTypes())
-        {
-            String type = internalTypeEnumeration.nextType();
-            if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
-            {
-                newDescriptorBuffer.append(type);
-            }
-
-            parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
-        }
-
-        newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
-        newDescriptorBuffer.append(internalTypeEnumeration.returnType());
-
-        return newDescriptorBuffer.toString();
-    }
-
-
-    /**
-     * Shrinks the array of referenced class files of the given method.
-     */
-    private static void shrinkReferencedClassFiles(ProgramClassFile  classFile,
-                                                   ProgramMethodInfo methodInfo)
-    {
-        ClassFile[] referencedClassFiles = methodInfo.referencedClassFiles;
-
-        if (referencedClassFiles != null)
-        {
-            // All parameters of non-static methods are shifted by one in the local
-            // variable frame.
-            int parameterIndex =
-                (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
-                    0 : 1;
-
-            int referencedClassFileIndex    = 0;
-            int newReferencedClassFileIndex = 0;
-
-            // Go over the parameters.
-            String descriptor = methodInfo.getDescriptor(classFile);
-            InternalTypeEnumeration internalTypeEnumeration =
-                new InternalTypeEnumeration(descriptor);
-
-            while (internalTypeEnumeration.hasMoreTypes())
-            {
-                String type = internalTypeEnumeration.nextType();
-                if (ClassUtil.isInternalArrayType(type))
-                {
-                    type = ClassUtil.internalTypeFromArrayType(type);
-                }
-
-                if (ClassUtil.isInternalClassType(type))
+                // Is the variable not required as a parameter?
+                if (!ParameterUsageMarker.isParameterUsed(method, parameterIndex))
                 {
-                    if (VariableUsageMarker.isVariableUsed(methodInfo, parameterIndex))
+                    if (DEBUG)
                     {
-                        referencedClassFiles[newReferencedClassFileIndex++] =
-                            referencedClassFiles[referencedClassFileIndex];
+                        System.out.println("  Deleting parameter #"+parameterIndex);
                     }
 
-                    referencedClassFileIndex++;
-                }
-
-                parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
-            }
+                    // Delete the unused variable.
+                    variableEditor.deleteVariable(parameterIndex);
 
-            // Also look at the return value.
-            String type = internalTypeEnumeration.returnType();
-            if (ClassUtil.isInternalArrayType(type))
-            {
-                type = ClassUtil.internalTypeFromArrayType(type);
-            }
-
-            if (ClassUtil.isInternalClassType(type))
-            {
-                referencedClassFiles[newReferencedClassFileIndex++] =
-                    referencedClassFiles[referencedClassFileIndex];
-
-                referencedClassFileIndex++;
+                    // Visit the method, if required.
+                    if (extraVariableMemberVisitor != null)
+                    {
+                        method.accept(clazz, extraVariableMemberVisitor);
+                    }
+                }
             }
 
-            // Clear the unused entries.
-            while (newReferencedClassFileIndex < referencedClassFileIndex)
-            {
-                referencedClassFiles[newReferencedClassFileIndex++] = null;
-            }
+            // Shift all remaining parameters and variables in the byte code.
+            variableEditor.visitCodeAttribute(clazz, method, codeAttribute);
         }
     }
 }
diff --git a/src/proguard/optimize/ParameterUsageMarker.java b/src/proguard/optimize/ParameterUsageMarker.java
deleted file mode 100644
index 87312d2..0000000
--- a/src/proguard/optimize/ParameterUsageMarker.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/* $Id: ParameterUsageMarker.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This MemberInfoVisitor marks the used method parameters methods in the
- * class hierarchies. The used variables must have been marked first. The
- * 'this' parameters of methods that have a hierarchy are marked too.
- *
- * @see VariableUsageMarker
- * @author Eric Lafortune
- */
-public class ParameterUsageMarker
-implements   MemberInfoVisitor
-{
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Is this a native method?
-        if ((programMethodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
-        {
-            // All parameters can be considered as being used.
-            long usedParameters = parameterMask(programClassFile, programMethodInfo);
-
-            // Is this a non-static method?
-            if ((programMethodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
-            {
-                // The 'this' parameter can be considered as being used too.
-                usedParameters = 1L | (usedParameters << 1);
-            }
-
-            // Mark the parameters.
-            markUsedVariables(programMethodInfo, usedParameters);
-        }
-
-        // Can the method have other implementations?
-        if (programClassFile.mayHaveImplementations(programMethodInfo))
-        {
-            // All implementations must at least keep the 'this' parameter too.
-            long usedParameters = 1L;
-
-            // Mark the parameters.
-            markUsedVariables(programMethodInfo, usedParameters);
-        }
-        // Is it an <init> method?
-        else if (programMethodInfo.getName(programClassFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
-        {
-            // Instance initializers always require the 'this' parameter.
-            long usedParameters = 1L;
-
-            // Is there a name clash with some existing <init> method?
-            if (programClassFile.findMethod(ClassConstants.INTERNAL_METHOD_NAME_INIT,
-                                            ParameterShrinker.shrinkDescriptor(programClassFile,
-                                                                               programMethodInfo)) != null)
-            {
-                // The shrinking is off. Mark all parameters.
-                usedParameters |=  parameterMask(programClassFile, programMethodInfo) << 1;
-            }
-
-            // Mark the parameters.
-            markUsedVariables(programMethodInfo, usedParameters);
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Can the method have other implementations?
-        if (libraryClassFile.mayHaveImplementations(libraryMethodInfo))
-        {
-            // All implementations must keep all parameters of this method,
-            // including the 'this' parameter.
-            long usedParameters = 1L |
-                                  (parameterMask(libraryClassFile, libraryMethodInfo) << 1);
-
-            // Mark it.
-            markUsedVariables(libraryMethodInfo, usedParameters);
-        }
-    }
-
-
-    // Small utility methods.
-
-    private void markUsedVariables(MethodInfo methodInfo, long usedParameters)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        if (info != null)
-        {
-            info.setUsedVariables(info.getUsedVariables() | usedParameters);
-        }
-    }
-
-
-    public static long getUsedVariables(MethodInfo methodInfo)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        return info != null ? info.getUsedVariables() : -1L;
-    }
-
-
-    /**
-     * Returns the mask of the parameters on the stack, for the given method,
-     * not including the 'this' parameter, if any.
-     */
-    private int parameterMask(ClassFile classFile, MethodInfo methodInfo)
-    {
-        return (1 << ClassUtil.internalMethodParameterSize(methodInfo.getDescriptor(classFile))) - 1;
-    }
-}
diff --git a/src/proguard/optimize/SideEffectInstructionChecker.java b/src/proguard/optimize/SideEffectInstructionChecker.java
deleted file mode 100644
index 287ff93..0000000
--- a/src/proguard/optimize/SideEffectInstructionChecker.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/* $Id: SideEffectInstructionChecker.java,v 1.12.2.5 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This class can tell whether an instruction has any side effects. Return
- * instructions can be included or not.
- *
- * @see WriteOnlyFieldMarker
- * @see NoSideEffectMethodMarker
- * @see SideEffectMethodMarker
- * @author Eric Lafortune
- */
-public class SideEffectInstructionChecker
-  implements InstructionVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor
-{
-    private boolean includeReturnInstructions;
-
-    // An argument for the visitor methods.
-    private boolean isReading;
-    
-    // A return value for the visitor methods.
-    private boolean hasSideEffects;
-
-
-    public SideEffectInstructionChecker(boolean includeReturnInstructions)
-    {
-        this.includeReturnInstructions = includeReturnInstructions;
-    }
-
-
-    public boolean hasSideEffects(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, Instruction instruction)
-    {
-        hasSideEffects = false;
-
-        instruction.accept(classFile, methodInfo,  codeAttrInfo, offset, this);
-
-        return hasSideEffects;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
-    {
-        byte opcode = simpleInstruction.opcode;
-
-        // Check for instructions that might cause side effects.
-        if (opcode == InstructionConstants.OP_IASTORE      ||
-            opcode == InstructionConstants.OP_LASTORE      ||
-            opcode == InstructionConstants.OP_FASTORE      ||
-            opcode == InstructionConstants.OP_DASTORE      ||
-            opcode == InstructionConstants.OP_AASTORE      ||
-            opcode == InstructionConstants.OP_BASTORE      ||
-            opcode == InstructionConstants.OP_CASTORE      ||
-            opcode == InstructionConstants.OP_SASTORE      ||
-            opcode == InstructionConstants.OP_ATHROW       ||
-            opcode == InstructionConstants.OP_MONITORENTER ||
-            opcode == InstructionConstants.OP_MONITOREXIT  ||
-            (includeReturnInstructions &&
-             (opcode == InstructionConstants.OP_IRETURN ||
-              opcode == InstructionConstants.OP_LRETURN ||
-              opcode == InstructionConstants.OP_FRETURN ||
-              opcode == InstructionConstants.OP_DRETURN ||
-              opcode == InstructionConstants.OP_ARETURN ||
-              opcode == InstructionConstants.OP_RETURN)))
-        {
-            // These instructions always cause a side effect.
-            hasSideEffects = true;
-        }
-
-    }
-
-
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-        byte opcode = variableInstruction.opcode;
-
-        // Check for instructions that might cause side effects.
-        if (includeReturnInstructions &&
-            opcode == InstructionConstants.OP_RET)
-        {
-            hasSideEffects = true;
-        }
-    }
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        byte opcode = cpInstruction.opcode;
-
-        // Check for instructions that might cause side effects.
-        if      (opcode == InstructionConstants.OP_PUTSTATIC     ||
-                 opcode == InstructionConstants.OP_PUTFIELD      ||
-                 opcode == InstructionConstants.OP_INVOKEVIRTUAL ||
-                 opcode == InstructionConstants.OP_INVOKESPECIAL ||
-                 opcode == InstructionConstants.OP_INVOKESTATIC  ||
-                 opcode == InstructionConstants.OP_INVOKEINTERFACE)
-        {
-            // Check if the field is write-only, or if the invoked method is
-            // causing any side effects.
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        }
-    }
-
-
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
-    {
-        byte opcode = branchInstruction.opcode;
-
-        // Check for instructions that might cause side effects.
-        if (includeReturnInstructions &&
-            (opcode == InstructionConstants.OP_JSR ||
-             opcode == InstructionConstants.OP_JSR_W))
-        {
-            hasSideEffects = true;
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        MemberInfo referencedMember = fieldrefCpInfo.referencedMemberInfo;
-
-        // Do we have a reference to the field?
-        if (referencedMember == null)
-        {
-            // We'll have to assume accessing the unknown field has side effects.
-            hasSideEffects = true;
-        }
-        else
-        {
-            // Check the referenced field itself.
-            fieldrefCpInfo.referencedMemberInfoAccept(this);
-        }
-    }
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        MemberInfo referencedMemberInfo = interfaceMethodrefCpInfo.referencedMemberInfo;
-
-        // Do we have a reference to the interface method?
-        if (referencedMemberInfo == null)
-        {
-            // We'll have to assume the unknown interface method has side effects.
-            hasSideEffects = true;
-        }
-        else
-        {
-            // First check the referenced interface method itself.
-            interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
-
-            // If the result isn't conclusive, check down the hierarchy.
-            if (!hasSideEffects)
-            {
-                ClassFile  referencedClassFile  = interfaceMethodrefCpInfo.referencedClassFile;
-                MethodInfo referencedMethodInfo = (MethodInfo)referencedMemberInfo;
-
-                // Check all implementations of the method.
-                referencedClassFile.methodImplementationsAccept(referencedMethodInfo,
-                                                                false,
-                                                                this);
-            }
-        }
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        MemberInfo referencedMemberInfo = methodrefCpInfo.referencedMemberInfo;
-
-        // Do we have a reference to the method?
-        if (referencedMemberInfo == null)
-        {
-            // We'll have to assume the unknown method has side effects.
-            hasSideEffects = true;
-        }
-        else
-        {
-            // First check the referenced method itself.
-            methodrefCpInfo.referencedMemberInfoAccept(this);
-
-            // If the result isn't conclusive, check down the hierarchy.
-            if (!hasSideEffects)
-            {
-                ClassFile  referencedClassFile  = methodrefCpInfo.referencedClassFile;
-                MethodInfo referencedMethodInfo = (MethodInfo)referencedMemberInfo;
-
-                // Check all other implementations of the method in the class
-                // hierarchy.
-                referencedClassFile.methodImplementationsAccept(referencedMethodInfo,
-                                                                false,
-                                                                this);
-            }
-        }
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        hasSideEffects = isReading ?
-            (programFieldInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0 :
-            !WriteOnlyFieldMarker.isWriteOnly(programFieldInfo);
-    }
-    
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        hasSideEffects = hasSideEffects ||
-                         SideEffectMethodMarker.hasSideEffects(programMethodInfo);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        hasSideEffects = isReading ?
-            (libraryFieldInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0 :
-            !WriteOnlyFieldMarker.isWriteOnly(libraryFieldInfo);
-    }
-        
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        hasSideEffects = hasSideEffects ||
-                         !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethodInfo);
-    }
-}
diff --git a/src/proguard/optimize/SideEffectMethodMarker.java b/src/proguard/optimize/SideEffectMethodMarker.java
deleted file mode 100644
index 046a52b..0000000
--- a/src/proguard/optimize/SideEffectMethodMarker.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/* $Id: SideEffectMethodMarker.java,v 1.6.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This ClassPoolVisitor marks all methods that have side effects.
- *
- * @see WriteOnlyFieldMarker
- * @see NoSideEffectMethodMarker
- * @author Eric Lafortune
- */
-public class SideEffectMethodMarker
-  implements ClassPoolVisitor,
-             ClassFileVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor
-{
-    // A reusable object for checking whether instructions have side effects.
-    private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false);
-
-    // Parameters and values for visitor methods.
-    private int     newSideEffectCount;
-    private boolean hasSideEffects;
-
-
-    // Implementations for ClassPoolVisitor.
-
-    public void visitClassPool(ClassPool classPool)
-    {
-        // Go over all class files and their methods, marking if they have side
-        // effects, until no new cases can be found.
-        do
-        {
-            newSideEffectCount = 0;
-
-            // Go over all class files and their methods once.
-            classPool.classFilesAccept(this);
-        }
-        while (newSideEffectCount > 0);
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Go over all methods.
-        programClassFile.methodsAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        if (!hasSideEffects(programMethodInfo) &&
-            !NoSideEffectMethodMarker.hasNoSideEffects(programMethodInfo))
-        {
-            // Set the return value, in case the method doesn't have a code
-            // attribute (a native method or an abstract method).
-            hasSideEffects = (programMethodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_NATIVE) != 0;
-
-            programMethodInfo.attributesAccept(programClassFile, this);
-
-            // Mark the method depending on the return value.
-            if (hasSideEffects)
-            {
-                markSideEffects(programMethodInfo);
-
-                newSideEffectCount++;
-            }
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        byte[] code   = codeAttrInfo.code;
-        int    length = codeAttrInfo.u4codeLength;
-
-        // Go over all instructions.
-        int offset = 0;
-        do
-        {
-            // Get the current instruction.
-            Instruction instruction = InstructionFactory.create(code, offset);
-
-            // Check if it is causing any side effects.
-            hasSideEffects = sideEffectInstructionChecker.hasSideEffects(classFile, methodInfo, codeAttrInfo, offset, instruction);
-
-            // Go to the next instruction.
-            offset += instruction.length(offset);
-        }
-        while (offset < length && !hasSideEffects);
-    }
-
-
-    // Small utility methods.
-
-    public static void markSideEffects(MethodInfo methodInfo)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        if (info != null)
-        {
-            info.setSideEffects();
-        }
-    }
-
-
-    public static boolean hasSideEffects(MethodInfo methodInfo)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        return info == null ||
-               info.hasSideEffects();
-    }
-}
diff --git a/src/proguard/optimize/UnusedParameterInvocationUnit.java b/src/proguard/optimize/UnusedParameterInvocationUnit.java
new file mode 100644
index 0000000..cfe8a2e
--- /dev/null
+++ b/src/proguard/optimize/UnusedParameterInvocationUnit.java
@@ -0,0 +1,132 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.ConstantInstruction;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.evaluation.*;
+import proguard.evaluation.value.Value;
+import proguard.optimize.info.ParameterUsageMarker;
+
+/**
+ * This InvocationUnit removes unused parameters from the stack before invoking
+ * a method, and then delegates to another given InvocationUnit.
+ *
+ * @see ParameterUsageMarker
+ * @author Eric Lafortune
+ */
+public class UnusedParameterInvocationUnit
+extends      SimplifiedVisitor
+implements   InvocationUnit,
+             ConstantVisitor,
+             MemberVisitor
+{
+    private static final boolean DEBUG = false;
+
+
+    private final InvocationUnit invocationUnit;
+
+    private Stack stack;
+
+
+    public UnusedParameterInvocationUnit(InvocationUnit invocationUnit)
+    {
+        this.invocationUnit = invocationUnit;
+    }
+
+
+    // Implementations for InvocationUnit.
+
+
+    public void enterMethod(Clazz clazz, Method method, Variables variables)
+    {
+        invocationUnit.enterMethod(clazz, method, variables);
+    }
+
+
+    public void exitMethod(Clazz clazz, Method method, Value returnValue)
+    {
+        invocationUnit.exitMethod(clazz, method, returnValue);
+    }
+
+
+    public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack)
+    {
+        // Fix the stack if this is a method invocation of which some
+        // parameters are marked as unused.
+        this.stack = stack;
+        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+
+        invocationUnit.invokeMember(clazz,
+                                    method,
+                                    codeAttribute,
+                                    offset,
+                                    constantInstruction,
+                                    stack);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        refConstant.referencedMemberAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (DEBUG)
+        {
+            System.out.println("UnusedParameterInvocationUnit: "+programMethod.getName(programClass)+programMethod.getDescriptor(programClass));
+        }
+
+        // Get the total size of the parameters.
+        int parameterSize = ParameterUsageMarker.getParameterSize(programMethod);
+
+        // Remove unused parameters.
+        for (int index = 0; index < parameterSize; index++)
+        {
+            if (!ParameterUsageMarker.isParameterUsed(programMethod, index))
+            {
+                if (DEBUG)
+                {
+                    System.out.println("  removing stack entry #"+(parameterSize - index - 1)+" ("+stack.getTop(parameterSize - index - 1)+")");
+                }
+
+                stack.removeTop(parameterSize - index - 1);
+            }
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {}
+}
diff --git a/src/proguard/optimize/VariableUsageMarker.java b/src/proguard/optimize/VariableUsageMarker.java
deleted file mode 100644
index 3884cc3..0000000
--- a/src/proguard/optimize/VariableUsageMarker.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/* $Id: VariableUsageMarker.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.instruction.*;
-
-/**
- * This InstructionVisitor marks all local variables that are used.
- *
- * @author Eric Lafortune
- */
-public class VariableUsageMarker
-implements   InstructionVisitor
-{
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
-    {
-        markVariableUsed(methodInfo, variableInstruction.variableIndex);
-
-        // Account for Category 2 instructions, which take up two entries.
-        if (variableInstruction.isCategory2())
-        {
-            markVariableUsed(methodInfo, variableInstruction.variableIndex + 1);
-        }
-    }
-
-
-    // Small utility methods.
-
-    public static void markVariableUsed(MethodInfo methodInfo, int variableIndex)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        if (info != null)
-        {
-            info.setVariableUsed(variableIndex);
-        }
-    }
-
-
-    public static boolean isVariableUsed(MethodInfo methodInfo, int variableIndex)
-    {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
-        return info == null ||
-               info.isVariableUsed(variableIndex);
-    }
-}
diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/src/proguard/optimize/WriteOnlyFieldFilter.java
index 7ef32cc..a0f2ec1 100644
--- a/src/proguard/optimize/WriteOnlyFieldFilter.java
+++ b/src/proguard/optimize/WriteOnlyFieldFilter.java
@@ -1,6 +1,6 @@
-/* $Id: WriteOnlyFieldFilter.java,v 1.1.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,45 +21,45 @@
 package proguard.optimize;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.MemberInfoVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.info.ReadWriteFieldMarker;
 
 /**
- * This <code>MemberInfoVisitor</code> delegates its visits to program fields to
- * another given <code>MemberInfoVisitor</code>, but only when the visited
+ * This <code>MemberVisitor</code> delegates its visits to program fields to
+ * other given <code>MemberVisitor</code> instances, but only when the visited
  * field has been marked as write-only.
  *
- * @see WriteOnlyFieldMarker#isWriteOnly(VisitorAccepter)
+ * @see ReadWriteFieldMarker
  * @author Eric Lafortune
  */
 public class WriteOnlyFieldFilter
-  implements MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
-    private MemberInfoVisitor memberInfoVisitor;
+    private final MemberVisitor writeOnlyFieldVisitor;
 
 
     /**
      * Creates a new WriteOnlyFieldFilter.
-     * @param memberInfoVisitor the <code>MemberInfoVisitor</code> to which
-     *                          visits will be delegated.
+     * @param writeOnlyFieldVisitor the <code>MemberVisitor</code> to which
+     *                              visits to write-only fields will be delegated.
      */
-    public WriteOnlyFieldFilter(MemberInfoVisitor memberInfoVisitor)
+    public WriteOnlyFieldFilter(MemberVisitor writeOnlyFieldVisitor)
     {
-        this.memberInfoVisitor = memberInfoVisitor;
+        this.writeOnlyFieldVisitor = writeOnlyFieldVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        if (WriteOnlyFieldMarker.isWriteOnly(programFieldInfo))
+
+        if (ReadWriteFieldMarker.isWritten(programField) &&
+            !ReadWriteFieldMarker.isRead(programField))
         {
-            memberInfoVisitor.visitProgramFieldInfo(programClassFile, programFieldInfo);
+            writeOnlyFieldVisitor.visitProgramField(programClass, programField);
         }
     }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo) {}
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
 }
diff --git a/src/proguard/optimize/WriteOnlyFieldMarker.java b/src/proguard/optimize/WriteOnlyFieldMarker.java
deleted file mode 100644
index b922f15..0000000
--- a/src/proguard/optimize/WriteOnlyFieldMarker.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/* $Id: WriteOnlyFieldMarker.java,v 1.7.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This InstructionVisitor marks all fields that are write-only.
- *
- * @author Eric Lafortune
- */
-public class WriteOnlyFieldMarker
-  implements InstructionVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor
-{
-    // Visitor info flags to indicate whether a FieldInfo object is write-only.
-    private static final Object READ       = new Object();
-    private static final Object WRITE_ONLY = new Object();
-
-
-    // Parameters and values for visitor methods.
-    private boolean reading;
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        byte opcode = cpInstruction.opcode;
-
-        // Check for instructions that involve fields.
-        if      (opcode == InstructionConstants.OP_GETSTATIC     ||
-                 opcode == InstructionConstants.OP_GETFIELD)
-        {
-            // Mark the field as being read from.
-            reading = true;
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        }
-        else if (opcode == InstructionConstants.OP_PUTSTATIC     ||
-                 opcode == InstructionConstants.OP_PUTFIELD)
-        {
-            // Mark the field as being written to.
-            reading = false;
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-    {
-        MemberInfo referencedMemberInfo = fieldrefCpInfo.referencedMemberInfo;
-
-        if (referencedMemberInfo != null)
-        {
-            referencedMemberInfo.accept(fieldrefCpInfo.referencedClassFile, this);
-        }
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        // Hasn't the field been marked as being read yet?
-        if (!isRead(programFieldInfo))
-        {
-            // Mark it now, depending on whether this is a reading operation
-            // or a writing operation.
-            if (reading)
-            {
-                markAsRead(programFieldInfo);
-            }
-            else
-            {
-                markAsWriteOnly(programFieldInfo);
-            }
-        }
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo) {}
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Small utility methods.
-
-    private static void markAsRead(VisitorAccepter visitorAccepter)
-    {
-        visitorAccepter.setVisitorInfo(READ);
-    }
-
-
-    private static boolean isRead(VisitorAccepter visitorAccepter)
-    {
-        return visitorAccepter == null                  ||
-               visitorAccepter.getVisitorInfo() == READ ||
-               KeepMarker.isKept(visitorAccepter);
-    }
-
-
-    public static void markAsWriteOnly(VisitorAccepter visitorAccepter)
-    {
-        visitorAccepter.setVisitorInfo(WRITE_ONLY);
-    }
-
-
-    public static boolean isWriteOnly(VisitorAccepter visitorAccepter)
-    {
-        return visitorAccepter != null                        &&
-               visitorAccepter.getVisitorInfo() == WRITE_ONLY &&
-               !KeepMarker.isKept(visitorAccepter);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
index 44c7b0d..314279d 100644
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -1,6 +1,6 @@
-/* $Id: EvaluationSimplifier.java,v 1.4.2.17 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,24 +21,25 @@
 package proguard.optimize.evaluation;
 
 import proguard.classfile.*;
+import proguard.classfile.visitor.ClassPrinter;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.editor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
 import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.optimize.*;
-import proguard.optimize.evaluation.value.*;
+import proguard.evaluation.value.*;
+import proguard.optimize.info.SideEffectInstructionChecker;
 
 /**
- * This MemberInfoVisitor performs partial evaluation on the program methods
- * that it visits.
+ * This AttributeVisitor simplifies the code attributes that it visits, based
+ * on partial evaluation.
  *
  * @author Eric Lafortune
  */
 public class EvaluationSimplifier
-implements   MemberInfoVisitor,
-             AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
              InstructionVisitor
 {
     //*
@@ -51,22 +52,17 @@ implements   MemberInfoVisitor,
     private static boolean DEBUG          = true;
     //*/
 
-    private static final int INITIAL_CODE_LENGTH = 1024;
-    private static final int INITIAL_VALUE_COUNT = 32;
-
-    private InstructionVisitor extraPushInstructionVisitor;
-    private InstructionVisitor extraBranchInstructionVisitor;
-    private InstructionVisitor extraDeletedInstructionVisitor;
-
-    private PartialEvaluator             partialEvaluator             = new PartialEvaluator();
-    private SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
-    private ClassFileCleaner             classFileCleaner             = new ClassFileCleaner();
-    private CodeAttrInfoEditor           codeAttrInfoEditor           = new CodeAttrInfoEditor(INITIAL_CODE_LENGTH);
+    private final InstructionVisitor extraPushInstructionVisitor;
+    private final InstructionVisitor extraBranchInstructionVisitor;
+    private final InstructionVisitor extraDeletedInstructionVisitor;
+    private final InstructionVisitor extraAddedInstructionVisitor;
 
+    private final PartialEvaluator             partialEvaluator;
+    private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
+    private final CodeAttributeEditor          codeAttributeEditor          = new CodeAttributeEditor();
 
-    private Variables parameters   = new TracedVariables(INITIAL_VALUE_COUNT);
-    private boolean[] isNecessary  = new boolean[INITIAL_CODE_LENGTH];
-    private boolean[] isSimplified = new boolean[INITIAL_CODE_LENGTH];
+    private boolean[] isNecessary  = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+    private boolean[] isSimplified = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
 
 
     /**
@@ -74,180 +70,140 @@ implements   MemberInfoVisitor,
      */
     public EvaluationSimplifier()
     {
-        this(null, null, null);
+        this(new PartialEvaluator(), null, null, null, 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.
      */
-    public EvaluationSimplifier(InstructionVisitor extraPushInstructionVisitor,
+    public EvaluationSimplifier(PartialEvaluator   partialEvaluator,
+                                InstructionVisitor extraPushInstructionVisitor,
                                 InstructionVisitor extraBranchInstructionVisitor,
-                                InstructionVisitor extraDeletedInstructionVisitor)
+                                InstructionVisitor extraDeletedInstructionVisitor,
+                                InstructionVisitor extraAddedInstructionVisitor)
     {
-        this.extraPushInstructionVisitor    = extraPushInstructionVisitor;
-        this.extraBranchInstructionVisitor  = extraBranchInstructionVisitor;
-        this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor;
+        this.partialEvaluator                      = partialEvaluator;
+        this.extraPushInstructionVisitor           = extraPushInstructionVisitor;
+        this.extraBranchInstructionVisitor         = extraBranchInstructionVisitor;
+        this.extraDeletedInstructionVisitor        = extraDeletedInstructionVisitor;
+        this.extraAddedInstructionVisitor          = extraAddedInstructionVisitor;
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
 //        DEBUG = DEBUG_ANALYSIS = DEBUG_RESULTS =
-//            programClassFile.getName().equals("abc/Def") &&
-//            programMethodInfo.getName(programClassFile).equals("abc");
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
 
+        // TODO: Remove this when the partial evaluator has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
         try
         {
             // Process the code.
-            programMethodInfo.attributesAccept(programClassFile, this);
+            visitCodeAttribute0(clazz, method, codeAttribute);
         }
         catch (RuntimeException ex)
         {
-            // TODO: Remove this when the partial evaluator has stabilized.
             System.err.println("Unexpected error while optimizing after partial evaluation:");
-            System.err.println("  ClassFile   = ["+programClassFile.getName()+"]");
-            System.err.println("  Method      = ["+programMethodInfo.getName(programClassFile)+programMethodInfo.getDescriptor(programClassFile)+"]");
+            System.err.println("  Class       = ["+clazz.getName()+"]");
+            System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+            System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
             System.err.println("Not optimizing this method");
 
             if (DEBUG)
             {
+                method.accept(clazz, new ClassPrinter());
+
                 throw ex;
             }
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
         if (DEBUG_RESULTS)
         {
             System.out.println();
-            System.out.println("Class "+ClassUtil.externalClassName(classFile.getName()));
-            System.out.println("Method "+ClassUtil.externalFullMethodDescription(classFile.getName(),
+            System.out.println("Class "+ClassUtil.externalClassName(clazz.getName()));
+            System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
                                                                                  0,
-                                                                                 methodInfo.getName(classFile),
-                                                                                 methodInfo.getDescriptor(classFile)));
-            System.out.println("  Params:"+parameters);
+                                                                                 method.getName(clazz),
+                                                                                 method.getDescriptor(clazz)));
         }
 
-        // Initialize the method parameters.
-        initializeParameters(classFile, methodInfo, codeAttrInfo);
-
         // Initialize the necessary array.
-        initializeNecessary(codeAttrInfo);
+        initializeNecessary(codeAttribute);
 
         // Evaluate the method.
-        Value returnValue = partialEvaluator.evaluate(classFile,
-                                                      methodInfo,
-                                                      codeAttrInfo,
-                                                      parameters);
-
-        // Clean up the visitor information in the exceptions right away.
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, classFileCleaner);
+        partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
 
-        int codeLength = codeAttrInfo.u4codeLength;
+        int codeLength = codeAttribute.u4codeLength;
 
         // Reset the code changes.
-        codeAttrInfoEditor.reset(codeLength);
+        codeAttributeEditor.reset(codeLength);
 
         // Replace any instructions that can be simplified.
         if (DEBUG_ANALYSIS) System.out.println("Instruction simplification:");
 
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+        codeAttribute.instructionsAccept(clazz, method, this);
 
         // Mark all essential instructions that have been encountered as used.
         if (DEBUG_ANALYSIS) System.out.println("Usage initialization: ");
 
-
         // The invocation of the "super" or "this" <init> method inside a
-        // constructor is always necessary, even if it is assumed to have no
-        // side effects.
-        boolean markSuperOrThis =
-            methodInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+        // constructor is always necessary.
+        int superInitializationOffset = partialEvaluator.superInitializationOffset();
+        if (superInitializationOffset != PartialEvaluator.NONE)
+        {
+            if (DEBUG_ANALYSIS) System.out.print(superInitializationOffset+"(super.<init>),");
 
-        int aload0Offset = 0;
+            isNecessary[superInitializationOffset] = true;
+        }
 
+        // Also mark infinite loops and instructions that cause side effects.
         int offset = 0;
         do
         {
             if (partialEvaluator.isTraced(offset))
             {
-                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
-                // Remember the most recent aload0 instruction index.
-                if (instruction.opcode == InstructionConstants.OP_ALOAD_0)
-                {
-                    aload0Offset = offset;
-                }
-
-                // Mark that the instruction is necessary if it is the first
-                // invocation of the "super" or "this" <init> method
-                // inside a constructor.
-                else if (markSuperOrThis &&
-                         instruction.opcode == InstructionConstants.OP_INVOKESPECIAL &&
-                         partialEvaluator.stackProducerOffsets(offset).contains(aload0Offset))
-                {
-                    markSuperOrThis = false;
-
-                    if (DEBUG_ANALYSIS) System.out.print(offset+"(<init>),");
-                    isNecessary[offset] = true;
-                }
-
                 // Mark that the instruction is necessary if it is an infinite loop.
-                else if (instruction.opcode == InstructionConstants.OP_GOTO &&
-                         partialEvaluator.branchTargets(offset).instructionOffsetValue().instructionOffset(0) == offset)
+                if (instruction.opcode == InstructionConstants.OP_GOTO &&
+                    partialEvaluator.branchTargets(offset).instructionOffsetValue().instructionOffset(0) == offset)
                 {
                     if (DEBUG_ANALYSIS) System.out.print(offset+"(infinite loop),");
                     isNecessary[offset] = true;
                 }
 
                 // Mark that the instruction is necessary if it has side effects.
-                else if (sideEffectInstructionChecker.hasSideEffects(classFile,
-                                                                     methodInfo,
-                                                                     codeAttrInfo,
+                else if (sideEffectInstructionChecker.hasSideEffects(clazz,
+                                                                     method,
+                                                                     codeAttribute,
                                                                      offset,
                                                                      instruction))
                 {
-                    if (DEBUG_ANALYSIS) System.out.print(offset +",");
+                    if (DEBUG_ANALYSIS) System.out.print(offset+",");
                     isNecessary[offset] = true;
                 }
             }
@@ -288,8 +244,7 @@ implements   MemberInfoVisitor,
             if (isNecessary[offset] &&
                 !isSimplified[offset])
             {
-                nextOffset = markProducers(offset,
-                                           nextOffset);
+                nextOffset = markProducers(offset, nextOffset);
             }
 
             // Update the lowest index of all marked instructions higher up.
@@ -341,7 +296,7 @@ implements   MemberInfoVisitor,
                 !isNecessary[offset] &&
                 !isSimplified[offset])
             {
-                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
                 // Make sure any non-dup/swap instructions are always consistent
@@ -350,8 +305,8 @@ implements   MemberInfoVisitor,
                 {
                     // Make sure any popping instructions are always
                     // consistent after this offset.
-                    fixPopInstruction(classFile,
-                                      codeAttrInfo,
+                    fixPopInstruction(clazz,
+                                      codeAttribute,
                                       offset,
                                       instruction);
                 }
@@ -383,15 +338,15 @@ implements   MemberInfoVisitor,
             {
                 if (partialEvaluator.isTraced(offset))
                 {
-                    Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                    Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                         offset);
 
                     // Make sure any dup/swap instructions are always consistent
                     // at this offset.
                     if (isDupOrSwap(instruction))
                     {
-                        updated |= fixDupInstruction(classFile,
-                                                     codeAttrInfo,
+                        updated |= fixDupInstruction(clazz,
+                                                     codeAttribute,
                                                      offset,
                                                      instruction);
                     }
@@ -415,7 +370,7 @@ implements   MemberInfoVisitor,
                 isNecessary[offset] &&
                 !isSimplified[offset])
             {
-                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
 
                 // Make sure any non-dup/swap instructions are always consistent
@@ -424,8 +379,8 @@ implements   MemberInfoVisitor,
                 {
                     // Make sure any pushing instructions are always
                     // consistent after this offset.
-                    fixPushInstruction(classFile,
-                                       codeAttrInfo,
+                    fixPushInstruction(clazz,
+                                       codeAttribute,
                                        offset,
                                        instruction);
                 }
@@ -437,6 +392,42 @@ implements   MemberInfoVisitor,
         if (DEBUG_ANALYSIS) System.out.println();
 
 
+        // Mark unmarked pop instructions after dup instructions,
+        // if required to keep the stack consistent.
+        // This is mainly required to fix "synchronized(C.class)" constructs
+        // as compiled by jikes and by the Eclipse compiler:
+        //    ...
+        //    dup
+        //    ifnonnull ...
+        //    pop
+        //    ...
+        if (DEBUG_ANALYSIS) System.out.println("Pop marking:");
+
+        offset = codeLength - 1;
+        do
+        {
+            if (//partialEvaluator.isTraced(offset) &&
+                isNecessary[offset]   &&
+                !isSimplified[offset] &&
+                !codeAttributeEditor.isModified(offset))
+            {
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
+                                                                    offset);
+                if (isDupOrSwap(instruction))
+                {
+                    markConsumingPopInstructions(clazz,
+                                                 codeAttribute,
+                                                 offset,
+                                                 instruction);
+                }
+            }
+
+            offset--;
+        }
+        while (offset >= 0);
+        if (DEBUG_ANALYSIS) System.out.println();
+
+
         // Mark branches straddling just inserted push/pop instructions.
         if (DEBUG_ANALYSIS) System.out.println("Final straddling branch marking:");
 
@@ -504,14 +495,14 @@ implements   MemberInfoVisitor,
             // Is it an initialization that hasn't been marked yet, and whose
             // corresponding variable is used for storage?
             int variableIndex = partialEvaluator.initializedVariable(offset);
-            if (variableIndex != PartialEvaluator.NONE &&
+            if (variableIndex >= 0 &&
                 !isNecessary[offset] &&
-                isVariableReferenced(codeAttrInfo, variableIndex))
+                isVariableReferenced(codeAttribute, offset+1, variableIndex))
             {
                 if (DEBUG_ANALYSIS) System.out.println(offset +",");
 
                 // Figure out what kind of initialization value has to be stored.
-                int pushComputationalType = partialEvaluator.variableValueAfter(offset, variableIndex).computationalType();
+                int pushComputationalType = partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).computationalType();
                 increaseStackSize(offset, pushComputationalType, false);
             }
 
@@ -520,14 +511,13 @@ implements   MemberInfoVisitor,
         while (offset < codeLength);
         if (DEBUG_ANALYSIS) System.out.println();
 
-
         if (DEBUG_RESULTS)
         {
             System.out.println("Simplification results:");
             offset = 0;
             do
             {
-                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
                 System.out.println((isNecessary[offset] ? " + " : " - ")+instruction.toString(offset));
 
@@ -545,10 +535,10 @@ implements   MemberInfoVisitor,
                         System.out.println("     has overall been using information from instructions setting stack: "+stackProducerOffsets);
                     }
 
-                    InstructionOffsetValue unusedProducerOffsets = partialEvaluator.unusedProducerOffsets(offset);
-                    if (unusedProducerOffsets.instructionOffsetCount() > 0)
+                    int initializationOffset = partialEvaluator.initializationOffset(offset);
+                    if (initializationOffset != PartialEvaluator.NONE)
                     {
-                        System.out.println("     no longer needs information from instructions setting stack: "+unusedProducerOffsets);
+                        System.out.println("     is to be initialized at ["+initializationOffset+"]");
                     }
 
                     InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
@@ -557,19 +547,19 @@ implements   MemberInfoVisitor,
                         System.out.println("     has overall been branching to "+branchTargets);
                     }
 
-                    Instruction preInsertion = codeAttrInfoEditor.preInsertions[offset];
+                    Instruction preInsertion = codeAttributeEditor.preInsertions[offset];
                     if (preInsertion != null)
                     {
                         System.out.println("     is preceded by: "+preInsertion);
                     }
 
-                    Instruction replacement = codeAttrInfoEditor.replacements[offset];
+                    Instruction replacement = codeAttributeEditor.replacements[offset];
                     if (replacement != null)
                     {
                         System.out.println("     is replaced by: "+replacement);
                     }
 
-                    Instruction postInsertion = codeAttrInfoEditor.postInsertions[offset];
+                    Instruction postInsertion = codeAttributeEditor.postInsertions[offset];
                     if (postInsertion != null)
                     {
                         System.out.println("     is followed by: "+postInsertion);
@@ -588,20 +578,20 @@ implements   MemberInfoVisitor,
         offset = 0;
         do
         {
-            Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+            Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                 offset);
             if (!isNecessary[offset])
             {
-                codeAttrInfoEditor.deleteInstruction(offset);
+                codeAttributeEditor.deleteInstruction(offset);
 
-                codeAttrInfoEditor.insertBeforeInstruction(offset, null);
-                codeAttrInfoEditor.replaceInstruction(offset, null);
-                codeAttrInfoEditor.insertAfterInstruction(offset, null);
+                codeAttributeEditor.insertBeforeInstruction(offset, null);
+                codeAttributeEditor.replaceInstruction(offset, null);
+                codeAttributeEditor.insertAfterInstruction(offset, null);
 
                 // Visit the instruction, if required.
                 if (extraDeletedInstructionVisitor != null)
                 {
-                    instruction.accept(classFile, methodInfo, codeAttrInfo, offset, extraDeletedInstructionVisitor);
+                    instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor);
                 }
             }
 
@@ -610,7 +600,7 @@ implements   MemberInfoVisitor,
         while (offset < codeLength);
 
         // Apply all accumulated changes to the code.
-        codeAttrInfoEditor.visitCodeAttrInfo(classFile, methodInfo, codeAttrInfo);
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
     }
 
 
@@ -633,6 +623,10 @@ implements   MemberInfoVisitor,
         // Mark all instructions whose stack values are used.
         nextOffset = markProducers(partialEvaluator.stackProducerOffsets(consumerOffset), nextOffset);
 
+        // Mark the initializer of the variables and stack entries used by the
+        // instruction.
+        nextOffset = markProducer(partialEvaluator.initializationOffset(consumerOffset), nextOffset);
+
         if (DEBUG_ANALYSIS) System.out.print(",");
 
         return nextOffset;
@@ -658,22 +652,39 @@ implements   MemberInfoVisitor,
             {
                 // Has the other instruction been marked yet?
                 int offset = producerOffsets.instructionOffset(offsetIndex);
-                if (offset > PartialEvaluator.AT_METHOD_ENTRY &&
-                    !isNecessary[offset])
-                {
-                    if (DEBUG_ANALYSIS) System.out.print("["+offset +"]");
+                nextOffset = markProducer(offset, nextOffset);
+            }
+        }
 
-                    // Mark it.
-                    isNecessary[offset] = true;
+        return nextOffset;
+    }
 
-                    // Restart at this instruction if it has a higher offset.
-                    if (nextOffset < offset)
-                    {
-                        if (DEBUG_ANALYSIS) System.out.print("!");
 
-                        nextOffset = offset;
-                    }
-                }
+    /**
+     * Marks the instruction at the given offset.
+     * @param producerOffset the offsets of the producer to be marked.
+     * @param nextOffset     the offset of the instruction to be investigated
+     *                       next.
+     * @return the updated offset of the instruction to be investigated next.
+     *         It is always greater than or equal the original offset, because
+     *         instructions are investigated starting at the highest index.
+     */
+    private int markProducer(int producerOffset, int nextOffset)
+    {
+        if (producerOffset > PartialEvaluator.AT_METHOD_ENTRY &&
+            !isNecessary[producerOffset])
+        {
+            if (DEBUG_ANALYSIS) System.out.print("["+producerOffset +"]");
+
+            // Mark it.
+            isNecessary[producerOffset] = true;
+
+            // Restart at this instruction if it has a higher offset.
+            if (nextOffset < producerOffset)
+            {
+                if (DEBUG_ANALYSIS) System.out.print("!");
+
+                nextOffset = producerOffset;
             }
         }
 
@@ -885,8 +896,8 @@ implements   MemberInfoVisitor,
                 new BranchInstruction(InstructionConstants.OP_GOTO_W,
                                       branchTarget - branchOrigin).shrink();
 
-            codeAttrInfoEditor.replaceInstruction(branchOrigin,
-                                                  replacementInstruction);
+            codeAttributeEditor.replaceInstruction(branchOrigin,
+                                                   replacementInstruction);
 
             // Restart at the branch origin if it has a higher offset.
             if (nextOffset < branchOrigin)
@@ -920,16 +931,16 @@ implements   MemberInfoVisitor,
     /**
      * Marks the specified instruction if it is a required dup/swap instruction,
      * replacing it by an appropriate variant if necessary.
-     * @param classFile    the class that is being checked.
-     * @param codeAttrInfo the code that is being checked.
-     * @param dupOffset    the offset of the dup/swap instruction.
-     * @param instruction  the dup/swap instruction.
+     * @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.
      * @return whether the instruction is updated.
      */
-    private boolean fixDupInstruction(ClassFile    classFile,
-                                      CodeAttrInfo codeAttrInfo,
-                                      int          dupOffset,
-                                      Instruction  instruction)
+    private boolean fixDupInstruction(Clazz         clazz,
+                                      CodeAttribute codeAttribute,
+                                      int           dupOffset,
+                                      Instruction   instruction)
     {
         byte    oldOpcode = instruction.opcode;
         byte    newOpcode = 0;
@@ -1155,14 +1166,14 @@ implements   MemberInfoVisitor,
             if      (newOpcode == 0)
             {
                 // Delete the instruction.
-                codeAttrInfoEditor.deleteInstruction(dupOffset);
+                codeAttributeEditor.deleteInstruction(dupOffset);
 
                 if (DEBUG_ANALYSIS) System.out.println("  Marking but deleting instruction "+instruction.toString(dupOffset));
             }
             else if (newOpcode == oldOpcode)
             {
                 // Leave the instruction unchanged.
-                codeAttrInfoEditor.undeleteInstruction(dupOffset);
+                codeAttributeEditor.undeleteInstruction(dupOffset);
 
                 if (DEBUG_ANALYSIS) System.out.println("  Marking unchanged instruction "+instruction.toString(dupOffset));
             }
@@ -1170,8 +1181,8 @@ implements   MemberInfoVisitor,
             {
                 // Replace the instruction.
                 Instruction replacementInstruction = new SimpleInstruction(newOpcode);
-                codeAttrInfoEditor.replaceInstruction(dupOffset,
-                                                      replacementInstruction);
+                codeAttributeEditor.replaceInstruction(dupOffset,
+                                                       replacementInstruction);
 
                 if (DEBUG_ANALYSIS) System.out.println("  Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
             }
@@ -1184,17 +1195,17 @@ implements   MemberInfoVisitor,
     /**
      * Pops the stack after the given necessary instruction, if it pushes an
      * entry that is not used at all.
-     * @param classFile      the class that is being checked.
-     * @param codeAttrInfo   the code that is being checked.
-     * @param producerOffset the offset of the instruction.
-     * @param instruction    the instruction.
+     * @param clazz          the class that is being checked.
+     * @param codeAttribute  the code that is being checked.
+     * @param producerOffset the offset of the producer instruction.
+     * @param instruction    the producer instruction.
      */
-    private void fixPushInstruction(ClassFile    classFile,
-                                    CodeAttrInfo codeAttrInfo,
-                                    int          producerOffset,
-                                    Instruction  instruction)
+    private void fixPushInstruction(Clazz         clazz,
+                                    CodeAttribute codeAttribute,
+                                    int           producerOffset,
+                                    Instruction   instruction)
     {
-        int pushCount = instruction.stackPushCount(classFile);
+        int pushCount = instruction.stackPushCount(clazz);
         if (pushCount > 0)
         {
             boolean stackEntryPresent0 = isStackEntryNecessaryAfter(producerOffset, 0, false);
@@ -1218,27 +1229,25 @@ implements   MemberInfoVisitor,
     /**
      * Pops the stack before the given unnecessary instruction, if the stack
      * contains an entry that it would have popped.
-     * @param classFile      the class that is being checked.
-     * @param codeAttrInfo   the code that is being checked.
+     * @param clazz          the class that is being checked.
+     * @param codeAttribute  the code that is being checked.
      * @param consumerOffset the offset of the consumer instruction.
-     * @param instruction    the instruction.
+     * @param instruction    the consumer instruction.
      */
-    private void fixPopInstruction(ClassFile    classFile,
-                                   CodeAttrInfo codeAttrInfo,
-                                   int          consumerOffset,
-                                   Instruction  instruction)
+    private void fixPopInstruction(Clazz         clazz,
+                                   CodeAttribute codeAttribute,
+                                   int           consumerOffset,
+                                   Instruction   instruction)
     {
-        int popCount = instruction.stackPopCount(classFile);
+        int popCount = instruction.stackPopCount(clazz);
         if (popCount > 0)
         {
             if (partialEvaluator.stackProducerOffsets(consumerOffset).contains(PartialEvaluator.AT_CATCH_ENTRY) ||
-                isStackEntryNecessaryBefore(consumerOffset, 0, false) &&
-                !isStackEntryNecessaryBefore(consumerOffset, 0, true))
+                (isStackEntryNecessaryBefore(consumerOffset, 0, false) &&
+                 !isStackEntryNecessaryBefore(consumerOffset, 0, true)))
             {
                 // Is the consumer a simple pop or pop2 instruction?
-                byte popOpcode = instruction.opcode;
-                if (popOpcode == InstructionConstants.OP_POP ||
-                    popOpcode == InstructionConstants.OP_POP2)
+                if (isPop(instruction))
                 {
                     if (DEBUG_ANALYSIS) System.out.println("  Popping value again at "+instruction.toString(consumerOffset));
 
@@ -1259,6 +1268,52 @@ implements   MemberInfoVisitor,
 
 
     /**
+     * Marks pop and pop2 instructions that pop stack entries produced by the
+     * given instruction.
+     * @param clazz               the class that is being checked.
+     * @param codeAttribute       the code that is being checked.
+     * @param producerOffset      the offset of the producer instruction.
+     * @param producerInstruction the producer instruction.
+     */
+    private void markConsumingPopInstructions(Clazz         clazz,
+                                              CodeAttribute codeAttribute,
+                                              int           producerOffset,
+                                              Instruction   producerInstruction)
+    {
+        // Loop over all pushed stack entries.
+        int pushCount = producerInstruction.stackPushCount(clazz);
+        for (int stackIndex = 0; stackIndex < pushCount; stackIndex++)
+        {
+            // Loop over all consumers of this entry.
+            InstructionOffsetValue consumerOffsets =
+                partialEvaluator.getStackAfter(producerOffset).getTopConsumerValue(stackIndex).instructionOffsetValue();
+
+            int consumerCount = consumerOffsets.instructionOffsetCount();
+            for (int consumerIndex = 0; consumerIndex < consumerCount; consumerIndex++)
+            {
+                int consumerOffset = consumerOffsets.instructionOffset(consumerIndex);
+
+                // Is the consumer not necessary yet?
+                if (!isNecessary[consumerOffset])
+                {
+                    Instruction consumerInstruction =
+                        InstructionFactory.create(codeAttribute.code, consumerOffset);
+
+                    // Is the consumer a simple pop or pop2 instruction?
+                    if (isPop(consumerInstruction))
+                    {
+                        // Mark it.
+                        isNecessary[consumerOffset] = true;
+
+                        if (DEBUG_ANALYSIS) System.out.println("  Marking "+consumerInstruction.toString(consumerOffset)+" due to "+producerInstruction.toString(producerOffset));
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
      * Puts the required push instruction before the given index. The
      * instruction is marked as necessary.
      * @param offset            the offset of the instruction.
@@ -1275,25 +1330,50 @@ implements   MemberInfoVisitor,
         isNecessary[offset] = true;
 
         // Create a simple push instrucion.
-        byte replacementOpcode =
-            computationalType == Value.TYPE_INTEGER   ? InstructionConstants.OP_ICONST_0    :
-            computationalType == Value.TYPE_LONG      ? InstructionConstants.OP_LCONST_0    :
-            computationalType == Value.TYPE_FLOAT     ? InstructionConstants.OP_FCONST_0    :
-            computationalType == Value.TYPE_DOUBLE    ? InstructionConstants.OP_DCONST_0    :
-            computationalType == Value.TYPE_REFERENCE ? InstructionConstants.OP_ACONST_NULL :
-                                                        InstructionConstants.OP_NOP;
 
-        Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
+        Instruction replacementInstruction =
+            new SimpleInstruction(pushOpcode(computationalType));
 
         // Insert the pop or push instruction.
-        codeAttrInfoEditor.insertBeforeInstruction(offset,
-                                                   replacementInstruction);
+        codeAttributeEditor.insertBeforeInstruction(offset,
+                                                    replacementInstruction);
+
+        if (extraAddedInstructionVisitor != null)
+        {
+            replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+        }
 
         // Delete the original instruction if necessary.
         if (delete)
         {
-            codeAttrInfoEditor.deleteInstruction(offset);
+            codeAttributeEditor.deleteInstruction(offset);
+
+            if (extraDeletedInstructionVisitor != null)
+            {
+                extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null);
+            }
+        }
+    }
+
+
+    /**
+     * 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+"]");
     }
 
 
@@ -1331,8 +1411,8 @@ implements   MemberInfoVisitor,
             Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
 
             // Insert the pop instruction.
-            codeAttrInfoEditor.replaceInstruction(offset,
-                                                  replacementInstruction);
+            codeAttributeEditor.replaceInstruction(offset,
+                                                   replacementInstruction);
 
             remainingPopCount -= count;
 
@@ -1354,10 +1434,15 @@ implements   MemberInfoVisitor,
             Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
 
             // Insert the pop instruction.
-            codeAttrInfoEditor.insertBeforeInstruction(offset,
-                                                       replacementInstruction);
+            codeAttributeEditor.insertBeforeInstruction(offset,
+                                                        replacementInstruction);
 
             remainingPopCount -= count;
+
+            if (extraAddedInstructionVisitor != null)
+            {
+                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+            }
         }
 
         if (after && remainingPopCount > 0)
@@ -1373,22 +1458,27 @@ implements   MemberInfoVisitor,
             Instruction replacementInstruction = new SimpleInstruction(replacementOpcode);
 
             // Insert the pop instruction.
-            codeAttrInfoEditor.insertAfterInstruction(offset,
-                                                      replacementInstruction);
+            codeAttributeEditor.insertAfterInstruction(offset,
+                                                       replacementInstruction);
 
             remainingPopCount -= count;
+
+            if (extraAddedInstructionVisitor != null)
+            {
+                replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
+            }
         }
 
         if (remainingPopCount > 0)
         {
-            throw new IllegalArgumentException("Unsupported stack size reduction ["+popCount+"] at ["+offset+"]");
+            throw new IllegalArgumentException("Unsupported stack size reduction ["+popCount+"]");
         }
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         if (partialEvaluator.isTraced(offset))
         {
@@ -1416,7 +1506,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_I2B:
                 case InstructionConstants.OP_I2C:
                 case InstructionConstants.OP_I2S:
-                    replaceIntegerPushInstruction(offset, simpleInstruction);
+                    replaceIntegerPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_LALOAD:
@@ -1435,7 +1525,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_I2L:
                 case InstructionConstants.OP_F2L:
                 case InstructionConstants.OP_D2L:
-                    replaceLongPushInstruction(offset, simpleInstruction);
+                    replaceLongPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_FALOAD:
@@ -1448,7 +1538,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_I2F:
                 case InstructionConstants.OP_L2F:
                 case InstructionConstants.OP_D2F:
-                    replaceFloatPushInstruction(offset, simpleInstruction);
+                    replaceFloatPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_DALOAD:
@@ -1461,18 +1551,18 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_I2D:
                 case InstructionConstants.OP_L2D:
                 case InstructionConstants.OP_F2D:
-                    replaceDoublePushInstruction(offset, simpleInstruction);
+                    replaceDoublePushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_AALOAD:
-                    replaceReferencePushInstruction(offset, simpleInstruction);
+                    replaceReferencePushInstruction(offset);
                     break;
             }
         }
     }
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         if (partialEvaluator.isTraced(offset))
         {
@@ -1483,7 +1573,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_ILOAD_1:
                 case InstructionConstants.OP_ILOAD_2:
                 case InstructionConstants.OP_ILOAD_3:
-                    replaceIntegerPushInstruction(offset, variableInstruction);
+                    replaceIntegerPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_LLOAD:
@@ -1491,7 +1581,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_LLOAD_1:
                 case InstructionConstants.OP_LLOAD_2:
                 case InstructionConstants.OP_LLOAD_3:
-                    replaceLongPushInstruction(offset, variableInstruction);
+                    replaceLongPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_FLOAD:
@@ -1499,7 +1589,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_FLOAD_1:
                 case InstructionConstants.OP_FLOAD_2:
                 case InstructionConstants.OP_FLOAD_3:
-                    replaceFloatPushInstruction(offset, variableInstruction);
+                    replaceFloatPushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_DLOAD:
@@ -1507,7 +1597,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_DLOAD_1:
                 case InstructionConstants.OP_DLOAD_2:
                 case InstructionConstants.OP_DLOAD_3:
-                    replaceDoublePushInstruction(offset, variableInstruction);
+                    replaceDoublePushInstruction(offset);
                     break;
 
                 case InstructionConstants.OP_ALOAD:
@@ -1515,7 +1605,7 @@ implements   MemberInfoVisitor,
                 case InstructionConstants.OP_ALOAD_1:
                 case InstructionConstants.OP_ALOAD_2:
                 case InstructionConstants.OP_ALOAD_3:
-                    replaceReferencePushInstruction(offset, variableInstruction);
+                    replaceReferencePushInstruction(offset);
                     break;
 
             }
@@ -1523,54 +1613,120 @@ implements   MemberInfoVisitor,
     }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
-        // Constant pool instructions are not simplified at this point.
+        if (partialEvaluator.isTraced(offset))
+        {
+            switch (constantInstruction.opcode)
+            {
+                case InstructionConstants.OP_GETSTATIC:
+                case InstructionConstants.OP_GETFIELD:
+                    replaceAnyPushInstruction(offset);
+                    break;
+
+                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(offset);
+                    }
+                    break;
+
+                case InstructionConstants.OP_CHECKCAST:
+                    replaceReferencePushInstruction(offset);
+                    break;
+            }
+        }
     }
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
-        switch (branchInstruction.opcode)
+        if (partialEvaluator.isTraced(offset))
         {
-            case InstructionConstants.OP_GOTO:
-            case InstructionConstants.OP_GOTO_W:
-                // Don't replace unconditional branches.
-                break;
+            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(offset, branchInstruction);
-                break;
+                case InstructionConstants.OP_JSR:
+                case InstructionConstants.OP_JSR_W:
+                    replaceJsrInstruction(offset, branchInstruction);
+                    break;
 
-            default:
-                replaceBranchInstruction(offset, branchInstruction);
-                break;
+                default:
+                    replaceBranchInstruction(offset, branchInstruction);
+                    break;
+            }
         }
     }
 
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
     {
-        replaceBranchInstruction(offset, tableSwitchInstruction);
+        if (partialEvaluator.isTraced(offset))
+        {
+            // First try to simplify it to a simple branch.
+            replaceBranchInstruction(offset, switchInstruction);
+
+            // Otherwise make sure all branch targets are valid.
+            if (!isSimplified[offset])
+            {
+                replaceSwitchInstruction(offset, switchInstruction);
+            }
+        }
     }
 
 
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    // Small utility methods.
+
+    /**
+     * Replaces the push instruction at the given offset by a simpler push
+     * instruction, if possible.
+     */
+    private void replaceAnyPushInstruction(int offset)
     {
-        replaceBranchInstruction(offset, lookUpSwitchInstruction);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+        if (pushedValue.isSpecific())
+        {
+            switch (pushedValue.computationalType())
+            {
+                case Value.TYPE_INTEGER:
+                    replaceIntegerPushInstruction(offset);
+                    break;
+                case Value.TYPE_LONG:
+                    replaceLongPushInstruction(offset);
+                    break;
+                case Value.TYPE_FLOAT:
+                    replaceFloatPushInstruction(offset);
+                    break;
+                case Value.TYPE_DOUBLE:
+                    replaceDoublePushInstruction(offset);
+                    break;
+                case Value.TYPE_REFERENCE:
+                    replaceReferencePushInstruction(offset);
+                    break;
+            }
+        }
     }
 
 
-    // Small utility methods.
-
     /**
-     * Replaces the given integer instruction by a simpler push instruction,
-     * if possible.
+     * Replaces the integer pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
      */
-    private void replaceIntegerPushInstruction(int offset, Instruction instruction)
+    private void replaceIntegerPushInstruction(int offset)
     {
-        Value pushedValue = partialEvaluator.stackTopValueAfter(offset, 0);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
         if (pushedValue.isSpecific())
         {
             int value = pushedValue.integerValue().value();
@@ -1585,12 +1741,12 @@ implements   MemberInfoVisitor,
 
 
     /**
-     * Replaces the given long instruction by a simpler push instruction,
-     * if possible.
+     * Replaces the long pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
      */
-    private void replaceLongPushInstruction(int offset, Instruction instruction)
+    private void replaceLongPushInstruction(int offset)
     {
-        Value pushedValue = partialEvaluator.stackTopValueAfter(offset, 0);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
         if (pushedValue.isSpecific())
         {
             long value = pushedValue.longValue().value();
@@ -1598,20 +1754,20 @@ implements   MemberInfoVisitor,
                 value == 1L)
             {
                 replacePushInstruction(offset,
-                                       (byte)(InstructionConstants.OP_LCONST_0 + value),
-                                       0);
+                                       InstructionConstants.OP_LCONST_0,
+                                       (int)value);
             }
         }
     }
 
 
     /**
-     * Replaces the given float instruction by a simpler push instruction,
-     * if possible.
+     * Replaces the float pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
      */
-    private void replaceFloatPushInstruction(int offset, Instruction instruction)
+    private void replaceFloatPushInstruction(int offset)
     {
-        Value pushedValue = partialEvaluator.stackTopValueAfter(offset, 0);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
         if (pushedValue.isSpecific())
         {
             float value = pushedValue.floatValue().value();
@@ -1620,20 +1776,20 @@ implements   MemberInfoVisitor,
                 value == 2f)
             {
                 replacePushInstruction(offset,
-                                       (byte)(InstructionConstants.OP_FCONST_0 + value),
-                                       0);
+                                       InstructionConstants.OP_FCONST_0,
+                                       (int)value);
             }
         }
     }
 
 
     /**
-     * Replaces the given double instruction by a simpler push instruction,
-     * if possible.
+     * Replaces the double pushing instruction at the given offset by a simpler
+     * push instruction, if possible.
      */
-    private void replaceDoublePushInstruction(int offset, Instruction instruction)
+    private void replaceDoublePushInstruction(int offset)
     {
-        Value pushedValue = partialEvaluator.stackTopValueAfter(offset, 0);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
         if (pushedValue.isSpecific())
         {
             double value = pushedValue.doubleValue().value();
@@ -1641,29 +1797,26 @@ implements   MemberInfoVisitor,
                 value == 1.0)
             {
                 replacePushInstruction(offset,
-                                       (byte)(InstructionConstants.OP_DCONST_0 + value),
-                                       0);
+                                       InstructionConstants.OP_DCONST_0,
+                                       (int)value);
             }
         }
     }
 
 
     /**
-     * Replaces the given reference instruction by a simpler push instruction,
-     * if possible.
+     * Replaces the reference pushing instruction at the given offset by a
+     * simpler push instruction, if possible.
      */
-    private void replaceReferencePushInstruction(int offset, Instruction instruction)
+    private void replaceReferencePushInstruction(int offset)
     {
-        Value pushedValue = partialEvaluator.stackTopValueAfter(offset, 0);
+        Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
         if (pushedValue.isSpecific())
         {
-            ReferenceValue value = pushedValue.referenceValue();
-            if (value.isNull() == Value.ALWAYS)
-            {
-                replacePushInstruction(offset,
-                                       InstructionConstants.OP_ACONST_NULL,
-                                       0);
-            }
+            // A reference value can only be specific if it is null.
+            replacePushInstruction(offset,
+                                   InstructionConstants.OP_ACONST_NULL,
+                                   0);
         }
     }
 
@@ -1679,7 +1832,7 @@ implements   MemberInfoVisitor,
 
         if (DEBUG_ANALYSIS) System.out.println("  Replacing instruction at ["+offset+"] by "+replacementInstruction.toString());
 
-        codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
+        codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
 
         // Mark that the instruction has been simplified.
         isSimplified[offset] = true;
@@ -1700,21 +1853,18 @@ implements   MemberInfoVisitor,
      */
     private void replaceJsrInstruction(int offset, BranchInstruction branchInstruction)
     {
-        if (partialEvaluator.isTraced(offset))
+        // Is the subroutine ever returning?
+        if (!partialEvaluator.isSubroutineReturning(offset + branchInstruction.branchOffset))
         {
-            // Is the subroutine ever returning?
-            if (!isReturningFromSubroutine(offset + branchInstruction.branchOffset))
-            {
-                // All 'jsr' instructions to this subroutine can be replaced
-                // by unconditional branch instructions.
-                replaceBranchInstruction(offset, branchInstruction);
-            }
-            else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
-            {
-                // We have to make sure the instruction after this 'jsr'
-                // instruction is valid, even if it is never reached.
-                insertInfiniteLoop(offset + branchInstruction.length(offset));
-            }
+            // All 'jsr' instructions to this subroutine can be replaced
+            // by unconditional branch instructions.
+            replaceBranchInstruction(offset, branchInstruction);
+        }
+        else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
+        {
+            // We have to make sure the instruction after this 'jsr'
+            // instruction is valid, even if it is never reached.
+            insertInfiniteLoop(offset + branchInstruction.length(offset));
         }
     }
 
@@ -1725,46 +1875,98 @@ implements   MemberInfoVisitor,
      */
     private void replaceBranchInstruction(int offset, Instruction instruction)
     {
-        if (partialEvaluator.isTraced(offset))
+        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+
+        // Is there exactly one branch target (not from a goto or jsr)?
+        if (branchTargets != null &&
+            branchTargets.instructionOffsetCount() == 1)
         {
-            InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+            // Is it branching to the next instruction?
+            int branchOffset = branchTargets.instructionOffset(0) - offset;
+            if (branchOffset == instruction.length(offset))
+            {
+                if (DEBUG_ANALYSIS) System.out.println("  Deleting zero branch instruction at ["+offset+"]");
 
-            // Is there exactly one branch target (not from a goto or jsr)?
-            if (branchTargets != null &&
-                branchTargets.instructionOffsetCount() == 1)
+                // Delete the branch instruction.
+                codeAttributeEditor.deleteInstruction(offset);
+            }
+            else
             {
-                // Is it branching to the next instruction?
-                int branchOffset = branchTargets.instructionOffset(0) - offset;
-                if (branchOffset == instruction.length(offset))
-                {
-                    if (DEBUG_ANALYSIS) System.out.println("  Deleting zero branch instruction at ["+offset+"]");
+                // Replace the branch instruction by a simple branch instruction.
+                Instruction replacementInstruction =
+                    new BranchInstruction(InstructionConstants.OP_GOTO_W,
+                                          branchOffset).shrink();
 
-                    // Delete the branch instruction.
-                    codeAttrInfoEditor.deleteInstruction(offset);
-                }
-                else
+                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.
+                isSimplified[offset] = true;
+
+                // Visit the instruction, if required.
+                if (extraBranchInstructionVisitor != null)
                 {
-                    // Replace the branch instruction by a simple branch instruction.
-                    Instruction replacementInstruction =
-                        new BranchInstruction(InstructionConstants.OP_GOTO_W,
-                                              branchOffset).shrink();
+                    // Note: we're not passing the right arguments for now,
+                    // knowing that they aren't used anyway.
+                    extraBranchInstructionVisitor.visitBranchInstruction(null, null, null, offset, null);
+                }
+            }
+        }
+    }
 
-                    if (DEBUG_ANALYSIS) System.out.println("  Replacing branch instruction at ["+offset+"] by "+replacementInstruction.toString());
 
-                    codeAttrInfoEditor.replaceInstruction(offset,
-                                                          replacementInstruction);
+    /**
+     * Makes sure all branch targets of the given switch instruction are valid.
+     */
+    private void replaceSwitchInstruction(int offset, SwitchInstruction switchInstruction)
+    {
+        // Get the actual branch targets.
+        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+        int defaultOffset =
+            branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) -
+            offset;
+
+        Instruction replacementInstruction = null;
 
-                    // Mark that the instruction has been simplified.
-                    isSimplified[offset] = true;
+        // 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;
 
-                    // 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);
-                    }
-                }
+                // 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);
             }
         }
     }
@@ -1781,8 +1983,8 @@ implements   MemberInfoVisitor,
 
         if (DEBUG_ANALYSIS) System.out.println("  Inserting infinite loop at unreachable instruction at ["+offset+"]");
 
-        codeAttrInfoEditor.replaceInstruction(offset,
-                                              replacementInstruction);
+        codeAttributeEditor.replaceInstruction(offset,
+                                               replacementInstruction);
 
         // Mark that the instruction has been simplified.
         isNecessary[offset]  = true;
@@ -1793,58 +1995,33 @@ implements   MemberInfoVisitor,
     // Small utility methods.
 
     /**
-     * Initializes the parameter data structure.
+     * Returns whether the given instruction is a dup or swap instruction
+     * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x1, swap).
      */
-    private void initializeParameters(ClassFile    classFile,
-                                      MethodInfo   methodInfo,
-                                      CodeAttrInfo codeAttrInfo)
+    private boolean isDupOrSwap(Instruction instruction)
     {
-        // Initialize the parameters.
-        boolean isStatic =
-            (methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
-
-        // Count the number of parameters, taking into account their Categories.
-        String parameterDescriptor = methodInfo.getDescriptor(classFile);
-        int parameterSize = (isStatic ? 0 : 1) +
-            ClassUtil.internalMethodParameterSize(parameterDescriptor);
-
-        // Reuse the existing parameters object, ensuring the right size.
-        parameters.reset(parameterSize);
-
-        // Go over the parameters again.
-        InternalTypeEnumeration internalTypeEnumeration =
-            new InternalTypeEnumeration(parameterDescriptor);
-
-        int parameterIndex = 0;
-
-        // Put the caller's reference in parameter 0.
-        if (!isStatic)
-        {
-            parameters.store(parameterIndex++, ReferenceValueFactory.create(false));
-        }
-
-        while (internalTypeEnumeration.hasMoreTypes())
-        {
-            String type = internalTypeEnumeration.nextType();
-
-            // Get a generic corresponding value.
-            Value value = ValueFactory.create(type);
+        return instruction.opcode >= InstructionConstants.OP_DUP &&
+               instruction.opcode <= InstructionConstants.OP_SWAP;
+    }
 
-            // Store the value in the parameter.
-            parameters.store(parameterIndex, value);
 
-            // Increment the index according to the Category of the value.
-            parameterIndex += value.isCategory2() ? 2 : 1;
-        }
+    /**
+     * 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;
     }
 
 
     /**
      * Initializes the necessary data structure.
      */
-    private void initializeNecessary(CodeAttrInfo codeAttrInfo)
+    private void initializeNecessary(CodeAttribute codeAttribute)
     {
-        int codeLength = codeAttrInfo.u4codeLength;
+        int codeLength = codeAttribute.u4codeLength;
 
         // Create new arrays for storing information at each instruction offset.
         if (isNecessary.length < codeLength)
@@ -1864,43 +2041,22 @@ implements   MemberInfoVisitor,
 
 
     /**
-     * Returns whether the given instruction is a dup or swap instruction
-     * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x1, swap).
-     */
-    private boolean isDupOrSwap(Instruction instruction)
-    {
-        return instruction.opcode >= InstructionConstants.OP_DUP &&
-               instruction.opcode <= InstructionConstants.OP_SWAP;
-    }
-
-
-    /**
-     * Returns whether the subroutine starting at the given offset is ever
-     * returning.
-     */
-    private boolean isReturningFromSubroutine(int subroutineOffset)
-    {
-        int subroutineEnd = partialEvaluator.subroutineEnd(subroutineOffset);
-        return partialEvaluator.isSubroutineEnd(subroutineEnd) &&
-               partialEvaluator.isTraced(subroutineEnd);
-    }
-
-
-    /**
      * Returns whether the given variable is ever referenced (stored) by an
      * instruction that is marked as necessary.
      */
-    private boolean isVariableReferenced(CodeAttrInfo codeAttrInfo,
-                                         int          variableIndex)
+    private boolean isVariableReferenced(CodeAttribute codeAttribute,
+                                         int           startOffset,
+                                         int           variableIndex)
     {
-        int codeLength = codeAttrInfo.u4codeLength;
+        int codeLength = codeAttribute.u4codeLength;
 
-        for (int offset = 0; offset < codeLength; offset++)
+        for (int offset = startOffset; offset < codeLength; offset++)
         {
             if (isNecessary[offset]   &&
                 !isSimplified[offset] &&
-                partialEvaluator.variableValueBefore(offset, variableIndex) != null &&
-                isVariableNecessaryBefore(offset, variableIndex, false))
+                isNecessary(partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex),
+                            true,
+                            false))
             {
                 return true;
             }
@@ -1911,20 +2067,6 @@ implements   MemberInfoVisitor,
 
 
     /**
-     * Returns whether the given variable is present before execution of the
-     * instruction at the given offset.
-     */
-    private boolean isVariableNecessaryBefore(int     instructionOffset,
-                                              int     variableIndex,
-                                              boolean all)
-    {
-        return isNecessary(partialEvaluator.variableProducerOffsetsBefore(instructionOffset, variableIndex),
-                           true,
-                           all);
-    }
-
-
-    /**
      * Returns whether the given stack entry is present after execution of the
      * instruction at the given offset.
      */
@@ -1935,7 +2077,7 @@ implements   MemberInfoVisitor,
 
 //        if (present1 ^ present2)
 //        {
-//            throw new IllegalArgumentException("Can't handle partial use of dup2 instructions at ["+instructionOffset+"]");
+//            throw new IllegalArgumentException("Can't handle partial use of dup2 instructions");
 //        }
 
         return present1 || present2;
@@ -1950,7 +2092,7 @@ implements   MemberInfoVisitor,
                                                 int     stackIndex,
                                                 boolean all)
     {
-        return isNecessary(partialEvaluator.stackTopConsumerOffsetsBefore(instructionOffset, stackIndex),
+        return isNecessary(partialEvaluator.getStackBefore(instructionOffset).getTopConsumerValue(stackIndex),
                            false,
                            all);
     }
@@ -1964,9 +2106,23 @@ implements   MemberInfoVisitor,
                                                int     stackIndex,
                                                boolean all)
     {
-        return isNecessary(partialEvaluator.stackTopConsumerOffsetsAfter(instructionOffset, stackIndex),
+        return isNecessary(partialEvaluator.getStackAfter(instructionOffset).getTopConsumerValue(stackIndex),
                            false,
-                           all);
+                           all) ||
+               partialEvaluator.getStackAfter(instructionOffset).getTopProducerValue(stackIndex).instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY);
+    }
+
+
+    /**
+     * Returns whether any or all of the instructions at the given offsets are
+     * marked as necessary.
+     */
+    private boolean isNecessary(Value   offsets,
+                                boolean producer,
+                                boolean all)
+    {
+        return offsets != null &&
+               isNecessary(offsets.instructionOffsetValue(), producer, all);
     }
 
 
@@ -1984,8 +2140,8 @@ implements   MemberInfoVisitor,
         {
             int offset = offsets.instructionOffset(offsetIndex);
 
-            if (all ^ (offset == PartialEvaluator.AT_METHOD_ENTRY ||
-                       (isNecessary[offset] && (producer || !isSimplified[offset]))))
+            if (offset != PartialEvaluator.AT_METHOD_ENTRY &&
+                (all ^ (isNecessary[offset] && (producer || !isSimplified[offset]))))
             {
                 return !all;
             }
diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
new file mode 100644
index 0000000..41efe7c
--- /dev/null
+++ b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
@@ -0,0 +1,512 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+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.evaluation.value.*;
+
+/**
+ * This AttributeVisitor analyzes the liveness of the variables in the code
+ * attributes that it visits, based on partial evaluation.
+ *
+ * @author Eric Lafortune
+ */
+public class LivenessAnalyzer
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+    private static final int MAX_VARIABLES_SIZE = 64;
+
+    private final PartialEvaluator partialEvaluator;
+
+    private long[] isAliveBefore = new long[ClassConstants.TYPICAL_CODE_LENGTH];
+    private long[] isAliveAfter  = new long[ClassConstants.TYPICAL_CODE_LENGTH];
+    private long[] isCategory2   = new long[ClassConstants.TYPICAL_CODE_LENGTH];
+
+    // Fields acting as global temporary variables.
+    private boolean checkAgain;
+    private long    alive;
+
+
+    /**
+     * Creates a new LivenessAnalyzer.
+     */
+    public LivenessAnalyzer()
+    {
+        this(new PartialEvaluator());
+    }
+
+
+    /**
+     * Creates a new LivenessAnalyzer that will use the given partial evaluator.
+     * It will run this evaluator on every code attribute that it visits.
+     */
+    public LivenessAnalyzer(PartialEvaluator partialEvaluator)
+    {
+        this.partialEvaluator = partialEvaluator;
+    }
+
+
+    /**
+     * Returns whether the specified variable is alive before the instruction
+     * at the given offset.
+     */
+    public boolean isAliveBefore(int instructionOffset, int variableIndex)
+    {
+        return variableIndex >= MAX_VARIABLES_SIZE ||
+               (isAliveBefore[instructionOffset] & (1L << variableIndex)) != 0;
+    }
+
+
+    /**
+     * Sets whether the specified variable is alive before the instruction
+     * at the given offset.
+     */
+    public void setAliveBefore(int instructionOffset, int variableIndex, boolean alive)
+    {
+        if (variableIndex < MAX_VARIABLES_SIZE)
+        {
+            if (alive)
+            {
+                isAliveBefore[instructionOffset] |= 1L << variableIndex;
+            }
+            else
+            {
+                isAliveBefore[instructionOffset] &= ~(1L << variableIndex);
+            }
+        }
+    }
+
+
+    /**
+     * Returns whether the specified variable is alive after the instruction
+     * at the given offset.
+     */
+    public boolean isAliveAfter(int instructionOffset, int variableIndex)
+    {
+        return variableIndex >= MAX_VARIABLES_SIZE ||
+               (isAliveAfter[instructionOffset] & (1L << variableIndex)) != 0;
+    }
+
+
+    /**
+     * Sets whether the specified variable is alive after the instruction
+     * at the given offset.
+     */
+    public void setAliveAfter(int instructionOffset, int variableIndex, boolean alive)
+    {
+        if (variableIndex < MAX_VARIABLES_SIZE)
+        {
+            if (alive)
+            {
+                isAliveAfter[instructionOffset] |= 1L << variableIndex;
+            }
+            else
+            {
+                isAliveAfter[instructionOffset] &= ~(1L << variableIndex);
+            }
+        }
+    }
+
+
+    /**
+     * Returns whether the specified variable takes up two entries after the
+     * instruction at the given offset.
+     */
+    public boolean isCategory2(int instructionOffset, int variableIndex)
+    {
+        return variableIndex < MAX_VARIABLES_SIZE &&
+               (isCategory2[instructionOffset] & (1L << variableIndex)) != 0;
+    }
+
+
+    /**
+     * Sets whether the specified variable takes up two entries after the
+     * instruction at the given offset.
+     */
+    public void setCategory2(int instructionOffset, int variableIndex, boolean category2)
+    {
+        if (variableIndex < MAX_VARIABLES_SIZE)
+        {
+            if (category2)
+            {
+                isCategory2[instructionOffset] |= 1L << variableIndex;
+            }
+            else
+            {
+                isCategory2[instructionOffset] &= ~(1L << variableIndex);
+            }
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        if (DEBUG)
+        {
+            System.out.println();
+            System.out.println("Liveness analysis: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+        }
+
+        // Initialize the global arrays.
+        initializeArrays(codeAttribute);
+
+        // Evaluate the method.
+        partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
+
+        int codeLength    = codeAttribute.u4codeLength;
+        int variablesSize = codeAttribute.u2maxLocals;
+
+        // We'll only really analyze the first 64 variables.
+        if (variablesSize > MAX_VARIABLES_SIZE)
+        {
+            variablesSize = MAX_VARIABLES_SIZE;
+        }
+
+        // Mark liveness blocks, as many times as necessary.
+        do
+        {
+            checkAgain = false;
+            alive      = 0L;
+
+            // Loop over all traced instructions, backward.
+            for (int offset = codeLength - 1; offset >= 0; offset--)
+            {
+                if (partialEvaluator.isTraced(offset))
+                {
+                    // Update the liveness based on the branch targets.
+                    InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+                    if (branchTargets != null)
+                    {
+                        // Update the liveness right after the branch instruction.
+                        alive = combinedLiveness(branchTargets);
+                    }
+
+                    // Merge the current liveness.
+                    alive |= isAliveAfter[offset];
+
+                    // Update the liveness after the instruction.
+                    isAliveAfter[offset] = alive;
+
+                    // Update the current liveness based on the instruction.
+                    codeAttribute.instructionAccept(clazz, method, offset, this);
+
+                    // Merge the current liveness.
+                    alive |= isAliveBefore[offset];
+
+                    // Update the liveness before the instruction.
+                    if ((~isAliveBefore[offset] & alive) != 0L)
+                    {
+                        isAliveBefore[offset] = alive;
+
+                        // Do we have to check again after this loop?
+                        checkAgain |= offset < maxOffset(partialEvaluator.branchOrigins(offset));
+                    }
+                }
+            }
+
+            // Account for the liveness at the start of the exception handlers.
+            codeAttribute.exceptionsAccept(clazz, method, this);
+        }
+        while (checkAgain);
+
+        // Loop over all instructions, to mark variables that take up two entries.
+        for (int offset = 0; offset < codeLength; offset++)
+        {
+            if (partialEvaluator.isTraced(offset))
+            {
+                // Loop over all variables.
+                for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++)
+                {
+                    // Is the variable alive and a category 2 type?
+                    if (isAliveBefore(offset, variableIndex))
+                    {
+                        Value value = partialEvaluator.getVariablesBefore(offset).getValue(variableIndex);
+                        if (value != null && value.isCategory2())
+                        {
+                            // Mark it as such.
+                            setCategory2(offset, variableIndex, true);
+
+                            // Mark the next variable as well.
+                            setAliveBefore(offset, variableIndex + 1, true);
+                            setCategory2(  offset, variableIndex + 1, true);
+                        }
+                    }
+
+                    // Is the variable alive and a category 2 type?
+                    if (isAliveAfter(offset, variableIndex))
+                    {
+                        Value value = partialEvaluator.getVariablesAfter(offset).getValue(variableIndex);
+                        if (value != null && value.isCategory2())
+                        {
+                            // Mark it as such.
+                            setCategory2(offset, variableIndex, true);
+
+                            // Mark the next variable as well.
+                            setAliveAfter(offset, variableIndex + 1, true);
+                            setCategory2( offset, variableIndex + 1, true);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (DEBUG)
+        {
+            // Loop over all instructions.
+            for (int offset = 0; offset < codeLength; offset++)
+            {
+                if (partialEvaluator.isTraced(offset))
+                {
+                    long aliveBefore = isAliveBefore[offset];
+                    long aliveAfter  = isAliveAfter[offset];
+                    long category2   = isCategory2[offset];
+
+                    // Print out the liveness of all variables before the instruction.
+                    for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++)
+                    {
+                        long variableMask = (1L << variableIndex);
+                        System.out.print((aliveBefore & variableMask) == 0L ? '.' :
+                                         (category2   & variableMask) == 0L ? 'x' :
+                                                                              '*');
+                    }
+
+                    // Print out the instruction itself.
+                    System.out.println(" "+ InstructionFactory.create(codeAttribute.code, offset).toString(offset));
+
+                    // Print out the liveness of all variables after the instruction.
+                    for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++)
+                    {
+                        long variableMask = (1L << variableIndex);
+                        System.out.print((aliveAfter & variableMask) == 0L ? '.' :
+                                         (category2  & variableMask) == 0L ? 'x' :
+                                                                             '=');
+                    }
+
+                    System.out.println();
+                }
+            }
+        }
+    }
+
+
+    // 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)
+    {
+        int variableIndex = variableInstruction.variableIndex;
+        if (variableIndex < MAX_VARIABLES_SIZE)
+        {
+            long livenessMask = 1L << variableIndex;
+
+            // Is it a load instruction or a store instruction?
+            if (variableInstruction.isLoad())
+            {
+                // Start marking the variable before the load instruction.
+                alive |= livenessMask;
+            }
+            else if (variableInstruction.opcode != InstructionConstants.OP_IINC)
+            {
+                // Stop marking the variable before the store instruction.
+                alive &= ~livenessMask;
+
+                // But do mark the variable right after the store instruction.
+                isAliveAfter[offset] |= livenessMask;
+            }
+        }
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        // Special case: variable 0 ('this') in an initializer has to be alive
+        // as long as it hasn't been initialized.
+         if (offset == partialEvaluator.superInitializationOffset())
+        {
+            alive |= 1L;
+        }
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        // Are any variables alive at the start of the handler?
+        long alive = isAliveBefore[exceptionInfo.u2handlerPC];
+        if (alive != 0L)
+        {
+            // Set the same liveness flags for the entire try block.
+            int startOffset = exceptionInfo.u2startPC;
+            int endOffset   = exceptionInfo.u2endPC;
+
+            for (int offset = startOffset; offset < endOffset; offset++)
+            {
+                if (partialEvaluator.isTraced(offset))
+                {
+                    if ((~(isAliveBefore[offset] & isAliveAfter[offset]) & alive) != 0L)
+                    {
+                        isAliveBefore[offset] |= alive;
+                        isAliveAfter[offset]  |= alive;
+
+                        // Check again after having marked this try block.
+                        checkAgain = true;
+                    }
+                }
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Initializes the global arrays.
+     */
+    private void initializeArrays(CodeAttribute codeAttribute)
+    {
+        int codeLength = codeAttribute.u4codeLength;
+
+        // Create new arrays for storing information at each instruction offset.
+        if (isAliveBefore.length < codeLength)
+        {
+            isAliveBefore = new long[codeLength];
+            isAliveAfter  = new long[codeLength];
+            isCategory2   = new long[codeLength];
+        }
+        else
+        {
+            for (int index = 0; index < codeLength; index++)
+            {
+                isAliveBefore[index] = 0L;
+                isAliveAfter[index]  = 0L;
+                isCategory2[index]   = 0L;
+            }
+        }
+    }
+
+
+    private long combinedLiveness(InstructionOffsetValue instructionOffsetValue)
+    {
+        long alive = 0L;
+
+        int count = instructionOffsetValue.instructionOffsetCount();
+        for (int index = 0; index < count; index++)
+        {
+            alive |= isAliveBefore[instructionOffsetValue.instructionOffset(index)];
+        }
+
+        return alive;
+    }
+
+
+    /**
+     * Returns the minimum offset from the given instruction offsets.
+     */
+    private int minOffset(Value instructionOffsets)
+    {
+        return minOffset(instructionOffsets, Integer.MAX_VALUE);
+    }
+
+
+    /**
+     * Returns the minimum offset from the given instruction offsets.
+     */
+    private int minOffset(Value instructionOffsets, int minOffset)
+    {
+        if (instructionOffsets != null)
+        {
+            InstructionOffsetValue instructionOffsetValue =
+                instructionOffsets.instructionOffsetValue();
+
+            int count = instructionOffsetValue.instructionOffsetCount();
+            for (int index = 0; index < count; index++)
+            {
+                int offset = instructionOffsetValue.instructionOffset(index);
+                if (minOffset > offset)
+                {
+                    minOffset = offset;
+                }
+            }
+        }
+
+        return minOffset;
+    }
+
+
+    /**
+     * Returns the maximum offset from the given instruction offsets.
+     */
+    private int maxOffset(Value instructionOffsets)
+    {
+        return maxOffset(instructionOffsets, Integer.MIN_VALUE);
+    }
+
+
+    /**
+     * Returns the maximum offset from the given instruction offsets.
+     */
+    private int maxOffset(Value instructionOffsets, int maxOffset)
+    {
+        if (instructionOffsets != null)
+        {
+            InstructionOffsetValue instructionOffsetValue =
+                instructionOffsets.instructionOffsetValue();
+
+            int count = instructionOffsetValue.instructionOffsetCount();
+            for (int index = 0; index < count; index++)
+            {
+                int offset = instructionOffsetValue.instructionOffset(index);
+                if (maxOffset < offset)
+                {
+                    maxOffset = offset;
+                }
+            }
+        }
+
+        return maxOffset;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
new file mode 100644
index 0000000..54991c9
--- /dev/null
+++ b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
@@ -0,0 +1,114 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.RefConstant;
+import proguard.evaluation.BasicInvocationUnit;
+import proguard.evaluation.value.*;
+
+/**
+ * This InvocationUbit loads parameter values and return values that were
+ * previously stored with the methods that are invoked.
+ *
+ * @see StoringInvocationUnit
+ * @author Eric Lafortune
+ */
+public class LoadingInvocationUnit
+extends      BasicInvocationUnit
+{
+    // Implementations for BasicInvocationUnit.
+
+    protected Value getFieldClassValue(Clazz       clazz,
+                                       RefConstant refConstant,
+                                       String      type)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember);
+            if (value != null)
+            {
+                return value;
+            }
+        }
+
+        return super.getFieldClassValue(clazz, refConstant, type);
+    }
+
+
+    protected Value getFieldValue(Clazz       clazz,
+                                  RefConstant refConstant,
+                                  String      type)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember);
+            if (value != null)
+            {
+                return value;
+            }
+        }
+
+        return super.getFieldValue(clazz, refConstant, type);
+    }
+
+
+    protected Value getMethodParameterValue(Clazz  clazz,
+                                            Method method,
+                                            int    parameterIndex,
+                                            String type,
+                                            Clazz  referencedClass)
+    {
+        Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex);
+        if (value != null)
+        {
+            return value;
+        }
+
+        return super.getMethodParameterValue(clazz,
+                                             method,
+                                             parameterIndex,
+                                             type,
+                                             referencedClass);
+    }
+
+
+    protected Value getMethodReturnValue(Clazz       clazz,
+                                         RefConstant refConstant,
+                                         String      type)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember);
+            if (value != null)
+            {
+                return value;
+            }
+        }
+
+        return super.getMethodReturnValue(clazz,
+                                          refConstant,
+                                          type);
+    }
+}
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
index 90332b8..6cd00c1 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -1,6 +1,6 @@
-/* $Id: PartialEvaluator.java,v 1.37.2.6 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,19 +22,25 @@ package proguard.optimize.evaluation;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.ClassConstant;
 import proguard.classfile.instruction.*;
-import proguard.classfile.util.ClassUtil;
-import proguard.optimize.evaluation.value.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.ClassPrinter;
+import proguard.evaluation.*;
+import proguard.evaluation.value.*;
 import proguard.optimize.peephole.BranchTargetFinder;
 
 /**
- * This class performs partial evaluation.
+ * This AttributeVisitor performs partial evaluation on the code attributes
+ * that it visits.
  *
  * @author Eric Lafortune
  */
 public class PartialEvaluator
-implements   ExceptionInfoVisitor,
-             InstructionVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             ExceptionInfoVisitor
 {
     //*
     private static final boolean DEBUG         = false;
@@ -44,68 +50,129 @@ implements   ExceptionInfoVisitor,
     private static boolean DEBUG_RESULTS = true;
     //*/
 
-    private static final int INITIAL_CODE_LENGTH = 1024;
-    private static final int INITIAL_VALUE_COUNT = 32;
-
     private static final int MAXIMUM_EVALUATION_COUNT = 5;
 
+    public static final int NONE            = -2;
     public static final int AT_METHOD_ENTRY = -1;
     public static final int AT_CATCH_ENTRY  = -1;
-    public static final int NONE            = -1;
-
-    private BranchTargetFinder branchTargetFinder = new BranchTargetFinder(1024);
-
-    private InstructionOffsetValue[] varProducerValues    = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
-    private InstructionOffsetValue[] stackProducerValues  = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
-    private InstructionOffsetValue[] unusedProducerValues = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
-    private InstructionOffsetValue[] branchOriginValues   = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
-    private InstructionOffsetValue[] branchTargetValues   = new InstructionOffsetValue[INITIAL_CODE_LENGTH];
-    private TracedVariables[]        variablesBefore      = new TracedVariables[INITIAL_CODE_LENGTH];
-    private TracedStack[]            stacksBefore         = new TracedStack[INITIAL_CODE_LENGTH];
-    private TracedVariables[]        variablesAfter       = new TracedVariables[INITIAL_CODE_LENGTH];
-    private TracedStack[]            stacksAfter          = new TracedStack[INITIAL_CODE_LENGTH];
-    private boolean[]                generalizedContexts  = new boolean[INITIAL_CODE_LENGTH];
-    private int[]                    evaluationCounts     = new int[INITIAL_CODE_LENGTH];
-    private int[]                    initializedVariables = new int[INITIAL_CODE_LENGTH];
+
+    private final ValueFactory   valueFactory;
+    private final InvocationUnit invocationUnit;
+    private final boolean        evaluateAllCode;
+
+    private InstructionOffsetValue[] varProducerValues     = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+    private InstructionOffsetValue[] stackProducerValues   = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+    private InstructionOffsetValue[] branchOriginValues    = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+    private InstructionOffsetValue[] branchTargetValues    = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
+    private TracedVariables[]        variablesBefore       = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
+    private TracedStack[]            stacksBefore          = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
+    private TracedVariables[]        variablesAfter        = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH];
+    private TracedStack[]            stacksAfter           = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH];
+    private boolean[]                generalizedContexts   = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]                    evaluationCounts      = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]                    initializedVariables  = new int[ClassConstants.TYPICAL_CODE_LENGTH];
     private boolean                  evaluateExceptions;
 
-    private TracedVariables  variables  = new TracedVariables(INITIAL_VALUE_COUNT);
-    private TracedStack      stack      = new TracedStack(INITIAL_VALUE_COUNT);
-    private TracedBranchUnit branchUnit = new TracedBranchUnit();
+    private final BasicBranchUnit    branchUnit;
+    private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
+//    private ClassCleaner       classCleaner       = new ClassCleaner();
+
+
+    /**
+     * Creates a new PartialEvaluator.
+     */
+    public PartialEvaluator()
+    {
+        this(new ValueFactory(), new BasicInvocationUnit(), true);
+    }
 
 
     /**
-     * Performs partial evaluation of the given method with the given parameters.
-     * @param classFile    the method's class file.
-     * @param methodInfo   the method's header.
-     * @param codeAttrInfo the method's code.
-     * @param parameters   the method parameters.
-     * @return             the partial result.
+     * 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 'catch' blocks should be evaluated,
+     *                        even if they are unreachable.
      */
-    public Value evaluate(ClassFile    classFile,
-                          MethodInfo   methodInfo,
-                          CodeAttrInfo codeAttrInfo,
-                          Variables    parameters)
+    public PartialEvaluator(ValueFactory   valueFactory,
+                            InvocationUnit invocationUnit,
+                            boolean        evaluateAllCode)
+    {
+        this.valueFactory    = valueFactory;
+        this.invocationUnit  = invocationUnit;
+        this.evaluateAllCode = evaluateAllCode;
+
+        this.branchUnit = evaluateAllCode ?
+            new BasicBranchUnit() :
+            new TracedBranchUnit();
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
 //        DEBUG = DEBUG_RESULTS =
-//            classFile.getName().equals("abc/Def") &&
-//            methodInfo.getName(classFile).equals("abc");
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
 
-        // Initialize the reusable arrays and variables.
-        initializeVariables(codeAttrInfo, parameters);
+        // TODO: Remove this when the partial evaluator has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
+        {
+            System.err.println("Unexpected error while performing 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()+")");
+
+            if (DEBUG)
+            {
+                method.accept(clazz, new ClassPrinter());
+            }
+
+            throw ex;
+        }
+    }
 
-        // Find all instruction offsets,...
-        codeAttrInfo.accept(classFile, methodInfo, branchTargetFinder);
 
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
         // Evaluate the instructions, starting at the entry point.
-        if (DEBUG) System.out.println("Partial evaluation: ");
+        if (DEBUG)
+        {
+            System.out.println();
+            System.out.println("Partial evaluation: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+            System.out.println("  Max locals = "+codeAttribute.u2maxLocals);
+            System.out.println("  Max stack  = "+codeAttribute.u2maxStack);
+        }
+
+        // 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 reusable arrays and variables.
+        initializeVariables(clazz, method, codeAttribute, variables, stack);
+
+        // Find all instruction offsets,...
+        codeAttribute.accept(clazz, method, branchTargetFinder);
 
-        evaluateInstructionBlock(classFile,
-                                 methodInfo,
-                                 codeAttrInfo,
+        // Start executing the first instruction block.
+        evaluateInstructionBlock(clazz,
+                                 method,
+                                 codeAttribute,
                                  variables,
                                  stack,
-                                 branchUnit,
                                  0);
 
         // Evaluate the exception catch blocks, until their entry variables
@@ -116,7 +183,7 @@ implements   ExceptionInfoVisitor,
             evaluateExceptions = false;
 
             // Evaluate all relevant exception catch blocks once.
-            codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+            codeAttribute.exceptionsAccept(clazz, method, this);
         }
         while (evaluateExceptions);
 
@@ -124,14 +191,23 @@ implements   ExceptionInfoVisitor,
         {
             System.out.println("Evaluation results:");
 
-            int codeLength = codeAttrInfo.u4codeLength;
-
             int offset = 0;
             do
             {
-                Instruction instruction = InstructionFactory.create(codeAttrInfo.code,
+                if (isBranchOrExceptionTarget(offset))
+                {
+                    System.out.println("Branch target:");
+                    if (isTraced(offset))
+                    {
+                        System.out.println("  Vars:  "+variablesBefore[offset]);
+                        System.out.println("  Stack: "+stacksBefore[offset]);
+                    }
+                }
+
+                Instruction instruction = InstructionFactory.create(codeAttribute.code,
                                                                     offset);
                 System.out.println(instruction.toString(offset));
+
                 if (isTraced(offset))
                 {
                     InstructionOffsetValue varProducerOffsets = varProducerOffsets(offset);
@@ -146,10 +222,10 @@ implements   ExceptionInfoVisitor,
                         System.out.println("     has overall been using information from instructions setting stack: "+stackProducerOffsets);
                     }
 
-                    InstructionOffsetValue unusedProducerOffsets = unusedProducerOffsets(offset);
-                    if (unusedProducerOffsets.instructionOffsetCount() > 0)
+                    int initializationOffset = branchTargetFinder.initializationOffset(offset);
+                    if (initializationOffset != NONE)
                     {
-                        System.out.println("     no longer needs information from instructions setting stack: "+unusedProducerOffsets);
+                        System.out.println("     is to be initialized at ["+initializationOffset+"]");
                     }
 
                     InstructionOffsetValue branchTargets = branchTargets(offset);
@@ -158,34 +234,14 @@ implements   ExceptionInfoVisitor,
                         System.out.println("     has overall been branching to "+branchTargets);
                     }
 
-                    System.out.println("     Vars:  "+variablesAfter[offset]);
-                    System.out.println("     Stack: "+stacksAfter[offset]);
+                    System.out.println("  Vars:  "+variablesAfter[offset]);
+                    System.out.println("  Stack: "+stacksAfter[offset]);
                 }
 
                 offset += instruction.length(offset);
             }
-            while (offset < codeLength);
+            while (offset < codeAttribute.u4codeLength);
         }
-
-        if (DEBUG)
-        {
-            Value returnValue = branchUnit.getTraceReturnValue();
-            if (returnValue != null)
-            {
-                System.out.println("Return value for method "+
-                                   ClassUtil.externalFullMethodDescription(classFile.getName(),
-                                                                           0,
-                                                                           methodInfo.getName(classFile),
-                                                                           methodInfo.getDescriptor(classFile))+
-                                   " -> ["+returnValue.toString()+"]");
-                System.out.println();
-            }
-        }
-
-        // Mark special dependencies of constructors.
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
-
-        return branchUnit.getTraceReturnValue();
     }
 
 
@@ -217,163 +273,147 @@ implements   ExceptionInfoVisitor,
 
 
     /**
-     * Returns whether the instruction at the given offset is the end of a
-     * subroutine.
+     * Returns whether the instruction at the given offset is the target of a
+     * branch instruction or an exception.
      */
-    public boolean isSubroutineEnd(int instructionOffset)
+    public boolean isBranchOrExceptionTarget(int instructionOffset)
     {
-        return branchTargetFinder.isSubroutineEnd(instructionOffset);
+        return branchTargetFinder.isBranchTarget(instructionOffset) ||
+               branchTargetFinder.isExceptionHandler(instructionOffset);
     }
 
 
     /**
-     * Returns the offset of the end of the subroutine that starts at the given
-     * offset.
+     * Returns whether the instruction at the given offset is part of a
+     * subroutine.
      */
-    public int subroutineEnd(int instructionOffset)
+    public boolean isSubroutine(int instructionOffset)
     {
-        return branchTargetFinder.subroutineEnd(instructionOffset);
+        return branchTargetFinder.isSubroutine(instructionOffset);
     }
 
 
     /**
-     * Returns the value of the given variable before the given instruction
-     * offset.
+     * Returns whether the subroutine at the given offset is ever returning
+     * by means of a regular 'ret' instruction.
      */
-    public Value variableValueBefore(int instructionOffset,
-                                     int variableIndex)
+    public boolean isSubroutineReturning(int instructionOffset)
     {
-        return variablesBefore[instructionOffset].load(variableIndex);
+        return branchTargetFinder.isSubroutineReturning(instructionOffset);
     }
 
 
     /**
-     * Returns the value of the given variable after the given instruction
+     * Returns the offset after the subroutine that starts at the given
      * offset.
      */
-    public Value variableValueAfter(int instructionOffset,
-                                    int variableIndex)
+    public int subroutineEnd(int instructionOffset)
     {
-        return variablesAfter[instructionOffset].load(variableIndex);
+        return branchTargetFinder.subroutineEnd(instructionOffset);
     }
 
 
     /**
-     * Returns the instruction offsets that set the value of the given variable
-     * before the given instruction offset.
+     * Returns the instruction offset at which the object instance that is
+     * created at the given 'new' instruction offset is initialized, or
+     * <code>NONE</code> if it is not being created.
      */
-    public InstructionOffsetValue variableProducerOffsetsBefore(int instructionOffset,
-                                                                int variableIndex)
+    public int initializationOffset(int instructionOffset)
     {
-        return variablesBefore[instructionOffset].getStoredTraceValue(variableIndex).instructionOffsetValue();
+        return branchTargetFinder.initializationOffset(instructionOffset);
     }
 
 
     /**
-     * Returns the instruction offsets that set the value of the given variable
-     * after the given instruction offset.
+     * Returns whether the method is an instance initializer.
      */
-    public InstructionOffsetValue variableProducerOffsetsAfter(int instructionOffset,
-                                                               int variableIndex)
+    public boolean isInitializer()
     {
-        return variablesAfter[instructionOffset].getStoredTraceValue(variableIndex).instructionOffsetValue();
+        return branchTargetFinder.isInitializer();
     }
 
 
     /**
-     * Returns the instruction offsets that set the variable that is being
-     * used at the given instruction offset.
+     * Returns the instruction offset at which this initializer is calling
+     * the "super" or "this" initializer method, or <code>NONE</code> if it is
+     * not an initializer.
      */
-    public InstructionOffsetValue varProducerOffsets(int instructionOffset)
+    public int superInitializationOffset()
     {
-        return varProducerValues[instructionOffset];
+        return branchTargetFinder.superInitializationOffset();
     }
 
 
     /**
-     * Returns the value of the given stack entry before the given instruction
-     * offset.
+     * Returns the offset of the 'new' instruction that corresponds to the
+     * invocation of the instance initializer at the given offset, or
+     * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or
+     * "this" initializer method, , or <code>NONE</code> if it is not a 'new'
+     * instruction.
      */
-    public Value stackTopValueBefore(int instructionOffset,
-                                     int stackIndex)
+    public int creationOffset(int offset)
     {
-        return stacksBefore[instructionOffset].getTop(stackIndex);
+        return branchTargetFinder.creationOffset(offset);
     }
 
 
     /**
-     * Returns the value of the given stack entry after the given instruction
+     * Returns the variables before execution of the instruction at the given
      * offset.
      */
-    public Value stackTopValueAfter(int instructionOffset,
-                                    int stackIndex)
+    public TracedVariables getVariablesBefore(int instructionOffset)
     {
-        return stacksAfter[instructionOffset].getTop(stackIndex);
+        return variablesBefore[instructionOffset];
     }
 
 
     /**
-     * Returns the instruction offsets that set the value of the given stack
-     * entry before the given instruction offset.
-     */
-    public InstructionOffsetValue stackTopProducerOffsetsBefore(int instructionOffset,
-                                                                int stackIndex)
-    {
-        return stacksAfter[instructionOffset].getTopProducerValue(stackIndex).instructionOffsetValue();
-    }
-
-
-    /**
-     * Returns the instruction offsets that set the value of the given stack
-     * entry after the given instruction offset.
+     * Returns the variables after execution of the instruction at the given
+     * offset.
      */
-    public InstructionOffsetValue stackTopProducerOffsetsAfter(int instructionOffset,
-                                                               int stackIndex)
+    public TracedVariables getVariablesAfter(int instructionOffset)
     {
-        return stacksAfter[instructionOffset].getTopProducerValue(stackIndex).instructionOffsetValue();
+        return variablesAfter[instructionOffset];
     }
 
 
     /**
-     * Returns the instruction offsets that set the stack entries that are being
+     * Returns the instruction offsets that set the variable that is being
      * used at the given instruction offset.
      */
-    public InstructionOffsetValue stackProducerOffsets(int instructionOffset)
+    public InstructionOffsetValue varProducerOffsets(int instructionOffset)
     {
-        return stackProducerValues[instructionOffset];
+        return varProducerValues[instructionOffset];
     }
 
 
     /**
-     * Returns the instruction offsets that use the value of the given stack
-     * entry before the given instruction offset.
+     * Returns the stack before execution of the instruction at the given
+     * offset.
      */
-    public InstructionOffsetValue stackTopConsumerOffsetsBefore(int instructionOffset,
-                                                               int stackIndex)
+    public TracedStack getStackBefore(int instructionOffset)
     {
-        return stacksBefore[instructionOffset].getTopConsumerValue(stackIndex).instructionOffsetValue();
+        return stacksBefore[instructionOffset];
     }
 
 
     /**
-     * Returns the instruction offsets that use the value of the given stack
-     * entry after the given instruction offset.
+     * Returns the stack after execution of the instruction at the given
+     * offset.
      */
-    public InstructionOffsetValue stackTopConsumerOffsetsAfter(int instructionOffset,
-                                                               int stackIndex)
+    public TracedStack getStackAfter(int instructionOffset)
     {
-        return stacksAfter[instructionOffset].getTopConsumerValue(stackIndex).instructionOffsetValue();
+        return stacksAfter[instructionOffset];
     }
 
 
     /**
-     * Returns the instruction offsets that set stack entries that are not being
-     * used at the given instruction offset (e.g. because the parameters are not
-     * being used).
+     * Returns the instruction offsets that set the stack entries that are being
+     * used at the given instruction offset.
      */
-    public InstructionOffsetValue unusedProducerOffsets(int instructionOffset)
+    public InstructionOffsetValue stackProducerOffsets(int instructionOffset)
     {
-        return unusedProducerValues[instructionOffset];
+        return stackProducerValues[instructionOffset];
     }
 
 
@@ -399,7 +439,7 @@ implements   ExceptionInfoVisitor,
 
     /**
      * Returns the variable that is initialized at the given instruction offset,
-     * or NONE if no variable was initialized.
+     * or <code>NONE</code> if no variable was initialized.
      */
     public int initializedVariable(int instructionOffset)
     {
@@ -409,146 +449,184 @@ implements   ExceptionInfoVisitor,
 
     // Implementations for ExceptionInfoVisitor.
 
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
-        if (isTraced(exceptionInfo.u2startpc, exceptionInfo.u2endpc))
+        int startPC = exceptionInfo.u2startPC;
+        int endPC   = exceptionInfo.u2endPC;
+
+        // Do we have to evaluate this exception catch block?
+        if (isTraced(startPC, endPC))
         {
-            if (DEBUG) System.out.println("Partial evaluation of exception ["+exceptionInfo.u2startpc+","+exceptionInfo.u2endpc+"] -> ["+exceptionInfo.u2handlerpc+"]:");
+            int handlerPC = exceptionInfo.u2handlerPC;
+            int catchType = exceptionInfo.u2catchType;
 
-            // Generalize the variables of the try block.
-            variables.reset(codeAttrInfo.u2maxLocals);
-            generalizeVariables(exceptionInfo.u2startpc,
-                                exceptionInfo.u2endpc,
-                                variables);
+            if (DEBUG) System.out.println("Partial evaluation of exception ["+startPC +","+endPC +"] -> ["+handlerPC+"]:");
 
-            // Remember the entry variables of the exception.
-            TracedVariables exceptionVariables = (TracedVariables)exceptionInfo.getVisitorInfo();
-            if (exceptionVariables == null)
-            {
-                exceptionVariables = new TracedVariables(codeAttrInfo.u2maxLocals);
+            // Reuse the existing variables and stack objects, ensuring the
+            // right size.
+            TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals);
+            TracedStack     stack     = new TracedStack(codeAttribute.u2maxStack);
 
-                exceptionInfo.setVisitorInfo(exceptionVariables);
-            }
-            else
-            {
-                // Bail out if the entry variables are the same as last time.
-                if (exceptionVariables.equals(variables))
-                {
-                    if (DEBUG) System.out.println("  Repeated initial variables");
+            // Initialize the trace values.
+            Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY);
+            variables.setProducerValue(storeValue);
+            stack.setProducerValue(storeValue);
 
-                    return;
-                }
-            }
+            // 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);
 
-            exceptionVariables.initialize(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;
 
-            // Reuse the existing variables and stack objects, ensuring the right size.
-            variables.reset(codeAttrInfo.u2maxLocals);
-            stack.reset(codeAttrInfo.u2maxStack);
+            Clazz catchClass = catchType != 0 ?
+                ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass :
+                null;
 
-            // The initial stack has a generic instruction offset.
-            Value storeValue = InstructionOffsetValueFactory.create(AT_CATCH_ENTRY);
-            variables.setProducerValue(storeValue);
-            stack.setProducerValue(storeValue);
+            stack.push(valueFactory.createReferenceValue(catchClassName,
+                                                         catchClass,
+                                                         false));
 
-            // Initialize the local variables and the stack.
-            variables.initialize(exceptionVariables);
-            //stack.push(ReferenceValueFactory.create((ClassCpInfo)((ProgramClassFile)classFile).getCpEntry(exceptionInfo.u2catchType), false));
-            stack.push(ReferenceValueFactory.create(false));
+            int evaluationCount = evaluationCounts[handlerPC];
 
             // Evaluate the instructions, starting at the entry point.
-            evaluateInstructionBlock(classFile,
-                                     methodInfo,
-                                     codeAttrInfo,
+            evaluateInstructionBlock(clazz,
+                                     method,
+                                     codeAttribute,
                                      variables,
                                      stack,
-                                     branchUnit,
-                                     exceptionInfo.u2handlerpc);
+                                     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");
 
-            // Remember to check this exception and other exceptions once more.
+            // 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+"]");
+        }
     }
 
 
-    // Implementations for InstructionVisitor.
+    // Utility methods to evaluate instruction blocks.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    /**
+     * Pushes block of instructions to be executed on the given stack.
+     */
+    private void pushInstructionBlock(TracedVariables variables,
+                                      TracedStack     stack,
+                                      int             startOffset,
+                                      java.util.Stack instructionBlockStack)
+    {
+        instructionBlockStack.push(new MyInstructionBlock(variables,
+                                                          stack,
+                                                          startOffset));
+    }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    /**
+     * Evaluates a block of instructions, starting at the given offset and ending
+     * at a branch instruction, a return instruction, or a throw instruction.
+     */
+    private void evaluateInstructionBlock(Clazz           clazz,
+                                          Method          method,
+                                          CodeAttribute   codeAttribute,
+                                          TracedVariables variables,
+                                          TracedStack     stack,
+                                          int             startOffset)
     {
-        // Make sure 'new' instructions (or subsequent 'dup' instructions)
-        // depend on the subsequent initializer calls, in case these calls
-        // are marked as not having any side effects.
+        // 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);
 
-        // Check if the invoked method is an initalizer.
-        if (isTraced(offset) &&
-            branchTargetFinder.isInitializer(offset))
+        // Execute all resulting instruction blocks on the execution stack.
+        while (!instructionBlockStack.empty())
         {
-            // Find the previous instruction (assuming there was no branch).
-            int previousOffset = offset - 1;
-            while (!isTraced(previousOffset))
-            {
-                previousOffset--;
-            }
-
-            // Compute the stack index of the uninitialized object.
-            int stackIndex = stacksAfter[offset].size();
-
-            // Get the (first and presumably only) offset of the instruction
-            // that put it there. This is typically a dup instruction.
-            int newOffset = stacksAfter[previousOffset].getBottomProducerValue(stackIndex).instructionOffsetValue().instructionOffset(0);
-
-            // Add a reverse dependency. The source instruction depends on
-            // the initializer instruction, thus making sure that the latter
-            // is preserved whenever the former is used.
-            stackProducerValues[newOffset] = stackProducerValues[newOffset].generalize(InstructionOffsetValueFactory.create(offset)).instructionOffsetValue();
+            MyInstructionBlock instructionBlock =
+                (MyInstructionBlock)instructionBlockStack.pop();
+
+            evaluateInstructionBlock(clazz,
+                                     method,
+                                     codeAttribute,
+                                     instructionBlock.variables,
+                                     instructionBlock.stack,
+                                     instructionBlock.startOffset,
+                                     instructionBlockStack);
         }
     }
 
 
-    // Utility methods to evaluate instruction blocks.
-
     /**
      * Evaluates a block of instructions, starting at the given offset and ending
      * at a branch instruction, a return instruction, or a throw instruction.
+     * Instruction blocks that are to be evaluated as a result are pushed on
+     * the given stack.
      */
-    private void evaluateInstructionBlock(ClassFile        classFile,
-                                          MethodInfo       methodInfo,
-                                          CodeAttrInfo     codeAttrInfo,
+    private void evaluateInstructionBlock(Clazz            clazz,
+                                          Method           method,
+                                          CodeAttribute    codeAttribute,
                                           TracedVariables  variables,
                                           TracedStack      stack,
-                                          TracedBranchUnit branchUnit,
-                                          int              instructionOffset)
+                                          int              startOffset,
+                                          java.util.Stack  instructionBlockStack)
     {
-        byte[] code = codeAttrInfo.code;
+        byte[] code = codeAttribute.code;
 
         if (DEBUG)
         {
-             System.out.println("Instruction block starting at ["+instructionOffset+"] in "+
-                                ClassUtil.externalFullMethodDescription(classFile.getName(),
+             System.out.println("Instruction block starting at ["+startOffset+"] in "+
+                                ClassUtil.externalFullMethodDescription(clazz.getName(),
                                                                         0,
-                                                                        methodInfo.getName(classFile),
-                                                                        methodInfo.getDescriptor(classFile)));
+                                                                        method.getName(clazz),
+                                                                        method.getDescriptor(clazz)));
              System.out.println("Init vars:  "+variables);
              System.out.println("Init stack: "+stack);
         }
 
-        Processor processor = new Processor(variables, stack, branchUnit);
+        Processor processor = new Processor(variables, stack, valueFactory, branchUnit, invocationUnit);
+
+        int instructionOffset = startOffset;
 
-        UnusedParameterCleaner unusedParameterCleaner = new UnusedParameterCleaner(stack);
+        int maxOffset = startOffset;
 
         // Evaluate the subsequent instructions.
         while (true)
         {
+            if (maxOffset < instructionOffset)
+            {
+                maxOffset = instructionOffset;
+            }
+
             // Maintain a generalized local variable frame and stack at this
             // instruction offset, before execution.
-            int evaluationCount = evaluationCounts[instructionOffset]++;
+            int evaluationCount = evaluationCounts[instructionOffset];
             if (evaluationCount == 0)
             {
                 // First time we're passing by this instruction.
@@ -572,7 +650,7 @@ implements   ExceptionInfoVisitor,
             else
             {
                 // Merge in the current context.
-                boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables);
+                boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true);
                 boolean stackChanged     = stacksBefore[instructionOffset].generalize(stack);
 
                 // Bail out if the current context is the same as last time.
@@ -589,12 +667,13 @@ implements   ExceptionInfoVisitor,
                 // of times.
                 if (evaluationCount >= MAXIMUM_EVALUATION_COUNT)
                 {
+                    if (DEBUG) System.out.println("Generalizing current context after "+evaluationCount+" evaluations");
 
                     // Continue, but generalize the current context.
                     // Note that the most recent variable values have to remain
                     // last in the generalizations, for the sake of the ret
                     // instruction.
-                    variables.generalize(variablesBefore[instructionOffset]);
+                    variables.generalize(variablesBefore[instructionOffset], false);
                     stack.generalize(stacksBefore[instructionOffset]);
 
                     // We'll execute in the generalized context.
@@ -607,16 +686,18 @@ implements   ExceptionInfoVisitor,
                 }
             }
 
+            // We'll evaluate this instruction.
+            evaluationCounts[instructionOffset]++;
+
             // Remember this instruction's offset with any stored value.
             Value storeValue = new InstructionOffsetValue(instructionOffset);
             variables.setProducerValue(storeValue);
             stack.setProducerValue(storeValue);
 
             // Reset the trace value.
-            InstructionOffsetValue traceValue = InstructionOffsetValueFactory.create();
+            InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
             variables.setCollectedProducerValue(traceValue);
             stack.setCollectedProducerValue(traceValue);
-            unusedParameterCleaner.setTraceValue(traceValue);
 
             // Reset the initialization flag.
             variables.resetInitialization();
@@ -632,14 +713,6 @@ implements   ExceptionInfoVisitor,
             branchUnit.resetCalled();
             branchUnit.setTraceBranchTargets(nextInstructionOffsetValue);
 
-            // First clean all traces to unused parameters if this is a method
-            // invocation.
-            instruction.accept(classFile,
-                               methodInfo,
-                               codeAttrInfo,
-                               instructionOffset,
-                               unusedParameterCleaner);
-
             if (DEBUG)
             {
                 System.out.println(instruction.toString(instructionOffset));
@@ -650,17 +723,17 @@ implements   ExceptionInfoVisitor,
                 // Process the instruction. The processor may modify the
                 // variables and the stack, and it may call the branch unit
                 // and the invocation unit.
-                instruction.accept(classFile,
-                                   methodInfo,
-                                   codeAttrInfo,
+                instruction.accept(clazz,
+                                   method,
+                                   codeAttribute,
                                    instructionOffset,
                                    processor);
             }
             catch (RuntimeException ex)
             {
-                System.err.println("Unexpected error while performing partial evaluation:");
-                System.err.println("  Class       = ["+classFile.getName()+"]");
-                System.err.println("  Method      = ["+methodInfo.getName(classFile)+methodInfo.getDescriptor(classFile)+"]");
+                System.err.println("Unexpected error while evaluating instruction:");
+                System.err.println("  Class       = ["+clazz.getName()+"]");
+                System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
                 System.err.println("  Instruction = "+instruction.toString(instructionOffset));
                 System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
 
@@ -670,13 +743,10 @@ implements   ExceptionInfoVisitor,
             // Collect the offsets of the instructions whose results were used.
             InstructionOffsetValue variablesTraceValue = variables.getCollectedProducerValue().instructionOffsetValue();
             InstructionOffsetValue stackTraceValue     = stack.getCollectedProducerValue().instructionOffsetValue();
-            InstructionOffsetValue unusedTraceValue    = unusedParameterCleaner.getTraceValue().instructionOffsetValue();
             varProducerValues[instructionOffset] =
                 varProducerValues[instructionOffset].generalize(variablesTraceValue).instructionOffsetValue();
             stackProducerValues[instructionOffset] =
                 stackProducerValues[instructionOffset].generalize(stackTraceValue).instructionOffsetValue();
-            unusedProducerValues[instructionOffset] =
-                unusedProducerValues[instructionOffset].generalize(unusedTraceValue).instructionOffsetValue();
             initializedVariables[instructionOffset] = variables.getInitializationIndex();
 
             // Collect the branch targets from the branch unit.
@@ -711,10 +781,6 @@ implements   ExceptionInfoVisitor,
                 {
                     System.out.println("     has up till now been using information from instructions setting stack: "+stackProducerValues[instructionOffset]);
                 }
-                if (unusedProducerValues[instructionOffset].instructionOffsetCount() > 0)
-                {
-                    System.out.println("     no longer needs information from instructions setting stack: "+unusedProducerValues[instructionOffset]);
-                }
                 if (branchTargetValues[instructionOffset] != null)
                 {
                     System.out.println("     has up till now been branching to "+branchTargetValues[instructionOffset]);
@@ -745,7 +811,7 @@ implements   ExceptionInfoVisitor,
             else
             {
                 // Merge in the current context.
-                variablesAfter[instructionOffset].generalize(variables);
+                variablesAfter[instructionOffset].generalize(variables, true);
                 stacksAfter[instructionOffset].generalize(stack);
             }
 
@@ -777,18 +843,15 @@ implements   ExceptionInfoVisitor,
                 // Are there multiple branch targets?
                 if (branchTargetCount > 1)
                 {
-                    // Handle them recursively and exit from this code block.
+                    // Push them on the execution stack and exit from this block.
                     for (int index = 0; index < branchTargetCount; index++)
                     {
                         if (DEBUG) System.out.println("Alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]");
 
-                        evaluateInstructionBlock(classFile,
-                                                 methodInfo,
-                                                 codeAttrInfo,
-                                                 new TracedVariables(variables),
-                                                 new TracedStack(stack),
-                                                 branchUnit,
-                                                 branchTargets.instructionOffset(index));
+                        pushInstructionBlock(new TracedVariables(variables),
+                                             new TracedStack(stack),
+                                             branchTargets.instructionOffset(index),
+                                             instructionBlockStack);
                     }
 
                     break;
@@ -799,9 +862,49 @@ implements   ExceptionInfoVisitor,
 
             // 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.
+            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);
+
+                if (DEBUG) System.out.println("Clearing context of subroutine from "+instructionOffset+" to "+subroutineEnd);
+
+                for (int offset = instructionOffset; offset < subroutineEnd; offset++)
+                {
+                    if (branchTargetFinder.isInstruction(offset))
+                    {
+                        evaluationCounts[offset] = 0;
+                    }
+                }
+
+                evaluateInstructionBlock(clazz,
+                                         method,
+                                         codeAttribute,
+                                         new TracedVariables(variables),
+                                         new TracedStack(stack),
+                                         instructionOffset);
+
+                if (DEBUG) System.out.println("Evaluating exceptions of subroutine from "+instructionOffset+" to "+subroutineEnd);
+
+                // Evaluate all relevant exception catch blocks once.
+                codeAttribute.exceptionsAccept(clazz,
+                                               method,
+                                               instructionOffset,
+                                               subroutineEnd,
+                                               this);
+
+                if (DEBUG) System.out.println("Ending subroutine from "+instructionOffset+" to "+subroutineEnd);
+
+                break;
+            }
         }
 
-        if (DEBUG) System.out.println("Ending processing of instruction block");
+        if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]");
     }
 
 
@@ -810,23 +913,20 @@ implements   ExceptionInfoVisitor,
     /**
      * Initializes the data structures for the variables, stack, etc.
      */
-    private void initializeVariables(CodeAttrInfo codeAttrInfo,
-                                     Variables    parameters)
+    private void initializeVariables(Clazz           clazz,
+                                     Method          method,
+                                     CodeAttribute   codeAttribute,
+                                     TracedVariables variables,
+                                     TracedStack     stack)
     {
-        int codeLength = codeAttrInfo.u4codeLength;
-
-        if (DEBUG)
-        {
-            System.out.println("  Max locals = "+codeAttrInfo.u2maxLocals);
-            System.out.println("  Max stack  = "+codeAttrInfo.u2maxStack);
-        }
+        int codeLength = codeAttribute.u4codeLength;
 
         // Create new arrays for storing information at each instruction offset.
         if (variablesAfter.length < codeLength)
         {
+            // Create new arrays.
             varProducerValues    = new InstructionOffsetValue[codeLength];
             stackProducerValues  = new InstructionOffsetValue[codeLength];
-            unusedProducerValues = new InstructionOffsetValue[codeLength];
             branchOriginValues   = new InstructionOffsetValue[codeLength];
             branchTargetValues   = new InstructionOffsetValue[codeLength];
             variablesBefore      = new TracedVariables[codeLength];
@@ -837,21 +937,21 @@ implements   ExceptionInfoVisitor,
             evaluationCounts     = new int[codeLength];
             initializedVariables = new int[codeLength];
 
+            // Reset the arrays.
             for (int index = 0; index < codeLength; index++)
             {
-                varProducerValues[index]    = InstructionOffsetValueFactory.create();
-                stackProducerValues[index]  = InstructionOffsetValueFactory.create();
-                unusedProducerValues[index] = InstructionOffsetValueFactory.create();
+                varProducerValues[index]    = InstructionOffsetValue.EMPTY_VALUE;
+                stackProducerValues[index]  = InstructionOffsetValue.EMPTY_VALUE;
                 initializedVariables[index] = NONE;
             }
         }
         else
         {
+            // Reset the arrays.
             for (int index = 0; index < codeLength; index++)
             {
-                varProducerValues[index]    = InstructionOffsetValueFactory.create();
-                stackProducerValues[index]  = InstructionOffsetValueFactory.create();
-                unusedProducerValues[index] = InstructionOffsetValueFactory.create();
+                varProducerValues[index]    = InstructionOffsetValue.EMPTY_VALUE;
+                stackProducerValues[index]  = InstructionOffsetValue.EMPTY_VALUE;
                 branchOriginValues[index]   = null;
                 branchTargetValues[index]   = null;
                 generalizedContexts[index]  = false;
@@ -860,60 +960,139 @@ implements   ExceptionInfoVisitor,
 
                 if (variablesBefore[index] != null)
                 {
-                    variablesBefore[index].reset(codeAttrInfo.u2maxLocals);
+                    variablesBefore[index].reset(codeAttribute.u2maxLocals);
                 }
 
                 if (stacksBefore[index] != null)
                 {
-                    stacksBefore[index].reset(codeAttrInfo.u2maxStack);
+                    stacksBefore[index].reset(codeAttribute.u2maxStack);
                 }
 
                 if (variablesAfter[index] != null)
                 {
-                    variablesAfter[index].reset(codeAttrInfo.u2maxLocals);
+                    variablesAfter[index].reset(codeAttribute.u2maxLocals);
                 }
 
                 if (stacksAfter[index] != null)
                 {
-                    stacksAfter[index].reset(codeAttrInfo.u2maxStack);
+                    stacksAfter[index].reset(codeAttribute.u2maxStack);
                 }
             }
         }
 
-        // Reuse the existing variables and stack objects, ensuring the right size.
-        variables.reset(codeAttrInfo.u2maxLocals);
-        stack.reset(codeAttrInfo.u2maxStack);
+        // Create the method parameters.
+        TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals);
+
+        // Remember this instruction's offset with any stored value.
+        Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY);
+        parameters.setProducerValue(storeValue);
+
+        // Reset the trace value.
+        InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
+        parameters.setCollectedProducerValue(traceValue);
+
+        // Initialize the method parameters.
+        invocationUnit.enterMethod(clazz, method, parameters);
+
+        if (DEBUG)
+        {
+            System.out.println("  Params: "+parameters);
+        }
 
         // Initialize the variables with the parameters.
         variables.initialize(parameters);
 
         // Set the store value of each parameter variable.
-        InstructionOffsetValue atMethodEntry = InstructionOffsetValueFactory.create(PartialEvaluator.AT_METHOD_ENTRY);
+        InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY);
 
         for (int index = 0; index < parameters.size(); index++)
         {
-            variables.setStoredTraceValue(index, atMethodEntry);
+            variables.setProducerValue(index, atMethodEntry);
         }
-
-        // Reset the return value.
-        branchUnit.setTraceReturnValue(null);
     }
 
 
     /**
      * Generalize the local variable frames of a block of instructions.
      */
-    private void generalizeVariables(int startOffset, int endOffset, TracedVariables generalizedVariables)
+    private void generalizeVariables(int             startOffset,
+                                     int             endOffset,
+                                     boolean         includeAfterLastInstruction,
+                                     TracedVariables generalizedVariables)
     {
+        boolean first     = true;
+        int     lastIndex = -1;
+
+        // Generalize the variables before each of the instructions in the block.
         for (int index = startOffset; index < endOffset; index++)
         {
             if (isTraced(index))
             {
-                // We can't use the return value, because local generalization
-                // can be different a couple of times, with the global
-                // generalization being the same.
-                generalizedVariables.generalize(variablesBefore[index]);
+                TracedVariables tracedVariables = variablesBefore[index];
+
+                if (first)
+                {
+                    // Initialize the variables with the first traced local
+                    // variable frame.
+                    generalizedVariables.initialize(tracedVariables);
+
+                    first = false;
+                }
+                else
+                {
+                    // Generalize the variables with the traced local variable
+                    // frame. We can't use the return value, because local
+                    // generalization can be different a couple of times,
+                    // with the global generalization being the same.
+                    generalizedVariables.generalize(tracedVariables, false);
+                }
+
+                lastIndex = index;
+            }
+        }
+
+        // Generalize the variables after the last instruction in the block,
+        // if required.
+        if (includeAfterLastInstruction &&
+            lastIndex >= 0)
+        {
+            TracedVariables tracedVariables = variablesAfter[lastIndex];
+
+            if (first)
+            {
+                // Initialize the variables with the local variable frame.
+                generalizedVariables.initialize(tracedVariables);
+            }
+            else
+            {
+                // Generalize the variables with the local variable frame.
+                generalizedVariables.generalize(tracedVariables, false);
             }
         }
+
+        // Just clear the variables if there aren't any traced instructions
+        // in the block.
+        if (first)
+        {
+            generalizedVariables.reset(generalizedVariables.size());
+        }
+    }
+
+
+    private static class MyInstructionBlock
+    {
+        private TracedVariables variables;
+        private TracedStack     stack;
+        private int             startOffset;
+
+
+        private MyInstructionBlock(TracedVariables variables,
+                                   TracedStack     stack,
+                                   int             startOffset)
+        {
+            this.variables   = variables;
+            this.stack       = stack;
+            this.startOffset = startOffset;
+        }
     }
 }
diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
new file mode 100644
index 0000000..4219d59
--- /dev/null
+++ b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
@@ -0,0 +1,164 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.RefConstant;
+import proguard.evaluation.BasicInvocationUnit;
+import proguard.evaluation.value.*;
+import proguard.optimize.info.*;
+
+/**
+ * This InvocationUbit stores parameter values and return values with the
+ * methods that are invoked.
+ *
+ * @see LoadingInvocationUnit
+ * @author Eric Lafortune
+ */
+public class StoringInvocationUnit
+extends      BasicInvocationUnit
+{
+    // Implementations for BasicInvocationUnit.
+
+    protected void setFieldClassValue(Clazz          clazz,
+                                      RefConstant    refConstant,
+                                      ReferenceValue value)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            generalizeFieldClassValue((Field)referencedMember, value);
+        }
+    }
+
+
+    protected void setFieldValue(Clazz       clazz,
+                                 RefConstant refConstant,
+                                 Value       value)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            generalizeFieldValue((Field)referencedMember, value);
+        }
+    }
+
+
+    protected void setMethodParameterValue(Clazz       clazz,
+                                           RefConstant refConstant,
+                                           int         parameterIndex,
+                                           Value       value)
+    {
+        Member referencedMember = refConstant.referencedMember;
+        if (referencedMember != null)
+        {
+            generalizeMethodParameterValue((Method)referencedMember,
+                                           parameterIndex,
+                                           value);
+        }
+    }
+
+
+    protected void setMethodReturnValue(Clazz  clazz,
+                                        Method method,
+                                        Value  value)
+    {
+        generalizeMethodReturnValue(method, value);
+    }
+
+
+    // Small utility methods.
+
+    private static void generalizeFieldClassValue(Field field, ReferenceValue value)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        if (info != null)
+        {
+            info.generalizeReferencedClass(value);
+        }
+    }
+
+
+    public static ReferenceValue getFieldClassValue(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        return info != null ?
+            info.getReferencedClass() :
+            null;
+    }
+
+
+    private static void generalizeFieldValue(Field field, Value value)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        if (info != null)
+        {
+            info.generalizeValue(value);
+        }
+    }
+
+
+    public static Value getFieldValue(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        return info != null ?
+            info.getValue() :
+            null;
+    }
+
+
+    private static void generalizeMethodParameterValue(Method method, int parameterIndex, Value value)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.generalizeParameter(parameterIndex, value);
+        }
+    }
+
+
+    public static Value getMethodParameterValue(Method method, int parameterIndex)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info != null ?
+            info.getParameter(parameterIndex) :
+            null;
+    }
+
+
+    private static void generalizeMethodReturnValue(Method method, Value value)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.generalizeReturnValue(value);
+        }
+    }
+
+
+    public static Value getMethodReturnValue(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info != null ?
+            info.getReturnValue() :
+            null;
+    }
+}
diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java
index 75a023b..118b30e 100644
--- a/src/proguard/optimize/evaluation/TracedBranchUnit.java
+++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java
@@ -1,6 +1,6 @@
-/* $Id: TracedBranchUnit.java,v 1.5.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,125 +20,40 @@
  */
 package proguard.optimize.evaluation;
 
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.optimize.evaluation.value.*;
+import proguard.classfile.Clazz;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.evaluation.BasicBranchUnit;
+import proguard.evaluation.value.Value;
 
 /**
  * This BranchUnit remembers the branch unit commands that are invoked on it.
  *
  * @author Eric Lafortune
  */
-class TracedBranchUnit implements BranchUnit
+class   TracedBranchUnit
+extends BasicBranchUnit
 {
-    private boolean                wasCalled;
-    private InstructionOffsetValue traceBranchTargets;
-    private Value                  traceReturnValue;
-
-
-    /**
-     * Resets the flag that tells whether any of the branch unit commands was
-     * called.
-     */
-    public void resetCalled()
-    {
-        wasCalled = false;
-    }
-
-    /**
-     * Returns whether any of the branch unit commands was called.
-     */
-    public boolean wasCalled()
-    {
-        return wasCalled;
-    }
-
-
-    /**
-     * Sets the initial branch targets, which will be updated as the branch
-     * methods of the branch unit are called.
-     */
-    public void setTraceBranchTargets(InstructionOffsetValue branchTargets)
-    {
-        this.traceBranchTargets = branchTargets;
-    }
-
-    public InstructionOffsetValue getTraceBranchTargets()
-    {
-        return traceBranchTargets;
-    }
-
-
-    /**
-     * Sets the initial return Value, which will be generalized as the
-     * return method of the branch unit is called. The initial value may be
-     * null.
-     */
-    public void setTraceReturnValue(Value traceReturnValue)
-    {
-        this.traceReturnValue = traceReturnValue;
-    }
-
-    public Value getTraceReturnValue()
-    {
-        return traceReturnValue;
-    }
-
-
     // Implementations for BranchUnit.
 
-    public void branch(ClassFile    classFile,
-                       CodeAttrInfo codeAttrInfo,
-                       int          offset,
-                       int          branchTarget)
-    {
-        branchConditionally(classFile,
-                            codeAttrInfo,
-                            offset,
-                            branchTarget,
-                            Value.ALWAYS);
-    }
-
-
-    public void branchConditionally(ClassFile    classFile,
-                                    CodeAttrInfo codeAttrInfo,
-                                    int          offset,
-                                    int          branchTarget,
-                                    int          conditional)
+    public void branchConditionally(Clazz         clazz,
+                                    CodeAttribute codeAttribute,
+                                    int           offset,
+                                    int           branchTarget,
+                                    int           conditional)
     {
-        // Mark possible branches at the offset and at the branch target.
-        if (conditional != Value.NEVER)
+        if      (conditional == Value.ALWAYS)
         {
-            InstructionOffsetValue branchTargetValue = InstructionOffsetValueFactory.create(branchTarget);
-
-            // Accumulate the branch targets for the evaluator.
-            traceBranchTargets = conditional == Value.ALWAYS ?
-                branchTargetValue :
-                traceBranchTargets.generalize(branchTargetValue).instructionOffsetValue();
+            // Always branch.
+            super.branch(clazz, codeAttribute, offset, branchTarget);
+        }
+        else if (conditional != Value.NEVER)
+        {
+            // Maybe branch.
+            super.branchConditionally(clazz, codeAttribute, offset, branchTarget, conditional);
+        }
+        else
+        {
+            super.setCalled();
         }
-
-        wasCalled = true;
-    }
-
-
-    public void returnFromMethod(Value returnValue)
-    {
-        traceReturnValue = traceReturnValue == null ?
-            returnValue :
-            traceReturnValue.generalize(returnValue);
-
-        // Stop processing this block.
-        traceBranchTargets = InstructionOffsetValueFactory.create();
-
-        wasCalled = true;
-    }
-
-
-    public void throwException()
-    {
-        // Stop processing this block.
-        traceBranchTargets = InstructionOffsetValueFactory.create();
-
-        wasCalled = true;
     }
 }
diff --git a/src/proguard/optimize/evaluation/TracedVariables.java b/src/proguard/optimize/evaluation/TracedVariables.java
deleted file mode 100644
index 0a42a3e..0000000
--- a/src/proguard/optimize/evaluation/TracedVariables.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/* $Id: TracedVariables.java,v 1.10.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation;
-
-import proguard.optimize.evaluation.value.*;
-
-/**
- * This Variables class saves additional information with variables, to keep track
- * of their origins.
- * <p>
- * The Variables class stores a given producer Value along with each Value it
- * stores. It then generalizes a given collected Value with the producer Value
- * of each Value it loads. The producer Value and the initial collected Value
- * can be set; the generalized collected Value can be retrieved.
- * <p>
- * In addition, an initialization index can be reset and retrieved, pointing
- * to the most recent variable that has been initialized by a store operation.
- *
- * @author Eric Lafortune
- */
-class TracedVariables extends Variables
-{
-    private Value     producerValue;
-    private Value     collectedProducerValue;
-    private Variables producerVariables;
-//  private Variables consumerVariables;
-    private int       initializationIndex;
-
-
-    public TracedVariables(int size)
-    {
-        super(size);
-
-        producerVariables = new Variables(size);
-    }
-
-
-    public TracedVariables(TracedVariables tracedVariables)
-    {
-        super(tracedVariables);
-
-        producerVariables = new Variables(tracedVariables.producerVariables);
-    }
-
-
-    /**
-     * Sets the Value that will be stored along with all store instructions.
-     */
-    public void setProducerValue(Value producerValue)
-    {
-        this.producerValue = producerValue;
-    }
-
-
-    /**
-     * Sets the initial Value with which all values stored along with load
-     * instructions will be generalized.
-     */
-    public void setCollectedProducerValue(Value collectedProducerValue)
-    {
-        this.collectedProducerValue = collectedProducerValue;
-    }
-
-    public Value getCollectedProducerValue()
-    {
-        return collectedProducerValue;
-    }
-
-
-    /**
-     * Resets the initialization index.
-     */
-    public void resetInitialization()
-    {
-        initializationIndex = -1;
-    }
-
-    public int getInitializationIndex()
-    {
-        return initializationIndex;
-    }
-
-
-    /**
-     * Gets the specified trace Value from the variables, without disturbing them.
-     * @param index the variable index.
-     * @return the trace value at the specified position.
-     */
-    public Value getStoredTraceValue(int index)
-    {
-        return producerVariables.load(index);
-    }
-
-
-    /**
-     * Gets the specified trace Value from the variables, without disturbing them.
-     * @param index the variable index.
-     * @param value the trace value to set.
-     */
-    public void setStoredTraceValue(int index, Value value)
-    {
-        producerVariables.store(index, value);
-    }
-
-
-    // Implementations for Variables.
-
-    public void reset(int size)
-    {
-        super.reset(size);
-
-        producerVariables.reset(size);
-    }
-
-    public void initialize(TracedVariables other)
-    {
-        super.initialize(other);
-
-        producerVariables.initialize(other.producerVariables);
-    }
-
-    public boolean generalize(TracedVariables other)
-    {
-        return
-            super.generalize(other) |
-            producerVariables.generalize(other.producerVariables);
-    }
-
-    public void store(int index, Value value)
-    {
-        // Is this store operation an initialization of the variable?
-        Value previousValue = super.load(index);
-        if (previousValue == null ||
-            previousValue.computationalType() != value.computationalType())
-        {
-            initializationIndex = index;
-        }
-
-        // Store the value itself in the variable.
-        super.store(index, value);
-
-        // Store the store value in its trace variable.
-        producerVariables.store(index, producerValue);
-    }
-
-    public Value load(int index)
-    {
-        // Load and accumulate the store value of the variable.
-        if (collectedProducerValue != null)
-        {
-            collectedProducerValue = collectedProducerValue.generalize(producerVariables.load(index));
-        }
-
-        // Return the value itself.
-        return super.load(index);
-    }
-
-
-    // Implementations for Object.
-
-    public boolean equals(Object object)
-    {
-        if (this.getClass() != object.getClass())
-        {
-            return false;
-        }
-
-        TracedVariables other = (TracedVariables)object;
-
-        return super.equals(object) && this.producerVariables.equals(other.producerVariables);
-    }
-
-
-    public int hashCode()
-    {
-        return super.hashCode() ^ producerVariables.hashCode();
-    }
-
-
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer();
-
-        for (int index = 0; index < this.size(); index++)
-        {
-            Value value       = this.values[index];
-            Value tracedValue = producerVariables.values[index];
-            buffer = buffer.append('[')
-                           .append(tracedValue == null ? "empty" : tracedValue.toString())
-                           .append('>')
-                           .append(value       == null ? "empty" : value.toString())
-                           .append(']');
-        }
-
-        return buffer.toString();
-    }
-}
diff --git a/src/proguard/optimize/evaluation/UnusedParameterCleaner.java b/src/proguard/optimize/evaluation/UnusedParameterCleaner.java
deleted file mode 100644
index 5abc04b..0000000
--- a/src/proguard/optimize/evaluation/UnusedParameterCleaner.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/* $Id: UnusedParameterCleaner.java,v 1.5.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.instruction.*;
-import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.optimize.*;
-import proguard.optimize.VariableUsageMarker;
-import proguard.optimize.evaluation.value.*;
-
-/**
- * This InstructionVisitor clears the trace values to unused parameters on
- * the stack, right before the method is being invoked.
- *
- * @see VariableUsageMarker
- * @author Eric Lafortune
- */
-public class UnusedParameterCleaner
-implements   InstructionVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor
-{
-    private static final boolean DEBUG = false;
-
-
-    private TracedStack tracedStack;
-    private Value       traceValue;
-
-
-    /**
-     * Creates a new UnusedParameterCleaner.
-     * @param tracedStack the stack on which trace values can be cleared.
-     */
-    public UnusedParameterCleaner(TracedStack tracedStack)
-    {
-        this.tracedStack = tracedStack;
-    }
-
-
-    /**
-     * Sets the initial Value with which all unused values will be generalized.
-     */
-    public void setTraceValue(Value traceValue)
-    {
-        this.traceValue = traceValue;
-    }
-
-    public Value getTraceValue()
-    {
-        return traceValue;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        // Fix the stack if this is a method invocation of which some
-        // parameters are marked as unused.
-        classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
-    {
-        interfaceMethodrefCpInfo.referencedMemberInfoAccept(this);
-    }
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        methodrefCpInfo.referencedMemberInfoAccept(this);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Get the used parameters,
-        long usedParameters = ParameterUsageMarker.getUsedVariables(programMethodInfo);
-
-        // Compute the number of parameters.
-        int parameterSize = ClassUtil.internalMethodParameterSize(programMethodInfo.getDescriptor(programClassFile));
-
-        if ((programMethodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
-        {
-            parameterSize++;
-        }
-
-        // Only consider the 64 first parameters at most.
-        if (parameterSize > 64)
-        {
-            parameterSize = 64;
-        }
-
-        // Loop over all parameters.
-        for (int index = 0; index < parameterSize; index++)
-        {
-            if ((usedParameters & (1L << index)) == 0)
-            {
-                int stackIndex = parameterSize - index - 1;
-
-                if (traceValue != null)
-                {
-                    traceValue = traceValue.generalize(tracedStack.getTopProducerValue(stackIndex));
-                }
-
-                tracedStack.setTopProducerValue(stackIndex,
-                                                InstructionOffsetValueFactory.create());
-                tracedStack.setTopConsumerValue(stackIndex,
-                                                InstructionOffsetValueFactory.create());
-
-                if (DEBUG)
-                {
-                    System.out.println("     clearing reference from unused parameter "+index);
-                    System.out.println("     Stack: "+tracedStack);
-                }
-            }
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-}
diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java
new file mode 100644
index 0000000..f42e695
--- /dev/null
+++ b/src/proguard/optimize/evaluation/VariableOptimizer.java
@@ -0,0 +1,222 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.VariableRemapper;
+import proguard.classfile.util.*;
+
+/**
+ * This AttributeVisitor optimizes variable allocation based on their the liveness,
+ * in the code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableOptimizer
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+    private static final int MAX_VARIABLES_SIZE = 64;
+
+
+    private final boolean reuseThis;
+
+    private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
+    private final VariableRemapper variableRemapper = new VariableRemapper();
+
+    private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE];
+
+
+    /**
+     * Creates a new VariableOptimizer.
+     * @param reuseThis specifies whether the 'this' variable can be reused.
+     */
+    public VariableOptimizer(boolean reuseThis)
+    {
+        this.reuseThis = reuseThis;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        // Initialize the global arrays.
+        initializeArrays(codeAttribute);
+
+        // Analyze the liveness of the variables in the code.
+        livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute);
+
+        int startIndex = 0;
+
+        int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
+        {
+            parameterSize++;
+
+            if (!reuseThis)
+            {
+                startIndex = 1;
+            }
+        }
+
+        int variableSize = codeAttribute.u2maxLocals;
+        int codeLength   = codeAttribute.u4codeLength;
+
+        boolean remapping = false;
+
+        // Loop over all variables.
+        for (int oldIndex = 0; oldIndex < variableSize; oldIndex++)
+        {
+            // By default, the variable will be mapped onto itself.
+            variableMap[oldIndex] = oldIndex;
+
+            // Only try remapping the variable if it's not a parameter.
+            if (oldIndex >= parameterSize &&
+                oldIndex < MAX_VARIABLES_SIZE)
+            {
+                // Try to remap the variable to a variable with a smaller index.
+                for (int newIndex = startIndex; newIndex < oldIndex; newIndex++)
+                {
+                    if (areNonOverlapping(oldIndex, newIndex, codeLength))
+                    {
+                        variableMap[oldIndex] = newIndex;
+
+                        updateLiveness(oldIndex, newIndex, codeLength);
+
+                        remapping = true;
+
+                        // This variable has been remapped. Go to the next one.
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Remap the variables.
+        if (remapping)
+        {
+            if (DEBUG)
+            {
+                System.out.println("Remapping variables:");
+                System.out.println("  Class "+ ClassUtil.externalClassName(clazz.getName()));
+                System.out.println("  Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
+                                                                                       0,
+                                                                                       method.getName(clazz),
+                                                                                       method.getDescriptor(clazz)));
+                for (int index = 0; index < variableSize; index++)
+                {
+                    System.out.println("  ["+index+"] -> ["+variableMap[index]+"]");
+                }
+            }
+
+            variableRemapper.setVariableMap(variableMap);
+            variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Initializes the global arrays.
+     */
+    private void initializeArrays(CodeAttribute codeAttribute)
+    {
+        int codeLength = codeAttribute.u4codeLength;
+
+        // Create new arrays for storing information at each instruction offset.
+        if (variableMap.length < codeLength)
+        {
+            variableMap = new int[codeLength];
+        }
+    }
+
+
+    /**
+     * Returns whether the given variables are never alive at the same time.
+     */
+    private boolean areNonOverlapping(int variableIndex1,
+                                      int variableIndex2,
+                                      int codeLength)
+    {
+        // Loop over all instructions.
+        for (int offset = 0; offset < codeLength; offset++)
+        {
+            if ((livenessAnalyzer.isAliveBefore(offset, variableIndex1) &&
+                 livenessAnalyzer.isAliveBefore(offset, variableIndex2)) ||
+
+                (livenessAnalyzer.isAliveAfter(offset, variableIndex1) &&
+                 livenessAnalyzer.isAliveAfter(offset, variableIndex2)) ||
+
+                // For now, exclude Category 2 variables.
+                livenessAnalyzer.isCategory2(offset, variableIndex1))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Updates the liveness resulting from mapping the given old variable on
+     * the given new variable.
+     */
+    private void updateLiveness(int oldVariableIndex,
+                                int newVariableIndex,
+                                int codeLength)
+    {
+        // Loop over all instructions.
+        for (int offset = 0; offset < codeLength; offset++)
+        {
+            // Update the liveness before the instruction.
+            if (livenessAnalyzer.isAliveBefore(offset, oldVariableIndex))
+            {
+                livenessAnalyzer.setAliveBefore(offset, oldVariableIndex, false);
+                livenessAnalyzer.setAliveBefore(offset, newVariableIndex, true);
+            }
+
+            // Update the liveness after the instruction.
+            if (livenessAnalyzer.isAliveAfter(offset, oldVariableIndex))
+            {
+                livenessAnalyzer.setAliveAfter(offset, oldVariableIndex, false);
+                livenessAnalyzer.setAliveAfter(offset, newVariableIndex, true);
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/evaluation/value/DoubleValueFactory.java b/src/proguard/optimize/evaluation/value/DoubleValueFactory.java
deleted file mode 100644
index 9ee80c3..0000000
--- a/src/proguard/optimize/evaluation/value/DoubleValueFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/* $Id: DoubleValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-/**
- * This class provides methods to create and reuse DoubleValue objects.
- *
- * @author Eric Lafortune
- */
-public class DoubleValueFactory
-{
-    // Shared copies of DoubleValue objects, to avoid creating a lot of objects.
-    private static DoubleValue         DOUBLE_VALUE   = new DoubleValue();
-    private static SpecificDoubleValue DOUBLE_VALUE_0 = new SpecificDoubleValue(0.0);
-    private static SpecificDoubleValue DOUBLE_VALUE_1 = new SpecificDoubleValue(1.0);
-
-
-    /**
-     * Creates a new DoubleValue with an undefined value.
-     */
-    public static DoubleValue create()
-    {
-        return DOUBLE_VALUE;
-    }
-
-    /**
-     * Creates a new DoubleValue with a given specific value.
-     */
-    public static SpecificDoubleValue create(double value)
-    {
-        return value == 0.0 ? DOUBLE_VALUE_0 :
-               value == 1.0 ? DOUBLE_VALUE_1 :
-                              new SpecificDoubleValue(value);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/FloatValueFactory.java b/src/proguard/optimize/evaluation/value/FloatValueFactory.java
deleted file mode 100644
index ade8e05..0000000
--- a/src/proguard/optimize/evaluation/value/FloatValueFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/* $Id: FloatValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-/**
- * This class provides methods to create and reuse FloatValue objects.
- *
- * @author Eric Lafortune
- */
-public class FloatValueFactory
-{
-    // Shared copies of FloatValue objects, to avoid creating a lot of objects.
-    private static FloatValue         FLOAT_VALUE   = new FloatValue();
-    private static SpecificFloatValue FLOAT_VALUE_0 = new SpecificFloatValue(0.0f);
-    private static SpecificFloatValue FLOAT_VALUE_1 = new SpecificFloatValue(1.0f);
-    private static SpecificFloatValue FLOAT_VALUE_2 = new SpecificFloatValue(2.0f);
-
-
-    /**
-     * Creates a new FloatValue with an undefined value.
-     */
-    public static FloatValue create()
-    {
-        return FLOAT_VALUE;
-    }
-
-    /**
-     * Creates a new FloatValue with a given specific value.
-     */
-    public static SpecificFloatValue create(float value)
-    {
-        return value == 0.0f ? FLOAT_VALUE_0 :
-               value == 1.0f ? FLOAT_VALUE_1 :
-               value == 2.0f ? FLOAT_VALUE_2 :
-                               new SpecificFloatValue(value);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java b/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java
deleted file mode 100644
index e006c06..0000000
--- a/src/proguard/optimize/evaluation/value/InstructionOffsetValueFactory.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/* $Id: InstructionOffsetValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-/**
- * This class provides methods to create and reuse InstructionOffsetValue objects.
- *
- * @author Eric Lafortune
- */
-public class InstructionOffsetValueFactory
-{
-    // Shared copies of InstructionOffsetValue objects, to avoid creating a lot of objects.
-    private static InstructionOffsetValue INSTRUCTION_OFFSET_VALUE = new InstructionOffsetValue();
-
-
-    /**
-     * Creates a new InstructionOffsetValue without a value.
-     */
-    public static InstructionOffsetValue create()
-    {
-        return INSTRUCTION_OFFSET_VALUE;
-    }
-
-
-    /**
-     * Creates a new InstructionOffsetValue with a given specific value.
-     */
-    public static InstructionOffsetValue create(int value)
-    {
-        return new InstructionOffsetValue(value);
-    }
-
-
-    /**
-     * Creates a new InstructionOffsetValue with a given list of possible values.
-     */
-    public static InstructionOffsetValue create(int[] values)
-    {
-        return new InstructionOffsetValue(values);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/IntegerValueFactory.java b/src/proguard/optimize/evaluation/value/IntegerValueFactory.java
deleted file mode 100644
index fbab3c3..0000000
--- a/src/proguard/optimize/evaluation/value/IntegerValueFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/* $Id: IntegerValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-/**
- * This class provides methods to create and reuse IntegerValue objects.
- *
- * @author Eric Lafortune
- */
-public class IntegerValueFactory
-{
-    // Shared copies of IntegerValue objects, to avoid creating a lot of objects.
-    private static IntegerValue         INTEGER_VALUE    = new IntegerValue();
-    private static SpecificIntegerValue INTEGER_VALUE_M1 = new SpecificIntegerValue(-1);
-    private static SpecificIntegerValue INTEGER_VALUE_0  = new SpecificIntegerValue(0);
-    private static SpecificIntegerValue INTEGER_VALUE_1  = new SpecificIntegerValue(1);
-    private static SpecificIntegerValue INTEGER_VALUE_2  = new SpecificIntegerValue(2);
-    private static SpecificIntegerValue INTEGER_VALUE_3  = new SpecificIntegerValue(3);
-    private static SpecificIntegerValue INTEGER_VALUE_4  = new SpecificIntegerValue(4);
-    private static SpecificIntegerValue INTEGER_VALUE_5  = new SpecificIntegerValue(5);
-
-    /**
-     * Creates a new IntegerValue with an undefined value.
-     */
-    public static IntegerValue create()
-    {
-        return INTEGER_VALUE;
-    }
-
-    /**
-     * Creates a new IntegerValue with a given specific value.
-     */
-    public static SpecificIntegerValue create(int value)
-    {
-        switch (value)
-        {
-            case -1: return INTEGER_VALUE_M1;
-            case  0: return INTEGER_VALUE_0;
-            case  1: return INTEGER_VALUE_1;
-            case  2: return INTEGER_VALUE_2;
-            case  3: return INTEGER_VALUE_3;
-            case  4: return INTEGER_VALUE_4;
-            case  5: return INTEGER_VALUE_5;
-            default: return new SpecificIntegerValue(value);
-        }
-    }
-
-}
diff --git a/src/proguard/optimize/evaluation/value/LongValueFactory.java b/src/proguard/optimize/evaluation/value/LongValueFactory.java
deleted file mode 100644
index 15414a5..0000000
--- a/src/proguard/optimize/evaluation/value/LongValueFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/* $Id: LongValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-/**
- * This class provides methods to create and reuse LongValue objects.
- *
- * @author Eric Lafortune
- */
-public class LongValueFactory
-{
-    // Shared copies of LongValue objects, to avoid creating a lot of objects.
-    private static LongValue         LONG_VALUE   = new LongValue();
-    private static SpecificLongValue LONG_VALUE_0 = new SpecificLongValue(0);
-    private static SpecificLongValue LONG_VALUE_1 = new SpecificLongValue(1);
-
-
-    /**
-     * Creates a new LongValue with an undefined value.
-     */
-    public static LongValue create()
-    {
-        return LONG_VALUE;
-    }
-
-    /**
-     * Creates a new LongValue with a given specific value.
-     */
-    public static SpecificLongValue create(long value)
-    {
-        return value == 0 ? LONG_VALUE_0 :
-               value == 1 ? LONG_VALUE_1 :
-                            new SpecificLongValue(value);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/ReferenceValue.java b/src/proguard/optimize/evaluation/value/ReferenceValue.java
deleted file mode 100644
index 4b8f853..0000000
--- a/src/proguard/optimize/evaluation/value/ReferenceValue.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/* $Id: ReferenceValue.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-import proguard.classfile.*;
-import proguard.classfile.ClassCpInfo;
-
-/**
- * This class represents a partially evaluated reference value.
- *
- * @author Eric Lafortune
- */
-public class ReferenceValue extends Category1Value
-{
-    protected boolean mayBeNull;
-
-
-    /**
-     * Creates a new reference value that may or may not be null.
-     */
-    public ReferenceValue(boolean mayBeNull)
-    {
-        this.mayBeNull = mayBeNull;
-    }
-
-
-    /**
-     * Returns the specific reference value, if applicable.
-     */
-    public ClassFile value()
-    {
-        return null;
-    }
-
-
-    /**
-     * Returns the array dimension, if applicable.
-     */
-    public int dimension()
-    {
-        return 0;
-    }
-
-
-    // Basic binary methods.
-
-    /**
-     * Returns the generalization of this ReferenceValue and the given other
-     * ReferenceValue.
-     */
-    public ReferenceValue generalize(ReferenceValue other)
-    {
-        return ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
-    }
-
-
-    /**
-     * Returns whether this ReferenceValue and the given ReferenceValue are equal:
-     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public int equal(ReferenceValue other)
-    {
-        return MAYBE;
-    }
-
-
-    // Basic unary methods.
-
-    /**
-     * Returns whether this ReferenceValue is <code>null</code>:
-     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public int isNull()
-    {
-        return mayBeNull ? MAYBE : NEVER;
-    }
-
-
-    /**
-     * Returns whether this ReferenceValue is an instance of the given class
-     * with the given dimensionality:
-     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public int instanceOf(ClassFile typeClassFile, int typeDimension)
-    {
-        return MAYBE;
-    }
-
-
-    // Derived binary methods.
-
-    /**
-     * Returns whether this ReferenceValue and the given ReferenceValue are different:
-     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public final int notEqual(ReferenceValue other)
-    {
-        return -equal(other);
-    }
-
-
-    /**
-     * Returns whether this ReferenceValue is not <code>null</code>:
-     * <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public final int isNotNull()
-    {
-        return -isNull();
-    }
-
-
-    // Similar binary methods, but this time with more specific arguments.
-
-    /**
-     * Returns the generalization of this ReferenceValue and the given other
-     * SpecificReferenceValue.
-     */
-    public ReferenceValue generalize(SpecificReferenceValue other)
-    {
-        return ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
-    }
-
-
-    /**
-     * Returns whether this ReferenceValue and the given SpecificReferenceValue are
-     * equal: <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
-     */
-    public int equal(SpecificReferenceValue other)
-    {
-        return MAYBE;
-    }
-
-
-    // Implementations for Value.
-
-    public final ReferenceValue referenceValue()
-    {
-        return this;
-    }
-
-    public final Value generalize(Value other)
-    {
-        return this.generalize(other.referenceValue());
-    }
-
-    public final int computationalType()
-    {
-        return TYPE_REFERENCE;
-    }
-
-
-    // Implementations for Object.
-
-    public boolean equals(Object object)
-    {
-        return object != null &&
-               this.getClass() == object.getClass() &&
-               this.mayBeNull  == ((ReferenceValue)object).mayBeNull;
-    }
-
-
-    public int hashCode()
-    {
-        return this.getClass().hashCode() ^ (mayBeNull ? 0 : 1);
-    }
-
-
-    public String toString()
-    {
-        return mayBeNull ? "a" : "a:!null";
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java b/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java
deleted file mode 100644
index c83e886..0000000
--- a/src/proguard/optimize/evaluation/value/ReferenceValueFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/* $Id: ReferenceValueFactory.java,v 1.5.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-import proguard.classfile.*;
-import proguard.classfile.ClassCpInfo;
-
-/**
- * This class provides methods to create and reuse ReferenceValue objects.
- *
- * @author Eric Lafortune
- */
-public class ReferenceValueFactory
-{
-    // Shared copies of ReferenceValue objects, to avoid creating a lot of objects.
-    private static ReferenceValue REFERENCE_VALUE_MAYBE_NULL = new ReferenceValue(true);
-    private static ReferenceValue REFERENCE_VALUE_NOT_NULL   = new ReferenceValue(false);
-    private static ReferenceValue REFERENCE_VALUE_NULL       = new SpecificReferenceValue(null, true);
-
-
-    /**
-     * Creates a new ReferenceValue with an undefined value.
-     */
-    public static ReferenceValue create(boolean mayBeNull)
-    {
-        return mayBeNull ? REFERENCE_VALUE_MAYBE_NULL :
-                           REFERENCE_VALUE_NOT_NULL;
-    }
-
-
-    /**
-     * Creates a new ReferenceValue that represents <code>null</code>.
-     */
-    public static ReferenceValue createNull()
-    {
-        return REFERENCE_VALUE_NULL;
-    }
-
-
-    /**
-     * Creates a new ReferenceValue of a specific type. If the value is
-     * <code>null</code>, a ReferenceValue of an undefined type is returned.
-     */
-    public static ReferenceValue create(ClassFile value, boolean mayBeNull)
-    {
-        return value == null ? ReferenceValueFactory.create(mayBeNull) :
-                               new SpecificReferenceValue(value, mayBeNull);
-    }
-
-
-    /**
-     * Creates a new array ReferenceValue of a specific type and dimensionality.
-     * If the value is <code>null</code>, a ReferenceValue of an undefined
-     * type is returned. If the dimension is 0, a ReferenceValue of the given
-     * type is returned.
-     */
-    public static ReferenceValue create(ClassFile value, int dimension, boolean mayBeNull)
-    {
-        return value == null  ? ReferenceValueFactory.create(mayBeNull)        :
-               dimension == 0 ? ReferenceValueFactory.create(value, mayBeNull) :
-                                new SpecificArrayReferenceValue(value, dimension, mayBeNull);
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java b/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java
deleted file mode 100644
index 1423686..0000000
--- a/src/proguard/optimize/evaluation/value/SpecificArrayReferenceValue.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/* $Id: SpecificArrayReferenceValue.java,v 1.5.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-import proguard.classfile.*;
-import proguard.classfile.ClassCpInfo;
-import proguard.classfile.util.ClassUtil;
-
-/**
- * This ReferenceValue represents an array reference value of a specific type
- * and dimensionality.
- *
- * @author Eric Lafortune
- */
-class SpecificArrayReferenceValue extends SpecificReferenceValue
-{
-    protected int dimension;
-
-
-    /**
-     * Creates a new specific array reference value. The dimensionality of the
-     * value argument alone is ignored.
-     */
-    public SpecificArrayReferenceValue(ClassFile value, int dimension, boolean mayBeNull)
-    {
-        super(value, mayBeNull);
-
-        this.dimension = dimension;
-    }
-
-
-    // Implementations for ReferenceValue.
-
-    public int dimension()
-    {
-        return dimension;
-    }
-
-
-    // Implementations of binary methods of ReferenceValue.
-
-    // Perhaps the other value arguments are more specific than apparent
-    // in these methods, so delegate to them.
-
-    public ReferenceValue generalize(ReferenceValue other)
-    {
-        return other.generalize(this);
-    }
-
-    public int equal(ReferenceValue other)
-    {
-        return other.equal(this);
-    }
-
-
-    // Implementations of unary methods of ReferenceValue.
-
-    public int instanceOf(ClassFile typeClassFile, int typeDimension)
-    {
-        // If this value is null, it is never an instance of any class.
-        if (value == null)
-        {
-            return NEVER;
-        }
-
-        // If the type class is unknown or the type has a higher dimension, we
-        // can't tell for sure.
-        if (typeClassFile == null ||
-            typeDimension > dimension)
-        {
-            return MAYBE;
-        }
-
-        // If the type dimension is less than the value's dimension, the type
-        // must be Object, Cloneable, or Serializable.
-        if (typeDimension < dimension)
-        {
-            String typeClassName = typeClassFile.getName();
-            return typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT)    ||
-                   typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE) ||
-                   typeClassName.equals(ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE) ?
-                       ALWAYS :
-                       NEVER;
-        }
-
-        // If the value extends the type, we're sure.
-        return value.extends_(typeClassFile) ||
-               value.implements_(typeClassFile) ?
-                   ALWAYS :
-                   MAYBE;
-    }
-
-
-    // Implementations of binary ReferenceValue methods with
-    // SpecificArrayReferenceValue arguments.
-
-    public ReferenceValue generalize(SpecificArrayReferenceValue other)
-    {
-        return this.value     == other.value &&
-               this.dimension == other.dimension ?
-            this.mayBeNull ? this : other :
-            ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
-    }
-
-    public int equal(SpecificArrayReferenceValue other)
-    {
-        return this.value  == null &&
-               other.value == null ? ALWAYS : MAYBE;
-    }
-
-
-    // Implementations for Object.
-
-    public boolean equals(Object object)
-    {
-        if (object == null ||
-            this.getClass() != object.getClass())
-        {
-            return false;
-        }
-
-        SpecificArrayReferenceValue other = (SpecificArrayReferenceValue)object;
-        return this.mayBeNull == other.mayBeNull                     &&
-               (this.value == null ? other.value == null :
-                                     this.value.equals(other.value)) &&
-               this.dimension == other.dimension;
-    }
-
-
-    public int hashCode()
-    {
-        return this.getClass().hashCode()             ^
-               (value == null ? 0 : value.hashCode()) ^
-               dimension;
-    }
-
-
-    public String toString()
-    {
-        return "a:" + (value == null ? "null" : value.getName()+"["+dimension+"]");
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java b/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java
deleted file mode 100644
index 3ac663a..0000000
--- a/src/proguard/optimize/evaluation/value/SpecificReferenceValue.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/* $Id: SpecificReferenceValue.java,v 1.5.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-import proguard.classfile.*;
-import proguard.classfile.ClassCpInfo;
-
-/**
- * This ReferenceValue represents a reference value of a specific type.
- *
- * @author Eric Lafortune
- */
-class SpecificReferenceValue extends ReferenceValue
-{
-    protected ClassFile value;
-
-
-    /**
-     * Creates a new specific reference value.
-     */
-    public SpecificReferenceValue(ClassFile value, boolean mayBeNull)
-    {
-        super(mayBeNull);
-
-        this.value = value;
-    }
-
-
-    // Implementations for ReferenceValue.
-
-    public ClassFile value()
-    {
-        return value;
-    }
-
-
-    // Implementations of binary methods of ReferenceValue.
-
-    // Perhaps the other value arguments are more specific than apparent
-    // in these methods, so delegate to them.
-
-    public ReferenceValue generalize(ReferenceValue other)
-    {
-        return other.generalize(this);
-    }
-
-    public int equal(ReferenceValue other)
-    {
-        return other.equal(this);
-    }
-
-
-    // Implementations of unary methods of ReferenceValue.
-
-    public int isNull()
-    {
-        return value == null ? ALWAYS :
-               mayBeNull     ? MAYBE  :
-                               NEVER;
-    }
-
-
-    public int instanceOf(ClassFile typeClassFile, int typeDimension)
-    {
-        // If this value is null, it is never an instance of any class.
-        if (value == null)
-        {
-            return NEVER;
-        }
-
-        // If the type class is unknown or the type is an array type, we can't
-        // tell for sure.
-        if (typeClassFile == null ||
-            typeDimension > 0)
-        {
-            return MAYBE;
-        }
-
-        // If the value extends the type, we're sure.
-        return value.extends_(typeClassFile) ||
-               value.implements_(typeClassFile) ?
-                   ALWAYS :
-                   MAYBE;
-    }
-
-
-    // Implementations of binary ReferenceValue methods with SpecificReferenceValue
-    // arguments.
-
-    public ReferenceValue generalize(SpecificReferenceValue other)
-    {
-        return this.value == other.value ?
-            this.mayBeNull ? this : other :
-            ReferenceValueFactory.create(this.mayBeNull || other.mayBeNull);
-    }
-
-    public int equal(SpecificReferenceValue other)
-    {
-        return this.value  == null &&
-               other.value == null ? ALWAYS : MAYBE;
-    }
-
-
-    // Implementations for Value.
-
-    public boolean isSpecific()
-    {
-        return true;
-    }
-
-
-    // Implementations for Object.
-
-    public boolean equals(Object object)
-    {
-        if (object == null ||
-            this.getClass() != object.getClass())
-        {
-            return false;
-        }
-
-        SpecificReferenceValue other = (SpecificReferenceValue)object;
-        return this.mayBeNull == other.mayBeNull &&
-               (this.value == null ? other.value == null :
-                                     this.value.equals(other.value));
-    }
-
-
-    public int hashCode()
-    {
-        return this.getClass().hashCode() ^
-               (value == null ? 0 : value.hashCode());
-    }
-
-
-    public String toString()
-    {
-        return "a:" + (value == null ? "null" : value.getName());
-    }
-}
diff --git a/src/proguard/optimize/evaluation/value/ValueFactory.java b/src/proguard/optimize/evaluation/value/ValueFactory.java
deleted file mode 100644
index 2dd4460..0000000
--- a/src/proguard/optimize/evaluation/value/ValueFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/* $Id: ValueFactory.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.evaluation.value;
-
-import proguard.classfile.ClassConstants;
-
-/**
- * This class provides methods to create and reuse Value objects.
- *
- * @author Eric Lafortune
- */
-public class ValueFactory
-{
-    /**
-     * Creates a new Value with an undefined value, corresponding to the given
-     * internal type. The void type returns <code>null</code>.
-     */
-    public static Value create(String internalType)
-    {
-        switch (internalType.charAt(0))
-        {
-            case ClassConstants.INTERNAL_TYPE_VOID:
-                return null;
-
-            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
-            case ClassConstants.INTERNAL_TYPE_BYTE:
-            case ClassConstants.INTERNAL_TYPE_CHAR:
-            case ClassConstants.INTERNAL_TYPE_SHORT:
-            case ClassConstants.INTERNAL_TYPE_INT:
-                return IntegerValueFactory.create();
-
-            case ClassConstants.INTERNAL_TYPE_FLOAT:
-                return FloatValueFactory.create();
-
-            case ClassConstants.INTERNAL_TYPE_LONG:
-                return LongValueFactory.create();
-
-            case ClassConstants.INTERNAL_TYPE_DOUBLE:
-                return DoubleValueFactory.create();
-
-            default:
-                return ReferenceValueFactory.create(true);
-        }
-    }
-}
diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/src/proguard/optimize/info/AccessMethodMarker.java
new file mode 100644
index 0000000..a3dd89d
--- /dev/null
+++ b/src/proguard/optimize/info/AccessMethodMarker.java
@@ -0,0 +1,187 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This InstructionVisitor marks the types of class accesses and class member
+ * accesses of the methods whose instructions it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class AccessMethodMarker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             ClassVisitor,
+             MemberVisitor
+{
+    private Method invokingMethod;
+
+
+    // 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)
+    {
+        invokingMethod = method;
+
+        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Check the referenced class or class member, if any.
+       stringConstant.referencedClassAccept(this);
+       stringConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        // Check the referenced class.
+        clazz.constantPoolEntryAccept(refConstant.u2classIndex, this);
+
+        // Check the referenced class member itself.
+        refConstant.referencedClassAccept(this);
+        refConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        // Check the referenced class.
+       classConstant.referencedClassAccept(this);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitAnyClass(Clazz clazz)
+    {
+        int accessFlags = clazz.getAccessFlags();
+
+        if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) == 0)
+        {
+            setAccessesPackageCode(invokingMethod);
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitAnyMember(Clazz clazz, Member member)
+    {
+        int accessFlags = member.getAccessFlags();
+
+        if      ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE)   != 0)
+        {
+            setAccessesPrivateCode(invokingMethod);
+        }
+        else if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0)
+        {
+            setAccessesProtectedCode(invokingMethod);
+        }
+        else if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC)    == 0)
+        {
+            setAccessesPackageCode(invokingMethod);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private static void setAccessesPrivateCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setAccessesPrivateCode();
+        }
+    }
+
+
+    /**
+     * Returns whether the given method accesses private class members.
+     */
+    public static boolean accessesPrivateCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null || info.accessesPrivateCode();
+    }
+
+
+    private static void setAccessesPackageCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setAccessesPackageCode();
+        }
+    }
+
+
+    /**
+     * Returns whether the given method accesses package visible classes or class
+     * members.
+     */
+    public static boolean accessesPackageCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null || info.accessesPackageCode();
+    }
+
+
+    private static void setAccessesProtectedCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setAccessesProtectedCode();
+        }
+    }
+
+
+    /**
+     * Returns whether the given method accesses protected class members.
+     */
+    public static boolean accessesProtectedCode(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null || info.accessesProtectedCode();
+    }
+}
diff --git a/src/proguard/optimize/info/BackwardBranchMarker.java b/src/proguard/optimize/info/BackwardBranchMarker.java
new file mode 100644
index 0000000..f96281b
--- /dev/null
+++ b/src/proguard/optimize/info/BackwardBranchMarker.java
@@ -0,0 +1,90 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This InstructionVisitor marks all methods that branch backward in any of the
+ * instructions that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class BackwardBranchMarker
+extends      SimplifiedVisitor
+implements   InstructionVisitor
+{
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        markBackwardBranch(method, branchInstruction.branchOffset);
+    }
+
+
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        markBackwardBranch(method, switchInstruction.defaultOffset);
+
+        for (int index = 0; index < switchInstruction.jumpOffsets.length; index++)
+        {
+            markBackwardBranch(method, switchInstruction.jumpOffsets[index]);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the given method if the given branch offset is negative.
+     */
+    private void markBackwardBranch(Method method, int branchOffset)
+    {
+        if (branchOffset < 0)
+        {
+            setBranchesBackward(method);
+        }
+    }
+
+
+    private static void setBranchesBackward(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setBranchesBackward();
+        }
+    }
+
+
+    public static boolean branchesBackward(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null || info.branchesBackward();
+    }
+}
diff --git a/src/proguard/optimize/info/CatchExceptionMarker.java b/src/proguard/optimize/info/CatchExceptionMarker.java
new file mode 100644
index 0000000..c747201
--- /dev/null
+++ b/src/proguard/optimize/info/CatchExceptionMarker.java
@@ -0,0 +1,69 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor marks all methods that catch exceptions.
+ *
+ * @author Eric Lafortune
+ */
+public class CatchExceptionMarker
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (codeAttribute.u2exceptionTableLength > 0)
+        {
+            markCatchException(method);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private static void markCatchException(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setCatchesExceptions();
+        }
+    }
+
+
+    public static boolean catchesExceptions(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null ||
+               info.catchesExceptions();
+    }
+}
diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/src/proguard/optimize/info/ClassOptimizationInfo.java
new file mode 100644
index 0000000..115266d
--- /dev/null
+++ b/src/proguard/optimize/info/ClassOptimizationInfo.java
@@ -0,0 +1,88 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.Clazz;
+
+/**
+ * This class stores some optimization information that can be attached to
+ * a class.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassOptimizationInfo
+{
+    private boolean isInstantiated = false;
+    private boolean isInstanceofed = false;
+    private boolean isDotClassed   = false;
+
+
+    public void setInstantiated()
+    {
+        isInstantiated = true;
+    }
+
+
+    public boolean isInstantiated()
+    {
+        return isInstantiated;
+    }
+
+
+    public void setInstanceofed()
+    {
+        isInstanceofed = true;
+    }
+
+
+    public boolean isInstanceofed()
+    {
+        return isInstanceofed;
+    }
+
+
+    public void setDotClassed()
+    {
+        isDotClassed = true;
+    }
+
+
+    public boolean isDotClassed()
+    {
+        return isDotClassed;
+    }
+
+
+    public static void setClassOptimizationInfo(Clazz clazz)
+    {
+        clazz.setVisitorInfo(new ClassOptimizationInfo());
+    }
+
+
+    public static ClassOptimizationInfo getClazzOptimizationInfo(Clazz clazz)
+    {
+        Object visitorInfo = clazz.getVisitorInfo();
+
+        return visitorInfo instanceof ClassOptimizationInfo ?
+            (ClassOptimizationInfo)visitorInfo :
+            null;
+    }
+}
diff --git a/src/proguard/optimize/info/ExceptionInstructionChecker.java b/src/proguard/optimize/info/ExceptionInstructionChecker.java
new file mode 100644
index 0000000..6b49796
--- /dev/null
+++ b/src/proguard/optimize/info/ExceptionInstructionChecker.java
@@ -0,0 +1,184 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.RefConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This class can tell whether an instruction might throw exceptions.
+ *
+ * @author Eric Lafortune
+ */
+public class ExceptionInstructionChecker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    // A return value for the visitor methods.
+    private boolean mayThrowExceptions;
+
+
+    /**
+     * Returns whether the given instruction may throw exceptions.
+     */
+    public boolean mayThrowExceptions(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        mayThrowExceptions = false;
+
+        instruction.accept(clazz, method,  codeAttribute, offset, this);
+
+        return mayThrowExceptions;
+    }
+
+
+    // 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)
+    {
+        byte opcode = simpleInstruction.opcode;
+
+        // Check for instructions that may throw exceptions.
+        if (opcode == InstructionConstants.OP_IDIV         ||
+            opcode == InstructionConstants.OP_LDIV         ||
+            opcode == InstructionConstants.OP_IREM         ||
+            opcode == InstructionConstants.OP_LREM         ||
+            opcode == InstructionConstants.OP_IALOAD       ||
+            opcode == InstructionConstants.OP_LALOAD       ||
+            opcode == InstructionConstants.OP_FALOAD       ||
+            opcode == InstructionConstants.OP_DALOAD       ||
+            opcode == InstructionConstants.OP_AALOAD       ||
+            opcode == InstructionConstants.OP_BALOAD       ||
+            opcode == InstructionConstants.OP_CALOAD       ||
+            opcode == InstructionConstants.OP_SALOAD       ||
+            opcode == InstructionConstants.OP_IASTORE      ||
+            opcode == InstructionConstants.OP_LASTORE      ||
+            opcode == InstructionConstants.OP_FASTORE      ||
+            opcode == InstructionConstants.OP_DASTORE      ||
+            opcode == InstructionConstants.OP_AASTORE      ||
+            opcode == InstructionConstants.OP_BASTORE      ||
+            opcode == InstructionConstants.OP_CASTORE      ||
+            opcode == InstructionConstants.OP_SASTORE      ||
+            opcode == InstructionConstants.OP_NEWARRAY     ||
+            opcode == InstructionConstants.OP_ARRAYLENGTH  ||
+            opcode == InstructionConstants.OP_ATHROW       ||
+            opcode == InstructionConstants.OP_MONITORENTER ||
+            opcode == InstructionConstants.OP_MONITOREXIT)
+        {
+            // These instructions may throw exceptions.
+            mayThrowExceptions = true;
+        }
+
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        byte opcode = constantInstruction.opcode;
+
+        // Check for instructions that may throw exceptions.
+        if (opcode == InstructionConstants.OP_GETSTATIC       ||
+            opcode == InstructionConstants.OP_PUTSTATIC       ||
+            opcode == InstructionConstants.OP_GETFIELD        ||
+            opcode == InstructionConstants.OP_PUTFIELD        ||
+            opcode == InstructionConstants.OP_INVOKEVIRTUAL   ||
+            opcode == InstructionConstants.OP_INVOKESPECIAL   ||
+            opcode == InstructionConstants.OP_INVOKESTATIC    ||
+            opcode == InstructionConstants.OP_INVOKEINTERFACE ||
+            opcode == InstructionConstants.OP_NEW             ||
+            opcode == InstructionConstants.OP_ANEWARRAY       ||
+            opcode == InstructionConstants.OP_CHECKCAST       ||
+            opcode == InstructionConstants.OP_MULTIANEWARRAY)
+        {
+            // These instructions may throw exceptions.
+            mayThrowExceptions = true;
+        }
+//        else
+//        if (opcode == InstructionConstants.OP_INVOKEVIRTUAL   ||
+//            opcode == InstructionConstants.OP_INVOKESPECIAL   ||
+//            opcode == InstructionConstants.OP_INVOKESTATIC    ||
+//            opcode == InstructionConstants.OP_INVOKEINTERFACE)
+//        {
+//            // Check if the invoking the method may throw an exception.
+//            clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+//        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        Member referencedMember = refConstant.referencedMember;
+
+        // Do we have a reference to the method?
+        if (referencedMember == null)
+        {
+            // We'll have to assume invoking the unknown method may throw an
+            // an exception.
+            mayThrowExceptions = true;
+        }
+        else
+        {
+            // First check the referenced method itself.
+            refConstant.referencedMemberAccept(this);
+
+            // If the result isn't conclusive, check down the hierarchy.
+            if (!mayThrowExceptions)
+            {
+                Clazz  referencedClass  = refConstant.referencedClass;
+                Method referencedMethod = (Method)referencedMember;
+
+                // Check all other implementations of the method in the class
+                // hierarchy.
+                referencedClass.methodImplementationsAccept(referencedMethod,
+                                                            false,
+                                                            this);
+            }
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+//        mayThrowExceptions = mayThrowExceptions ||
+//                             ExceptionMethodMarker.mayThrowExceptions(programMethod);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+//        mayThrowExceptions = mayThrowExceptions ||
+//                             !NoExceptionMethodMarker.doesntThrowExceptions(libraryMethod);
+    }
+}
diff --git a/src/proguard/optimize/info/FieldOptimizationInfo.java b/src/proguard/optimize/info/FieldOptimizationInfo.java
new file mode 100644
index 0000000..f5f0e84
--- /dev/null
+++ b/src/proguard/optimize/info/FieldOptimizationInfo.java
@@ -0,0 +1,162 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.util.MethodLinker;
+import proguard.evaluation.value.*;
+
+/**
+ * This class stores some optimization information that can be attached to
+ * a field.
+ *
+ * @author Eric Lafortune
+ */
+public class FieldOptimizationInfo
+{
+    private static final SpecificValueFactory VALUE_FACTORY = new SpecificValueFactory();
+
+    private boolean        isWritten;
+    private boolean        isRead;
+    private boolean        canBeMadePrivate = true;
+    private ReferenceValue referencedClass;
+    private Value          value;
+
+
+    public FieldOptimizationInfo(Clazz clazz, Field field)
+    {
+        isWritten =
+        isRead    = (field.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0;
+        value     = initialValue(field.getDescriptor(clazz));
+    }
+
+
+    public void setWritten()
+    {
+        isWritten = true;
+    }
+
+
+    public boolean isWritten()
+    {
+        return isWritten;
+    }
+
+
+    public void setRead()
+    {
+        isRead = true;
+    }
+
+
+    public boolean isRead()
+    {
+        return isRead;
+    }
+
+
+    public void setCanNotBeMadePrivate()
+    {
+        canBeMadePrivate = false;
+    }
+
+
+    public boolean canBeMadePrivate()
+    {
+        return canBeMadePrivate;
+    }
+
+
+    public void generalizeReferencedClass(ReferenceValue referencedClass)
+    {
+        this.referencedClass = this.referencedClass != null ?
+            this.referencedClass.generalize(referencedClass) :
+            referencedClass;
+    }
+
+
+    public ReferenceValue getReferencedClass()
+    {
+        return referencedClass;
+    }
+
+
+    public void generalizeValue(Value value)
+    {
+        this.value = this.value != null ?
+            this.value.generalize(value) :
+            value;
+    }
+
+
+    public Value getValue()
+    {
+        return value;
+    }
+
+
+    // Small utility methods.
+
+    private Value initialValue(String type)
+    {
+        switch (type.charAt(0))
+        {
+            case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+            case ClassConstants.INTERNAL_TYPE_BYTE:
+            case ClassConstants.INTERNAL_TYPE_CHAR:
+            case ClassConstants.INTERNAL_TYPE_SHORT:
+            case ClassConstants.INTERNAL_TYPE_INT:
+                return VALUE_FACTORY.createIntegerValue(0);
+
+            case ClassConstants.INTERNAL_TYPE_LONG:
+                return VALUE_FACTORY.createLongValue(0L);
+
+            case ClassConstants.INTERNAL_TYPE_FLOAT:
+                return VALUE_FACTORY.createFloatValue(0.0f);
+
+            case ClassConstants.INTERNAL_TYPE_DOUBLE:
+                return VALUE_FACTORY.createDoubleValue(0.0);
+
+            case ClassConstants.INTERNAL_TYPE_CLASS_START:
+            case ClassConstants.INTERNAL_TYPE_ARRAY:
+                return VALUE_FACTORY.createReferenceValueNull();
+
+            default:
+                throw new IllegalArgumentException("Invalid type ["+type+"]");
+        }
+    }
+
+
+    public static void setFieldOptimizationInfo(Clazz clazz, Field field)
+    {
+        MethodLinker.lastMember(field).setVisitorInfo(new FieldOptimizationInfo(clazz, field));
+    }
+
+
+    public static FieldOptimizationInfo getFieldOptimizationInfo(Field field)
+    {
+        Object visitorInfo = MethodLinker.lastMember(field).getVisitorInfo();
+
+        return visitorInfo instanceof FieldOptimizationInfo ?
+            (FieldOptimizationInfo)visitorInfo :
+            null;
+    }
+}
diff --git a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java
new file mode 100644
index 0000000..d9c9772
--- /dev/null
+++ b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java
@@ -0,0 +1,59 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.KeepMarker;
+
+/**
+ * This MemberVisitor attaches a FieldOptimizationInfo instance to every field
+ * and a MethodOptimizationInfo instance to every method that is not being kept
+ * that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class MemberOptimizationInfoSetter
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (!KeepMarker.isKept(programMethod))
+        {
+            MethodOptimizationInfo.setMethodOptimizationInfo(programClass,
+                                                             programMethod);
+        }
+    }
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (!KeepMarker.isKept(programField))
+        {
+            FieldOptimizationInfo.setFieldOptimizationInfo(programClass,
+                                                           programField);
+        }
+    }
+}
diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/src/proguard/optimize/info/MethodInvocationMarker.java
new file mode 100644
index 0000000..a5151c8
--- /dev/null
+++ b/src/proguard/optimize/info/MethodInvocationMarker.java
@@ -0,0 +1,107 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This InstructionVisitor counts the number of times methods are invoked from
+ * the instructions that are visited.
+ *
+ * @author Eric Lafortune
+ */
+public class MethodInvocationMarker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    // 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)
+    {
+        clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Mark the referenced method, if any.
+        stringConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        // Mark the referenced method.
+        refConstant.referencedMemberAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitAnyMember(Clazz Clazz, Member member) {}
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        incrementInvocationCount(programMethod);
+    }
+
+
+    // Small utility methods.
+
+    private static void incrementInvocationCount(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.incrementInvocationCount();
+        }
+    }
+
+
+    /**
+     * Returns the number of times the given method was invoked by the
+     * instructions that were visited.
+     */
+    public static int getInvocationCount(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info != null ? info.getInvocationCount() :
+                              Integer.MAX_VALUE;
+    }
+}
diff --git a/src/proguard/optimize/info/MethodOptimizationInfo.java b/src/proguard/optimize/info/MethodOptimizationInfo.java
new file mode 100644
index 0000000..e8af8b3
--- /dev/null
+++ b/src/proguard/optimize/info/MethodOptimizationInfo.java
@@ -0,0 +1,273 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.evaluation.value.Value;
+
+/**
+ * This class stores some optimization information that can be attached to
+ * a method.
+ *
+ * @author Eric Lafortune
+ */
+public class MethodOptimizationInfo
+{
+    private boolean hasNoSideEffects      = false;
+    private boolean hasSideEffects        = false;
+    private boolean canBeMadePrivate      = true;
+    private boolean catchesExceptions     = false;
+    private boolean branchesBackward      = false;
+    private boolean invokesSuperMethods   = false;
+    private boolean accessesPrivateCode   = false;
+    private boolean accessesPackageCode   = false;
+    private boolean accessesProtectedCode = false;
+    private int     invocationCount       = 0;
+    private int     parameterSize         = 0;
+    private long    usedParameters        = 0L;
+    private Value[] parameters;
+    private Value   returnValue;
+
+
+    /**
+     * Creates a new MethodOptimizationInfo for the given method.
+     */
+    public MethodOptimizationInfo(Clazz clazz, Method method)
+    {
+        // Set up an array of the right size for storing information about the
+        // passed parameters.
+        int parameterCount =
+            ClassUtil.internalMethodParameterCount(method.getDescriptor(clazz));
+
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
+        {
+            parameterCount++;
+        }
+
+        if (parameterCount > 0)
+        {
+            parameters = new Value[parameterCount];
+        }
+    }
+
+
+    public void setNoSideEffects()
+    {
+        hasNoSideEffects = true;
+    }
+
+
+    public boolean hasNoSideEffects()
+    {
+        return hasNoSideEffects;
+    }
+
+
+    public void setSideEffects()
+    {
+        hasSideEffects = true;
+    }
+
+
+    public boolean hasSideEffects()
+    {
+        return hasSideEffects;
+    }
+
+
+    public void setCanNotBeMadePrivate()
+    {
+        canBeMadePrivate = false;
+    }
+
+
+    public boolean canBeMadePrivate()
+    {
+        return canBeMadePrivate;
+    }
+
+
+    public void setCatchesExceptions()
+    {
+        catchesExceptions = true;
+    }
+
+
+    public boolean catchesExceptions()
+    {
+        return catchesExceptions;
+    }
+
+
+    public void setBranchesBackward()
+    {
+        branchesBackward = true;
+    }
+
+
+    public boolean branchesBackward()
+    {
+        return branchesBackward;
+    }
+
+
+    public void setInvokesSuperMethods()
+    {
+        invokesSuperMethods = true;
+    }
+
+
+    public boolean invokesSuperMethods()
+    {
+        return invokesSuperMethods;
+    }
+
+
+    public void setAccessesPrivateCode()
+    {
+        accessesPrivateCode = true;
+    }
+
+
+    public boolean accessesPrivateCode()
+    {
+        return accessesPrivateCode;
+    }
+
+
+    public void setAccessesPackageCode()
+    {
+        accessesPackageCode = true;
+    }
+
+
+    public boolean accessesPackageCode()
+    {
+        return accessesPackageCode;
+    }
+
+
+    public void setAccessesProtectedCode()
+    {
+        accessesProtectedCode = true;
+    }
+
+
+    public boolean accessesProtectedCode()
+    {
+        return accessesProtectedCode;
+    }
+
+
+    public void incrementInvocationCount()
+    {
+        invocationCount++;
+    }
+
+
+    public int getInvocationCount()
+    {
+        return invocationCount;
+    }
+
+
+    public void setParameterSize(int parameterSize)
+    {
+        this.parameterSize = parameterSize;
+    }
+
+
+    public int getParameterSize()
+    {
+        return parameterSize;
+    }
+
+
+    public void setParameterUsed(int parameterIndex)
+    {
+        usedParameters |= 1 << parameterIndex;
+    }
+
+
+    public void setUsedParameters(long usedParameters)
+    {
+        this.usedParameters = usedParameters;
+    }
+
+
+    public boolean isParameterUsed(int parameterIndex)
+    {
+        return parameterIndex >= 64 || (usedParameters & (1 << parameterIndex)) != 0;
+    }
+
+
+    public long getUsedParameters()
+    {
+        return usedParameters;
+    }
+
+
+    public void generalizeParameter(int parameterIndex, Value parameter)
+    {
+        parameters[parameterIndex] = parameters[parameterIndex] != null ?
+            parameters[parameterIndex].generalize(parameter) :
+            parameter;
+    }
+
+
+    public Value getParameter(int parameterIndex)
+    {
+        return parameters != null ?
+            parameters[parameterIndex] :
+            null;
+    }
+
+
+    public void generalizeReturnValue(Value returnValue)
+    {
+        this.returnValue = this.returnValue != null ?
+            this.returnValue.generalize(returnValue) :
+            returnValue;
+    }
+
+
+    public Value getReturnValue()
+    {
+        return returnValue;
+    }
+
+
+    public static void setMethodOptimizationInfo(Clazz clazz, Method method)
+    {
+        MethodLinker.lastMember(method).setVisitorInfo(new MethodOptimizationInfo(clazz, method));
+    }
+
+
+    public static MethodOptimizationInfo getMethodOptimizationInfo(Method method)
+    {
+        Object visitorInfo = MethodLinker.lastMember(method).getVisitorInfo();
+
+        return visitorInfo instanceof MethodOptimizationInfo ?
+            (MethodOptimizationInfo)visitorInfo :
+            null;
+    }
+}
diff --git a/src/proguard/optimize/NoSideEffectMethodMarker.java b/src/proguard/optimize/info/NoSideEffectMethodMarker.java
similarity index 54%
rename from src/proguard/optimize/NoSideEffectMethodMarker.java
rename to src/proguard/optimize/info/NoSideEffectMethodMarker.java
index 03df23b..48b00fc 100644
--- a/src/proguard/optimize/NoSideEffectMethodMarker.java
+++ b/src/proguard/optimize/info/NoSideEffectMethodMarker.java
@@ -1,6 +1,6 @@
-/* $Id: NoSideEffectMethodMarker.java,v 1.5.2.4 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,14 +18,14 @@
  * 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;
+package proguard.optimize.info;
 
 import proguard.classfile.*;
-import proguard.classfile.util.MethodInfoLinker;
-import proguard.classfile.visitor.MemberInfoVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
 
 /**
- * This MemberInfoVisitor marks all methods that it visits as not having any side
+ * This MemberVisitor marks all methods that it visits as not having any side
  * effects. It will make the SideEffectMethodMarker consider them as such
  * without further analysis.
  *
@@ -33,55 +33,58 @@ import proguard.classfile.visitor.MemberInfoVisitor;
  * @author Eric Lafortune
  */
 public class NoSideEffectMethodMarker
-  implements MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   MemberVisitor
 {
     // A visitor info flag to indicate the visitor accepter is being kept,
     // but that it doesn't have any side effects.
     private static final Object KEPT_BUT_NO_SIDE_EFFECTS = new Object();
 
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
+    // Implementations for MemberVisitor.
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitAnyMember(Clazz Clazz, Member member)
     {
-        markNoSideEffects(programMethodInfo);
+        // Ignore any attempts to mark fields.
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        markNoSideEffects(programMethod);
+    }
+
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        markNoSideEffects(libraryMethodInfo);
+        markNoSideEffects(libraryMethod);
     }
 
 
     // Small utility methods.
 
-    public static void markNoSideEffects(MethodInfo methodInfo)
+    private static void markNoSideEffects(Method method)
     {
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
         if (info != null)
         {
             info.setNoSideEffects();
         }
         else
         {
-            MethodInfoLinker.lastMemberInfo(methodInfo).setVisitorInfo(KEPT_BUT_NO_SIDE_EFFECTS);
+            MethodLinker.lastMember(method).setVisitorInfo(KEPT_BUT_NO_SIDE_EFFECTS);
         }
     }
 
 
-    public static boolean hasNoSideEffects(MethodInfo methodInfo)
+    public static boolean hasNoSideEffects(Method method)
     {
-        if (MethodInfoLinker.lastVisitorAccepter(methodInfo).getVisitorInfo() == KEPT_BUT_NO_SIDE_EFFECTS)
+        if (MethodLinker.lastVisitorAccepter(method).getVisitorInfo() == KEPT_BUT_NO_SIDE_EFFECTS)
         {
             return true;
         }
 
-        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(methodInfo);
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
         return info != null &&
                info.hasNoSideEffects();
     }
diff --git a/src/proguard/optimize/info/NonPrivateMemberMarker.java b/src/proguard/optimize/info/NonPrivateMemberMarker.java
new file mode 100644
index 0000000..eb5f26f
--- /dev/null
+++ b/src/proguard/optimize/info/NonPrivateMemberMarker.java
@@ -0,0 +1,177 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassVisitor marks all class members that can not be made private in the
+ * classes that it visits, and in the classes to which they refer.
+ *
+ * @author Eric Lafortune
+ */
+public class NonPrivateMemberMarker
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    private final MethodImplementationFilter filteredMethodMarker = new MethodImplementationFilter(this);
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Mark all referenced class members in different classes.
+        programClass.constantPoolEntriesAccept(this);
+
+        // Explicitly mark the <clinit> method.
+        programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
+                                  ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
+                                  this);
+
+        // Explicitly mark the parameterless <init> method.
+        programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT,
+                                  ClassConstants.INTERNAL_METHOD_TYPE_INIT,
+                                  this);
+
+        // Mark all methods that may have implementations.
+        programClass.methodsAccept(filteredMethodMarker);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Go over all methods.
+        libraryClass.methodsAccept(this);
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        Clazz referencedClass = stringConstant.referencedClass;
+
+        // Is it refering to another class or class member?
+        if (referencedClass != null &&
+            !referencedClass.equals(clazz))
+        {
+            // The referenced class member, if any, can never be made private.
+            stringConstant.referencedMemberAccept(this);
+        }
+    }
+
+
+    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        Clazz referencedClass = refConstant.referencedClass;
+
+        // Is it refering to a class member in another class?
+        // The class member might be in another class, or
+        // it may be referenced through another class.
+        if (referencedClass != null &&
+            !referencedClass.equals(clazz) ||
+            !refConstant.getClassName(clazz).equals(clazz.getName()))
+        {
+            // The referenced class member can never be made private.
+            refConstant.referencedMemberAccept(this);
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        markCanNotBeMadePrivate(programField);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        markCanNotBeMadePrivate(libraryField);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        markCanNotBeMadePrivate(programMethod);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        markCanNotBeMadePrivate(libraryMethod);
+    }
+
+
+    // Small utility methods.
+
+    private static void markCanNotBeMadePrivate(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        if (info != null)
+        {
+            info.setCanNotBeMadePrivate();
+        }
+    }
+
+
+    /**
+     * Returns whether the given field can be made private.
+     */
+    public static boolean canBeMadePrivate(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        return info != null &&
+               info.canBeMadePrivate();
+    }
+
+
+    private static void markCanNotBeMadePrivate(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setCanNotBeMadePrivate();
+        }
+    }
+
+
+    /**
+     * Returns whether the given method can be made private.
+     */
+    public static boolean canBeMadePrivate(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info != null &&
+               info.canBeMadePrivate();
+    }
+}
diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java
new file mode 100644
index 0000000..736846d
--- /dev/null
+++ b/src/proguard/optimize/info/ParameterUsageMarker.java
@@ -0,0 +1,220 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This MemberVisitor counts the parameters and marks the used parameters
+ * of the methods that it visits. It also marks the 'this' parameters of
+ * methods that have hierarchies.
+ *
+ * @author Eric Lafortune
+ */
+public class ParameterUsageMarker
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private static final boolean DEBUG = false;
+
+
+    private final VariableUsageMarker variableUsageMarker = new VariableUsageMarker();
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        int parameterSize = parameterSize(programClass, programMethod);
+        if (parameterSize > 0)
+        {
+            // Is it a native method?
+            int accessFlags = programMethod.getAccessFlags();
+            if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0)
+            {
+                // Mark all parameters.
+                markUsedParameters(programMethod, -1L);
+            }
+
+            // Is it an abstract method?
+            else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0)
+            {
+                // Mark the 'this' parameter.
+                markParameterUsed(programMethod, 0);
+            }
+
+            // Is it a non-native, concrete method?
+            else
+            {
+                // Is the method not static, but synchronized, or can it have
+                // other implementations, or is it a class instance initializer?
+                if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0 &&
+                    ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0 ||
+                     programClass.mayHaveImplementations(programMethod)            ||
+                     programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)))
+                {
+                    // Mark the 'this' parameter.
+                    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);
+                    }
+                }
+            }
+
+            if (DEBUG)
+            {
+                System.out.print("ParameterUsageMarker: ["+programClass.getName() +"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]: ");
+                for (int index = 0; index < parameterSize; index++)
+                {
+                    System.out.print(isParameterUsed(programMethod, index) ? '+' : '-');
+                }
+                System.out.println();
+            }
+
+        }
+
+        // Set the parameter size.
+        setParameterSize(programMethod, parameterSize);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        // Can the method have other implementations?
+        if (libraryClass.mayHaveImplementations(libraryMethod))
+        {
+            // All implementations must keep all parameters of this method,
+            // including the 'this' parameter.
+            long usedParameters = -1L;
+
+            // Mark it.
+            markUsedParameters(libraryMethod, usedParameters);
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Sets the total size of the parameters.
+     */
+    private static void setParameterSize(Method method, int parameterSize)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setParameterSize(parameterSize);
+        }
+    }
+
+
+    /**
+     * Returns the total size of the parameters.
+     */
+    public static int getParameterSize(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info != null ? info.getParameterSize() : 0;
+    }
+
+
+    /**
+     * Marks the given parameter as being used.
+     */
+    public static void markParameterUsed(Method method, int variableIndex)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setParameterUsed(variableIndex);
+        }
+    }
+
+
+    /**
+     * Marks the given parameters as being used.
+     */
+    public static void markUsedParameters(Method method, long usedParameters)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setUsedParameters(info.getUsedParameters() | usedParameters);
+        }
+    }
+
+
+    /**
+     * Returns whether the given parameter is being used.
+     */
+    public static boolean isParameterUsed(Method method, int variableIndex)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null ||
+               info.isParameterUsed(variableIndex);
+    }
+
+
+    /**
+     * Returns which parameters are being used.
+     */
+    public static long getUsedParameters(Method method)
+    {
+        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)
+    {
+        int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0)
+        {
+            parameterSize++;
+        }
+
+        return parameterSize;
+    }
+}
diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/src/proguard/optimize/info/ReadWriteFieldMarker.java
new file mode 100644
index 0000000..6d312c4
--- /dev/null
+++ b/src/proguard/optimize/info/ReadWriteFieldMarker.java
@@ -0,0 +1,163 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This InstructionVisitor marks all fields that are write-only.
+ *
+ * @author Eric Lafortune
+ */
+public class ReadWriteFieldMarker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    // Parameters for the visitor methods.
+    private boolean reading;
+    private boolean writing;
+
+
+    // 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)
+    {
+        byte opcode = constantInstruction.opcode;
+
+        // Check for instructions that involve fields.
+        switch (opcode)
+        {
+            case InstructionConstants.OP_LDC:
+            case InstructionConstants.OP_LDC_W:
+                // Mark the field, if any, as being read from and written to.
+                reading = true;
+                writing = true;
+                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+                break;
+
+            case InstructionConstants.OP_GETSTATIC:
+            case InstructionConstants.OP_GETFIELD:
+                // Mark the field as being read from.
+                reading = true;
+                writing = false;
+                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+                break;
+
+            case InstructionConstants.OP_PUTSTATIC:
+            case InstructionConstants.OP_PUTFIELD:
+                // Mark the field as being written to.
+                reading = false;
+                writing = true;
+                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+                break;
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+    {
+        // Mark the referenced field, if any.
+        stringConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        // Mark the referenced field.
+        fieldrefConstant.referencedMemberAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitAnyMember(Clazz Clazz, Member member) {}
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Mark the field if it is being read from.
+        if (reading)
+        {
+            markAsRead(programField);
+        }
+
+        // Mark the field if it is being written to.
+        if (writing)
+        {
+            markAsWritten(programField);
+        }
+    }
+
+
+    // Small utility methods.
+
+    private static void markAsRead(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        if (info != null)
+        {
+            info.setRead();
+        }
+    }
+
+
+    public static boolean isRead(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        return info == null ||
+               info.isRead();
+    }
+
+
+    private static void markAsWritten(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        if (info != null)
+        {
+            info.setWritten();
+        }
+    }
+
+
+    public static boolean isWritten(Field field)
+    {
+        FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field);
+        return info == null ||
+               info.isWritten();
+    }
+}
diff --git a/src/proguard/optimize/info/SideEffectInstructionChecker.java b/src/proguard/optimize/info/SideEffectInstructionChecker.java
new file mode 100644
index 0000000..fae64c8
--- /dev/null
+++ b/src/proguard/optimize/info/SideEffectInstructionChecker.java
@@ -0,0 +1,220 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This class can tell whether an instruction has any side effects. Return
+ * instructions can be included or not.
+ *
+ * @see ReadWriteFieldMarker
+ * @see NoSideEffectMethodMarker
+ * @see SideEffectMethodMarker
+ * @author Eric Lafortune
+ */
+public class SideEffectInstructionChecker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    private final boolean includeReturnInstructions;
+
+    // A return value for the visitor methods.
+    private boolean hasSideEffects;
+
+
+    public SideEffectInstructionChecker(boolean includeReturnInstructions)
+    {
+        this.includeReturnInstructions = includeReturnInstructions;
+    }
+
+
+    public boolean hasSideEffects(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        hasSideEffects = false;
+
+        instruction.accept(clazz, method,  codeAttribute, offset, this);
+
+        return hasSideEffects;
+    }
+
+
+    // 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)
+    {
+        byte opcode = simpleInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (opcode == InstructionConstants.OP_IASTORE      ||
+            opcode == InstructionConstants.OP_LASTORE      ||
+            opcode == InstructionConstants.OP_FASTORE      ||
+            opcode == InstructionConstants.OP_DASTORE      ||
+            opcode == InstructionConstants.OP_AASTORE      ||
+            opcode == InstructionConstants.OP_BASTORE      ||
+            opcode == InstructionConstants.OP_CASTORE      ||
+            opcode == InstructionConstants.OP_SASTORE      ||
+            opcode == InstructionConstants.OP_ATHROW       ||
+            opcode == InstructionConstants.OP_MONITORENTER ||
+            opcode == InstructionConstants.OP_MONITOREXIT  ||
+            (includeReturnInstructions &&
+             (opcode == InstructionConstants.OP_IRETURN ||
+              opcode == InstructionConstants.OP_LRETURN ||
+              opcode == InstructionConstants.OP_FRETURN ||
+              opcode == InstructionConstants.OP_DRETURN ||
+              opcode == InstructionConstants.OP_ARETURN ||
+              opcode == InstructionConstants.OP_RETURN)))
+        {
+            // These instructions always cause a side effect.
+            hasSideEffects = true;
+        }
+
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        byte opcode = variableInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (includeReturnInstructions &&
+            opcode == InstructionConstants.OP_RET)
+        {
+            hasSideEffects = true;
+        }
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        byte opcode = constantInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (opcode == InstructionConstants.OP_PUTSTATIC     ||
+            opcode == InstructionConstants.OP_PUTFIELD      ||
+            opcode == InstructionConstants.OP_INVOKEVIRTUAL ||
+            opcode == InstructionConstants.OP_INVOKESPECIAL ||
+            opcode == InstructionConstants.OP_INVOKESTATIC  ||
+            opcode == InstructionConstants.OP_INVOKEINTERFACE)
+        {
+            // Check if the field is write-only or volatile, or if the invoked
+            // method is causing any side effects.
+            clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+        }
+    }
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        byte opcode = branchInstruction.opcode;
+
+        // Check for instructions that might cause side effects.
+        if (includeReturnInstructions &&
+            (opcode == InstructionConstants.OP_JSR ||
+             opcode == InstructionConstants.OP_JSR_W))
+        {
+            hasSideEffects = true;
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
+    {
+        // We'll have to assume accessing an unknown field has side effects.
+        hasSideEffects = true;
+
+        // Check the referenced field.
+        fieldrefConstant.referencedMemberAccept(this);
+    }
+
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        Member referencedMember = refConstant.referencedMember;
+
+        // Do we have a reference to the method?
+        if (referencedMember == null)
+        {
+            // We'll have to assume invoking the unknown method has side effects.
+            hasSideEffects = true;
+        }
+        else
+        {
+            // First check the referenced method itself.
+            refConstant.referencedMemberAccept(this);
+
+            // If the result isn't conclusive, check down the hierarchy.
+            if (!hasSideEffects)
+            {
+                Clazz  referencedClass  = refConstant.referencedClass;
+                Method referencedMethod = (Method)referencedMember;
+
+                // Check all other implementations of the method in the class
+                // hierarchy.
+                referencedClass.methodImplementationsAccept(referencedMethod,
+                                                            false,
+                                                            this);
+            }
+        }
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        hasSideEffects = ReadWriteFieldMarker.isRead(programField);
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        hasSideEffects = hasSideEffects ||
+                         SideEffectMethodMarker.hasSideEffects(programMethod);
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        hasSideEffects = true;
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        hasSideEffects = hasSideEffects ||
+                         !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod);
+    }
+}
diff --git a/src/proguard/optimize/info/SideEffectMethodMarker.java b/src/proguard/optimize/info/SideEffectMethodMarker.java
new file mode 100644
index 0000000..a7a2ad4
--- /dev/null
+++ b/src/proguard/optimize/info/SideEffectMethodMarker.java
@@ -0,0 +1,175 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassPoolVisitor marks all methods that have side effects.
+ *
+ * @see ReadWriteFieldMarker
+ * @see NoSideEffectMethodMarker
+ * @author Eric Lafortune
+ */
+public class SideEffectMethodMarker
+extends      SimplifiedVisitor
+implements   ClassPoolVisitor,
+             ClassVisitor,
+             MemberVisitor,
+             AttributeVisitor
+{
+    // A reusable object for checking whether instructions have side effects.
+    private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false);
+
+    // Parameters and values for visitor methods.
+    private int     newSideEffectCount;
+    private boolean hasSideEffects;
+
+
+    // Implementations for ClassPoolVisitor.
+
+    public void visitClassPool(ClassPool classPool)
+    {
+        // Go over all classes and their methods, marking if they have side
+        // effects, until no new cases can be found.
+        do
+        {
+            newSideEffectCount = 0;
+
+            // Go over all classes and their methods once.
+            classPool.classesAccept(this);
+        }
+        while (newSideEffectCount > 0);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Go over all methods.
+        programClass.methodsAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (!hasSideEffects(programMethod) &&
+            !NoSideEffectMethodMarker.hasNoSideEffects(programMethod))
+        {
+            // Initialize the return value.
+            hasSideEffects =
+                (programMethod.getAccessFlags() &
+                 (ClassConstants.INTERNAL_ACC_NATIVE |
+                  ClassConstants.INTERNAL_ACC_SYNCHRONIZED)) != 0;
+
+            // Look further if the method hasn't been marked yet.
+            if (!hasSideEffects)
+            {
+                // Investigate the actual code.
+                programMethod.attributesAccept(programClass, this);
+            }
+
+            // Mark the method depending on the return value.
+            if (hasSideEffects)
+            {
+                markSideEffects(programMethod);
+
+                newSideEffectCount++;
+            }
+        }
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Remember whether the code has any side effects.
+        hasSideEffects = hasSideEffects(clazz, method, codeAttribute);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the given code has any side effects.
+     */
+    private boolean hasSideEffects(Clazz         clazz,
+                                   Method        method,
+                                   CodeAttribute codeAttribute)
+    {
+        byte[] code   = codeAttribute.code;
+        int    length = codeAttribute.u4codeLength;
+
+        // Go over all instructions.
+        int offset = 0;
+        do
+        {
+            // Get the current instruction.
+            Instruction instruction = InstructionFactory.create(code, offset);
+
+            // Check if it may be throwing exceptions.
+            if (sideEffectInstructionChecker.hasSideEffects(clazz,
+                                                            method,
+                                                            codeAttribute,
+                                                            offset,
+                                                            instruction))
+            {
+                return true;
+            }
+
+            // Go to the next instruction.
+            offset += instruction.length(offset);
+        }
+        while (offset < length);
+
+        return false;
+    }
+
+
+    private static void markSideEffects(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setSideEffects();
+        }
+    }
+
+
+    public static boolean hasSideEffects(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null ||
+               info.hasSideEffects();
+    }
+}
diff --git a/src/proguard/optimize/peephole/SingleImplementationMarker.java b/src/proguard/optimize/info/SingleImplementationMarker.java
similarity index 60%
rename from src/proguard/optimize/peephole/SingleImplementationMarker.java
rename to src/proguard/optimize/info/SingleImplementationMarker.java
index a048fc6..87f6f14 100644
--- a/src/proguard/optimize/peephole/SingleImplementationMarker.java
+++ b/src/proguard/optimize/info/SingleImplementationMarker.java
@@ -1,6 +1,6 @@
-/* $Id: SingleImplementationMarker.java,v 1.8.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,29 +18,30 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-package proguard.optimize.peephole;
+package proguard.optimize.info;
 
 import proguard.classfile.*;
 import proguard.classfile.util.*;
-import proguard.classfile.visitor.*;
-import proguard.optimize.*;
+import proguard.classfile.visitor.ClassVisitor;
+import proguard.optimize.KeepMarker;
 
 /**
- * This ClassFileVisitor investigates all class files that it visits to see
- * whether they have/are the sole (non-abstract) implementation of an interface.
+ * This ClassVisitor investigates all classes that it visits to see whether
+ * they have/are the sole (non-abstract) implementation of an interface.
  * It may already modify the access of the single implementing class to match
  * the access of the interface.
  *
  * @author Eric Lafortune
  */
 public class SingleImplementationMarker
-implements   ClassFileVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor
 {
     private static final boolean DEBUG = false;
 
 
-    private boolean          allowAccessModification;
-    private ClassFileVisitor extraClassFileVisitor;
+    private final boolean      allowAccessModification;
+    private final ClassVisitor extraClassVisitor;
 
 
     /**
@@ -60,31 +61,31 @@ implements   ClassFileVisitor
      * @param allowAccessModification indicates whether the access modifiers of
      *                                a class can be changed in order to inline
      *                                it.
-     * @param extraClassFileVisitor   an optional extra visitor for all inlinable
+     * @param extraClassVisitor       an optional extra visitor for all inlinable
      *                                interfaces.
      */
-    public SingleImplementationMarker(boolean          allowAccessModification,
-                                      ClassFileVisitor extraClassFileVisitor)
+    public SingleImplementationMarker(boolean      allowAccessModification,
+                                      ClassVisitor extraClassVisitor)
     {
         this.allowAccessModification = allowAccessModification;
-        this.extraClassFileVisitor   = extraClassFileVisitor;
+        this.extraClassVisitor       = extraClassVisitor;
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // The program class must be an interface class that cannot be
         // implemented again.
-        if ((programClassFile.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ||
-            KeepMarker.isKept(programClassFile))
+        if ((programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) == 0 ||
+            KeepMarker.isKept(programClass))
         {
             return;
         }
 
         // The interface class must have a single implementation.
-        ClassFile[] subClasses = programClassFile.subClasses;
+        Clazz[] subClasses = programClass.subClasses;
         if (subClasses == null ||
             subClasses.length != 1)
         {
@@ -92,30 +93,30 @@ implements   ClassFileVisitor
         }
 
         // If the single implementation is an interface, check it recursively.
-        ClassFile singleImplementationClassFile = subClasses[0];
-        int singleImplementationAccessFlags = singleImplementationClassFile.getAccessFlags();
+        Clazz singleImplementationClass = subClasses[0];
+        int singleImplementationAccessFlags = singleImplementationClass.getAccessFlags();
         if ((singleImplementationAccessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0)
         {
-            singleImplementationClassFile.accept(this);
+            singleImplementationClass.accept(this);
 
             // See if the subinterface has a single implementation.
-            singleImplementationClassFile = singleImplementation(singleImplementationClassFile);
-            if (singleImplementationClassFile == null)
+            singleImplementationClass = singleImplementation(singleImplementationClass);
+            if (singleImplementationClass == null)
             {
                 return;
             }
 
-            singleImplementationAccessFlags = singleImplementationClassFile.getAccessFlags();
+            singleImplementationAccessFlags = singleImplementationClass.getAccessFlags();
         }
 
         // The single implementation must contain all non-static methods of this
         // interface, so invocations can easily be diverted.
-        for (int index = 0; index < programClassFile.u2methodsCount; index++)
+        for (int index = 0; index < programClass.u2methodsCount; index++)
         {
-            MethodInfo method = programClassFile.methods[index];
+            Method method = programClass.methods[index];
             if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 &&
-                singleImplementationClassFile.findMethod(method.getName(programClassFile),
-                                                         method.getDescriptor(programClassFile)) == null)
+                singleImplementationClass.findMethod(method.getName(programClass),
+                                                     method.getDescriptor(programClass)) == null)
             {
                 return;
             }
@@ -124,15 +125,15 @@ implements   ClassFileVisitor
         // Doesn't the implementation have at least the same access as the
         // interface?
         if (AccessUtil.accessLevel(singleImplementationAccessFlags) <
-            AccessUtil.accessLevel(programClassFile.getAccessFlags()))
+            AccessUtil.accessLevel(programClass.getAccessFlags()))
         {
             // Are we allowed to fix the access?
             if (allowAccessModification)
             {
                 // Fix the access.
-                ((ProgramClassFile)singleImplementationClassFile).u2accessFlags =
+                ((ProgramClass)singleImplementationClass).u2accessFlags =
                     AccessUtil.replaceAccessFlags(singleImplementationAccessFlags,
-                                                  programClassFile.getAccessFlags());
+                                                  programClass.getAccessFlags());
             }
             else
             {
@@ -144,38 +145,35 @@ implements   ClassFileVisitor
 
         if (DEBUG)
         {
-            System.out.println("Single implementation of ["+programClassFile.getName()+"]: ["+singleImplementationClassFile.getName()+"]");
+            System.out.println("Single implementation of ["+programClass.getName()+"]: ["+singleImplementationClass.getName()+"]");
         }
 
         // Mark the interface and its single implementation.
-        markSingleImplementation(programClassFile, singleImplementationClassFile);
+        markSingleImplementation(programClass, singleImplementationClass);
 
         // Visit the interface, if required.
-        if (extraClassFileVisitor != null)
+        if (extraClassVisitor != null)
         {
-            singleImplementationClassFile.accept(extraClassFileVisitor);
+            singleImplementationClass.accept(extraClassVisitor);
         }
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
-
-
     // Small utility methods.
 
     public static void markSingleImplementation(VisitorAccepter visitorAccepter,
-                                                ClassFile       singleImplementation)
+                                                Clazz           singleImplementation)
     {
         // The interface has a single implementation.
         visitorAccepter.setVisitorInfo(singleImplementation);
     }
 
 
-    public static ClassFile singleImplementation(VisitorAccepter visitorAccepter)
+    public static Clazz singleImplementation(VisitorAccepter visitorAccepter)
     {
         return visitorAccepter != null &&
-               visitorAccepter.getVisitorInfo() instanceof ClassFile ?
-                   (ClassFile)visitorAccepter.getVisitorInfo() :
+               visitorAccepter.getVisitorInfo() instanceof Clazz ?
+                   (Clazz)visitorAccepter.getVisitorInfo() :
                    null;
     }
 }
diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/src/proguard/optimize/info/SuperInvocationMarker.java
new file mode 100644
index 0000000..ba5e07c
--- /dev/null
+++ b/src/proguard/optimize/info/SuperInvocationMarker.java
@@ -0,0 +1,93 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.RefConstant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This InstructionVisitor marks all methods that invoke super methods (other
+ * than initializers) from the instructions that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class SuperInvocationMarker
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor
+{
+    private boolean invokesSuperMethods;
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        if (constantInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL)
+        {
+            invokesSuperMethods = false;
+
+            clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+
+            if (invokesSuperMethods)
+            {
+                setInvokesSuperMethods(method);
+            }
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant)
+    {
+        invokesSuperMethods =
+            !clazz.equals(refConstant.referencedClass) &&
+            !refConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+    }
+
+
+    // Small utility methods.
+
+    private static void setInvokesSuperMethods(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        if (info != null)
+        {
+            info.setInvokesSuperMethods();
+        }
+    }
+
+
+    public static boolean invokesSuperMethods(Method method)
+    {
+        MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method);
+        return info == null || info.invokesSuperMethods();
+    }
+}
diff --git a/src/proguard/optimize/info/VariableUsageMarker.java b/src/proguard/optimize/info/VariableUsageMarker.java
new file mode 100644
index 0000000..298908e
--- /dev/null
+++ b/src/proguard/optimize/info/VariableUsageMarker.java
@@ -0,0 +1,108 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.info;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor marks the local variables that are used in the code
+ * attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableUsageMarker
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor
+{
+    private int       variablesSize;
+    private boolean[] variableUsed = new boolean[ClassConstants.TYPICAL_VARIABLES_SIZE];
+
+
+    /**
+     * Returns the size of the variables frame of the most recently visited
+     * code attribute.
+     */
+    public int getVariablesSize()
+    {
+        return variablesSize;
+    }
+
+
+    /**
+     * Returns whether the given variable has been marked as being used.
+     */
+    public boolean isVariableUsed(int variableIndex)
+    {
+        return variableUsed[variableIndex];
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        int maxLocals = codeAttribute.u2maxLocals;
+
+        // Try to reuse the previous array.
+        if (variableUsed.length < maxLocals)
+        {
+            variableUsed = new boolean[maxLocals];
+        }
+        else
+        {
+            for (int index = 0; index < maxLocals; index++)
+            {
+                variableUsed[index] = false;
+            }
+        }
+
+        variablesSize = maxLocals;
+
+        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)
+    {
+        // Mark the variable.
+        variableUsed[variableInstruction.variableIndex] = true;
+
+        // Account for Category 2 instructions, which take up two entries.
+        if (variableInstruction.isCategory2())
+        {
+            variableUsed[variableInstruction.variableIndex + 1] = true;
+        }
+    }
+}
diff --git a/src/proguard/optimize/info/package.html b/src/proguard/optimize/info/package.html
new file mode 100644
index 0000000..d16486e
--- /dev/null
+++ b/src/proguard/optimize/info/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains classes to collect additional information about classes
+and class members, which can then be used for optimization.
+</body>
diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java
index 3030446..80f9149 100644
--- a/src/proguard/optimize/peephole/BranchTargetFinder.java
+++ b/src/proguard/optimize/peephole/BranchTargetFinder.java
@@ -1,6 +1,6 @@
-/* $Id: BranchTargetFinder.java,v 1.8.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,55 +22,65 @@ package proguard.optimize.peephole;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This AttrInfoVisitor finds all instruction offsets, branch targets, and
- * exception targets in the CodeAttrInfo objects that it visits.
+ * This AttributeVisitor finds all instruction offsets, branch targets, and
+ * exception targets in the CodeAttribute objects that it visits.
  *
  * @author Eric Lafortune
  */
 public class BranchTargetFinder
-implements   AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
              InstructionVisitor,
              ExceptionInfoVisitor,
-             CpInfoVisitor
+             ConstantVisitor
 {
-    private static final short INSTRUCTION       =   1;
-    private static final short BRANCH_ORIGIN     =   2;
-    private static final short BRANCH_TARGET     =   4;
-//  private static final short AFTER_BRANCH      =   8;
-    private static final short EXCEPTION_START   =  16;
-    private static final short EXCEPTION_END     =  32;
-    private static final short EXCEPTION_HANDLER =  64;
-    private static final short SUBROUTINE_START  = 128;
-    private static final short SUBROUTINE_END    = 256;
-    private static final short INITIALIZER       = 512;
-
-
-    private short[] instructionMarks;
-    private int[]   subroutineEnds;
-
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+    public static final int NONE            = -2;
+    public static final int AT_METHOD_ENTRY = -1;
+
+    private static final short INSTRUCTION           = 1 << 0;
+    private static final short BRANCH_ORIGIN         = 1 << 1;
+    private static final short BRANCH_TARGET         = 1 << 2;
+    private static final short AFTER_BRANCH          = 1 << 3;
+    private static final short EXCEPTION_START       = 1 << 4;
+    private static final short EXCEPTION_END         = 1 << 5;
+    private static final short EXCEPTION_HANDLER     = 1 << 6;
+    private static final short SUBROUTINE_INVOCATION = 1 << 7;
+    private static final short SUBROUTINE_RETURNING  = 1 << 8;
+
+    private static final int MAXIMUM_CREATION_OFFSETS = 32;
+
+
+    private short[] instructionMarks      = new short[ClassConstants.TYPICAL_CODE_LENGTH + 1];
+    private int[]   subroutineStarts      = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]   subroutineEnds        = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]   creationOffsets       = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int[]   initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
+    private int     superInitializationOffset;
+
+    private int     currentSubroutineStart;
+    private int     currentSubroutineEnd;
+    private final int[]   recentCreationOffsets = new int[MAXIMUM_CREATION_OFFSETS];
+    private int     recentCreationOffsetIndex;
     private boolean isInitializer;
 
 
     /**
-     * Creates a new BranchTargetFinder.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public BranchTargetFinder(int codeLength)
-    {
-        instructionMarks = new short[codeLength + 1];
-        subroutineEnds   = new int[codeLength];
-    }
-
-
-    /**
      * Returns whether there is an instruction at the given offset in the
-     * CodeAttrInfo that was visited most recently.
+     * CodeAttribute that was visited most recently.
      */
     public boolean isInstruction(int offset)
     {
@@ -79,13 +89,13 @@ implements   AttrInfoVisitor,
 
 
     /**
-     * Returns whether the instruction at the given offset is the target of a
-     * branch instruction or an exception in the CodeAttrInfo that was visited
-     * most recently.
+     * Returns whether the instruction at the given offset is the target of
+     * any kind in the CodeAttribute that was visited most recently.
      */
     public boolean isTarget(int offset)
     {
-        return (instructionMarks[offset] & (BRANCH_TARGET   |
+        return offset == 0 ||
+               (instructionMarks[offset] & (BRANCH_TARGET   |
                                             EXCEPTION_START |
                                             EXCEPTION_END   |
                                             EXCEPTION_HANDLER)) != 0;
@@ -94,7 +104,7 @@ implements   AttrInfoVisitor,
 
     /**
      * Returns whether the instruction at the given offset is the origin of a
-     * branch instruction in the CodeAttrInfo that was visited most recently.
+     * branch instruction in the CodeAttribute that was visited most recently.
      */
     public boolean isBranchOrigin(int offset)
     {
@@ -104,7 +114,7 @@ implements   AttrInfoVisitor,
 
     /**
      * Returns whether the instruction at the given offset is the target of a
-     * branch instruction in the CodeAttrInfo that was visited most recently.
+     * branch instruction in the CodeAttribute that was visited most recently.
      */
     public boolean isBranchTarget(int offset)
     {
@@ -113,8 +123,19 @@ implements   AttrInfoVisitor,
 
 
     /**
+     * Returns whether the instruction at the given offset comes right after a
+     * definite branch instruction in the CodeAttribute that was visited most
+     * recently.
+     */
+    public boolean isAfterBranch(int offset)
+    {
+        return (instructionMarks[offset] & AFTER_BRANCH) != 0;
+    }
+
+
+    /**
      * Returns whether the instruction at the given offset is the start of an
-     * exception try block in the CodeAttrInfo that was visited most recently.
+     * exception try block in the CodeAttribute that was visited most recently.
      */
     public boolean isExceptionStart(int offset)
     {
@@ -124,7 +145,7 @@ implements   AttrInfoVisitor,
 
     /**
      * Returns whether the instruction at the given offset is the end of an
-     * exception try block in the CodeAttrInfo that was visited most recently.
+     * exception try block in the CodeAttribute that was visited most recently.
      */
     public boolean isExceptionEnd(int offset)
     {
@@ -134,7 +155,7 @@ implements   AttrInfoVisitor,
 
     /**
      * Returns whether the instruction at the given offset is the start of an
-     * exception catch block in the CodeAttrInfo that was visited most recently.
+     * exception catch block in the CodeAttribute that was visited most recently.
      */
     public boolean isExceptionHandler(int offset)
     {
@@ -143,30 +164,58 @@ implements   AttrInfoVisitor,
 
 
     /**
+     * Returns whether the instruction at the given offset is a subroutine
+     * invocation in the CodeAttribute that was visited most recently.
+     */
+    public boolean isSubroutineInvocation(int offset)
+    {
+        return (instructionMarks[offset] & SUBROUTINE_INVOCATION) != 0;
+    }
+
+
+    /**
      * Returns whether the instruction at the given offset is the start of a
      * subroutine in the CodeAttribute that was visited most recently.
      */
     public boolean isSubroutineStart(int offset)
     {
-        return (instructionMarks[offset] & SUBROUTINE_START) != 0;
+        return subroutineStarts[offset] == offset;
+    }
+
+
+    /**
+     * Returns whether the instruction at the given offset is part of a
+     * subroutine in the CodeAttribute that was visited most recently.
+     */
+    public boolean isSubroutine(int offset)
+    {
+        return subroutineStarts[offset] != NONE;
     }
 
 
     /**
-     * Returns whether the instruction at the given offset is the end of a
-     * subroutine in the CodeAttribute that was visited most recently. Such an
-     * instruction is always a 'ret' instruction.
+     * Returns whether the subroutine at the given offset is ever returning
+     * by means of a regular 'ret' instruction.
      */
-    public boolean isSubroutineEnd(int offset)
+    public boolean isSubroutineReturning(int offset)
     {
-        return (instructionMarks[offset] & SUBROUTINE_END) != 0;
+        return (instructionMarks[offset] & SUBROUTINE_RETURNING) != 0;
     }
 
 
     /**
-     * Returns the offset of the end of the subroutine that starts at the given
-     * offset, in the CodeAttribute that was visited most recently. Such an
-     * end is always at a 'ret' instruction.
+     * Returns the start offset of the subroutine at the given offset, in the
+     * CodeAttribute that was visited most recently.
+     */
+    public int subroutineStart(int offset)
+    {
+        return subroutineStarts[offset];
+    }
+
+
+    /**
+     * Returns the offset after the subroutine at the given offset, in the
+     * CodeAttribute that was visited most recently.
      */
     public int subroutineEnd(int offset)
     {
@@ -175,92 +224,234 @@ implements   AttrInfoVisitor,
 
 
     /**
+     * Returns whether the instruction at the given offset is a 'new'
+     * instruction, in the CodeAttribute that was visited most recently.
+     */
+    public boolean isNew(int offset)
+    {
+        return initializationOffsets[offset] != NONE;
+    }
+
+
+    /**
+     * Returns the instruction offset at which the object instance that is
+     * created at the given 'new' instruction offset is initialized, or
+     * <code>NONE</code> if it is not being created.
+     */
+    public int initializationOffset(int offset)
+    {
+        return initializationOffsets[offset];
+    }
+
+
+    /**
+     * Returns whether the method is an instance initializer, in the
+     * CodeAttribute that was visited most recently.
+     */
+    public boolean isInitializer()
+    {
+        return superInitializationOffset != NONE;
+    }
+
+
+    /**
+     * Returns the instruction offset at which this initializer is calling
+     * the "super" or "this" initializer method, or <code>NONE</code> if it is
+     * not an initializer.
+     */
+    public int superInitializationOffset()
+    {
+        return superInitializationOffset;
+    }
+
+
+    /**
      * Returns whether the instruction at the given offset is the special
-     * invocation of an instance initializer in the CodeAttrInfo that was
+     * invocation of an instance initializer, in the CodeAttribute that was
      * visited most recently.
      */
     public boolean isInitializer(int offset)
     {
-        return (instructionMarks[offset] & INITIALIZER) != 0;
+        return creationOffsets[offset] != NONE;
+    }
+
+
+    /**
+     * Returns the offset of the 'new' instruction that corresponds to the
+     * invocation of the instance initializer at the given offset, or
+     * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or
+     * "this" initializer method, , or <code>NONE</code> if it is not a 'new'
+     * instruction.
+     */
+    public int creationOffset(int offset)
+    {
+        return creationOffsets[offset];
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
         // Make sure there are sufficiently large arrays.
-        int codeLength = codeAttrInfo.u4codeLength;
-        if (subroutineEnds.length < codeLength)
+        int codeLength = codeAttribute.u4codeLength;
+        if (subroutineStarts.length < codeLength)
         {
-            // Create a new arrays.
-            instructionMarks = new short[codeLength + 1];
-            subroutineEnds   = new int[codeLength];
+            // Create new arrays.
+            instructionMarks      = new short[codeLength + 1];
+            subroutineStarts      = new int[codeLength];
+            subroutineEnds        = new int[codeLength];
+            creationOffsets       = new int[codeLength];
+            initializationOffsets = new int[codeLength];
+
+            // Reset the arrays.
+            for (int index = 0; index < codeLength; index++)
+            {
+                subroutineStarts[index]      = NONE;
+                subroutineEnds[index]        = NONE;
+                creationOffsets[index]       = NONE;
+                initializationOffsets[index] = NONE;
+            }
         }
         else
         {
-            // Reset the marks array.
+            // Reset the arrays.
             for (int index = 0; index < codeLength; index++)
             {
-                instructionMarks[index] = 0;
+                instructionMarks[index]      = 0;
+                subroutineStarts[index]      = NONE;
+                subroutineEnds[index]        = NONE;
+                creationOffsets[index]       = NONE;
+                initializationOffsets[index] = NONE;
             }
+
+            instructionMarks[codeLength] = 0;
         }
 
-        // The first instruction and the end of the code are branch target
-        // sentinels.
-        instructionMarks[0]          = BRANCH_TARGET;
+        superInitializationOffset = NONE;
+
+        // We're not starting in a subroutine.
+        currentSubroutineStart = NONE;
+        currentSubroutineEnd   = NONE;
+
+        recentCreationOffsetIndex = 0;
+
+        // Initialize the stack of 'new' instruction offsets if this method is
+        // an instance initializer.
+        if (method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+        {
+            recentCreationOffsets[recentCreationOffsetIndex++] = AT_METHOD_ENTRY;
+        }
+
+        // The end of the code is a branch target sentinel.
         instructionMarks[codeLength] = BRANCH_TARGET;
 
         // Mark branch targets by going over all instructions.
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
+        codeAttribute.instructionsAccept(clazz, method, this);
 
         // Mark branch targets in the exception table.
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
+        codeAttribute.exceptionsAccept(clazz, method, this);
+
+        // Fill out any gaps in the subroutine starts and the subroutine ends
+        // and subroutine returning flags, working backward.
+
+        // We're not starting in a subroutine.
+        int     subroutineStart     = NONE;
+        int     subroutineEnd       = codeLength;
+        boolean subroutineReturning = false;
 
-        // Fill out the subroutine end array.
-        int subroutineEnd = codeLength - 1;
         for (int index = codeLength - 1; index >= 0; index--)
         {
-            // Remember the most recent subroutine end.
-            if (isSubroutineEnd(index))
+            if (isInstruction(index))
             {
-                subroutineEnd = index;
+                // Are we inside a previously marked subroutine?
+                if (subroutineStarts[index] != NONE)
+                {
+                    // Update the current subroutine start.
+                    subroutineStart = subroutineStarts[index];
+                }
+                else if (subroutineStart != NONE)
+                {
+                    // Mark the subroutine start.
+                    subroutineStarts[index] = subroutineStart;
+                }
+
+                // Did we reach the start of the subroutine.
+                if (isSubroutineStart(index))
+                {
+                    // Stop marking it.
+                    subroutineStart = NONE;
+                }
+
+                // Are we inside a subroutine?
+                if (isSubroutine(index))
+                {
+                    // Mark the subroutine end.
+                    subroutineEnds[index] = subroutineEnd;
+
+                    // Update or mark the subroutine returning flag.
+                    if (isSubroutineReturning(index))
+                    {
+                        subroutineReturning = true;
+                    }
+                    else if (subroutineReturning)
+                    {
+                        instructionMarks[index] |= SUBROUTINE_RETURNING;
+                    }
+                }
+                else
+                {
+                    // Update the subroutine end and returning flag.
+                    subroutineEnd       = index;
+                    subroutineReturning = false;
+                }
             }
+        }
 
-            // Fill out the most recent subroutine end.
-            subroutineEnds[index] = subroutineEnd;
+        if (DEBUG)
+        {
+            System.out.println();
+            System.out.println("Branch targets: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+
+            for (int index = 0; index < codeLength; index++)
+            {
+                if (isInstruction(index))
+                {
+                    System.out.println("" +
+                                       (isBranchOrigin(index)         ? 'B' : '-') +
+                                       (isBranchTarget(index)         ? 'T' : '-') +
+                                       (isExceptionStart(index)       ? 'E' : '-') +
+                                       (isExceptionEnd(index)         ? 'E' : '-') +
+                                       (isExceptionHandler(index)     ? 'H' : '-') +
+                                       (isSubroutineInvocation(index) ? 'J' : '-') +
+                                       (isSubroutineStart(index)      ? 'S' : '-') +
+                                       (isSubroutineReturning(index)  ? 'r' : '-') +
+                                       (isSubroutine(index)           ? " ["+subroutineStart(index)+" -> "+subroutineEnd(index)+"] " : " ") +
+                                       InstructionFactory.create(codeAttribute.code, index).toString(index));
+                }
+            }
         }
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         // Mark the instruction.
         instructionMarks[offset] |= INSTRUCTION;
 
-        short opcode = simpleInstruction.opcode;
+        // Check if this is the first instruction of a subroutine.
+        checkSubroutine(offset);
+
+        byte opcode = simpleInstruction.opcode;
         if (opcode == InstructionConstants.OP_IRETURN ||
             opcode == InstructionConstants.OP_LRETURN ||
             opcode == InstructionConstants.OP_FRETURN ||
@@ -268,115 +459,148 @@ implements   AttrInfoVisitor,
             opcode == InstructionConstants.OP_ARETURN ||
             opcode == InstructionConstants.OP_ATHROW)
         {
-            instructionMarks[offset] |= BRANCH_ORIGIN;
+            // Mark the branch origin.
+            markBranchOrigin(offset);
+
+            // Mark the next instruction.
+            markAfterBranchOrigin(offset + simpleInstruction.length(offset));
         }
     }
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
         // Mark the instruction.
         instructionMarks[offset] |= INSTRUCTION;
 
-        // Check if the instruction is an initializer invocation.
-        isInitializer = false;
-        classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        if (isInitializer)
+        // Check if this is the first instruction of a subroutine.
+        checkSubroutine(offset);
+
+        // Check if the instruction is a 'new' instruction.
+        if (constantInstruction.opcode == InstructionConstants.OP_NEW)
         {
-            instructionMarks[offset] |= INITIALIZER;
+            // Push the 'new' instruction offset on the stack.
+            recentCreationOffsets[recentCreationOffsetIndex++] = offset;
+        }
+        else
+        {
+            // Check if the instruction is an initializer invocation.
+            isInitializer = false;
+            clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+            if (isInitializer)
+            {
+                // Pop the 'new' instruction offset from the stack.
+                int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex];
+
+                // Fill it out in the creation offsets.
+                creationOffsets[offset] = recentCreationOffset;
+
+                // Fill out the initialization offsets.
+                if (recentCreationOffset == AT_METHOD_ENTRY)
+                {
+                    superInitializationOffset = offset;
+                }
+                else
+                {
+                    initializationOffsets[recentCreationOffset] = offset;
+                }
+            }
         }
     }
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         // Mark the instruction.
         instructionMarks[offset] |= INSTRUCTION;
 
+        // Check if this is the first instruction of a subroutine.
+        checkSubroutine(offset);
+
         if (variableInstruction.opcode == InstructionConstants.OP_RET)
         {
             // Mark the branch origin.
-            instructionMarks[offset] |= BRANCH_ORIGIN | SUBROUTINE_END;
+            markBranchOrigin(offset);
+
+            // Mark the regular subroutine return.
+            instructionMarks[offset] |= SUBROUTINE_RETURNING;
+
+            // Mark the next instruction.
+            markAfterBranchOrigin(offset + variableInstruction.length(offset));
         }
     }
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
-        // Mark the instruction.
-        instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+        // Mark the branch origin.
+        markBranchOrigin(offset);
+
+        // Check if this is the first instruction of a subroutine.
+        checkSubroutine(offset);
 
         // Mark the branch target.
-        instructionMarks[offset + branchInstruction.branchOffset] |= BRANCH_TARGET;
+        markBranchTarget(offset, branchInstruction.branchOffset);
 
         byte opcode = branchInstruction.opcode;
         if (opcode == InstructionConstants.OP_JSR ||
             opcode == InstructionConstants.OP_JSR_W)
         {
+            // Mark the subroutine invocation.
+            instructionMarks[offset] |= SUBROUTINE_INVOCATION;
+
             // Mark the subroutine start.
-            instructionMarks[offset + branchInstruction.branchOffset] |= SUBROUTINE_START;
+            int targetOffset = offset + branchInstruction.branchOffset;
+            subroutineStarts[targetOffset] = targetOffset;
+        }
+        else if (opcode == InstructionConstants.OP_GOTO ||
+                 opcode == InstructionConstants.OP_GOTO_W)
+        {
+            // Mark the next instruction.
+            markAfterBranchOrigin(offset + branchInstruction.length(offset));
         }
     }
 
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction)
-    {
-        // Mark the instruction.
-        instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
-
-        // Mark the branch targets of the default jump offset.
-        instructionMarks[offset + tableSwitchInstruction.defaultOffset] |= BRANCH_TARGET;
 
-        // Mark the branch targets of the jump offsets.
-        markBranchTargets(offset,
-                          tableSwitchInstruction.jumpOffsets,
-                          tableSwitchInstruction.highCase -
-                          tableSwitchInstruction.lowCase + 1);
-    }
-
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
     {
-        // Mark the instruction.
-        instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+        // Mark the branch origin.
+        markBranchOrigin(offset);
+
+        // Check if this is the first instruction of a subroutine.
+        checkSubroutine(offset);
 
         // Mark the branch targets of the default jump offset.
-        instructionMarks[offset + lookUpSwitchInstruction.defaultOffset] |= BRANCH_TARGET;
+        markBranchTarget(offset, switchInstruction.defaultOffset);
 
         // Mark the branch targets of the jump offsets.
         markBranchTargets(offset,
-                          lookUpSwitchInstruction.jumpOffsets,
-                          lookUpSwitchInstruction.jumpOffsetCount);
+                          switchInstruction.jumpOffsets);
+
+        // Mark the next instruction.
+        markAfterBranchOrigin(offset + switchInstruction.length(offset));
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
-        isInitializer = methodrefCpInfo.getName(classFile).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
+        isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT);
     }
 
 
     // Implementations for ExceptionInfoVisitor.
 
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
-        // Remap the code offsets. Note that the branch target array also has
-        // an entry for the first offset after the code, for u2endpc.
-        instructionMarks[exceptionInfo.u2startpc]   |= EXCEPTION_START;
-        instructionMarks[exceptionInfo.u2endpc]     |= EXCEPTION_END;
-        instructionMarks[exceptionInfo.u2handlerpc] |= EXCEPTION_HANDLER;
+        // Mark the exception offsets.
+        instructionMarks[exceptionInfo.u2startPC]   |= EXCEPTION_START;
+        instructionMarks[exceptionInfo.u2endPC]     |= EXCEPTION_END;
+        instructionMarks[exceptionInfo.u2handlerPC] |= EXCEPTION_HANDLER;
     }
 
 
@@ -386,11 +610,79 @@ implements   AttrInfoVisitor,
      * Marks the branch targets of the given jump offsets for the instruction
      * at the given offset.
      */
-    private void markBranchTargets(int offset, int[] jumpOffsets, int length)
+    private void markBranchTargets(int offset, int[] jumpOffsets)
+    {
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            markBranchTarget(offset, jumpOffsets[index]);
+        }
+    }
+
+
+    /**
+     * Marks the branch origin at the given offset.
+     */
+    private void markBranchOrigin(int offset)
+    {
+        instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN;
+    }
+
+
+    /**
+     * Marks the branch target at the given offset.
+     */
+    private void markBranchTarget(int offset, int jumpOffset)
     {
-        for (int index = 0; index < length; index++)
+        int targetOffset = offset + jumpOffset;
+
+        instructionMarks[targetOffset] |= BRANCH_TARGET;
+
+        // Are we inside a previously marked subroutine?
+        if (isSubroutine(offset))
+        {
+            // Mark the subroutine start of the target.
+            subroutineStarts[targetOffset] = currentSubroutineStart;
+
+            // Update the current subroutine end.
+            if (currentSubroutineEnd < targetOffset)
+            {
+                currentSubroutineEnd = targetOffset;
+            }
+        }
+    }
+
+
+    /**
+     * Marks the instruction at the given offset, after a branch.
+     */
+    private void markAfterBranchOrigin(int nextOffset)
+    {
+        instructionMarks[nextOffset] |= AFTER_BRANCH;
+
+        // Are we at the end of the current subroutine?
+        if (currentSubroutineEnd <= nextOffset)
+        {
+            // Reset the subroutine start.
+            currentSubroutineStart = NONE;
+        }
+    }
+
+
+    /**
+     * Checks if the specified instruction is inside a subroutine.
+     */
+    private void checkSubroutine(int offset)
+    {
+        // Are we inside a previously marked subroutine?
+        if (isSubroutine(offset))
+        {
+            // Update the current subroutine start.
+            currentSubroutineStart = subroutineStarts[offset];
+        }
+        else
         {
-            instructionMarks[offset + jumpOffsets[index]] |= BRANCH_TARGET;
+            // Mark the subroutine start (or NONE).
+            subroutineStarts[offset] = currentSubroutineStart;
         }
     }
 }
diff --git a/src/proguard/optimize/peephole/ClassFileFinalizer.java b/src/proguard/optimize/peephole/ClassFileFinalizer.java
deleted file mode 100644
index ee7d2b1..0000000
--- a/src/proguard/optimize/peephole/ClassFileFinalizer.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/* $Id: ClassFileFinalizer.java,v 1.8.2.1 2006/02/13 00:20:43 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util.MemberFinder;
-import proguard.classfile.visitor.*;
-import proguard.optimize.*;
-
-/**
- * This <code>ClassFileVisitor</code> and <code>MemberInfoVisitor</code>
- * makes the class files it visits, and their methods, final, if possible.
- *
- * @author Eric Lafortune
- */
-public class ClassFileFinalizer
-  implements ClassFileVisitor,
-             MemberInfoVisitor
-{
-    private ClassFileVisitor  extraClassFileVisitor;
-    private MemberInfoVisitor extraMemberInfoVisitor;
-
-    private MemberFinder memberFinder = new MemberFinder();
-
-
-    /**
-     * Creates a new ClassFileFinalizer.
-     */
-    public ClassFileFinalizer()
-    {
-        this(null, null);
-    }
-
-
-    /**
-     * Creates a new ClassFileFinalizer.
-     * @param extraClassFileVisitor  an optional extra visitor for all finalized
-     *                               class files.
-     * @param extraMemberInfoVisitor an optional extra visitor for all finalized
-     *                               methods.
-     */
-    public ClassFileFinalizer(ClassFileVisitor  extraClassFileVisitor,
-                              MemberInfoVisitor extraMemberInfoVisitor)
-    {
-        this.extraClassFileVisitor  = extraClassFileVisitor;
-        this.extraMemberInfoVisitor = extraMemberInfoVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // If the class is not final/interface/abstract,
-        // and it is not being kept,
-        // then make it final.
-        if ((programClassFile.u2accessFlags & (ClassConstants.INTERNAL_ACC_FINAL     |
-                                               ClassConstants.INTERNAL_ACC_INTERFACE |
-                                               ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 &&
-            !KeepMarker.isKept(programClassFile)                                           &&
-            programClassFile.subClasses == null)
-        {
-            programClassFile.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
-
-            // Visit the class file, if required.
-            if (extraClassFileVisitor != null)
-            {
-                extraClassFileVisitor.visitProgramClassFile(programClassFile);
-            }
-        }
-
-        // Check all methods.
-        programClassFile.methodsAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile) {}
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        String name = programMethodInfo.getName(programClassFile);
-
-        // 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 ((programMethodInfo.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)                         &&
-            ((programClassFile.u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0 ||
-             (!KeepMarker.isKept(programMethodInfo) &&
-              (programClassFile.subClasses == null ||
-               !memberFinder.isOverriden(programClassFile, programMethodInfo)))))
-        {
-            programMethodInfo.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL;
-
-            // Visit the method, if required.
-            if (extraMemberInfoVisitor != null)
-            {
-                extraMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-            }
-        }
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-}
diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/src/proguard/optimize/peephole/ClassFinalizer.java
new file mode 100644
index 0000000..3f41efb
--- /dev/null
+++ b/src/proguard/optimize/peephole/ClassFinalizer.java
@@ -0,0 +1,127 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2003 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is 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.util.*;
+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.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFinalizer
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor
+{
+    private final ClassVisitor  extraClassVisitor;
+    private final MemberVisitor extraMemberVisitor;
+
+    private final MemberFinder memberFinder = new MemberFinder();
+
+
+    /**
+     * Creates a new ClassFinalizer.
+     */
+    public ClassFinalizer()
+    {
+        this(null, 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)
+    {
+        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);
+
+        // 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/GetterSetterInliner.java b/src/proguard/optimize/peephole/GetterSetterInliner.java
deleted file mode 100644
index 5fc77fc..0000000
--- a/src/proguard/optimize/peephole/GetterSetterInliner.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/* $Id: GetterSetterInliner.java,v 1.18.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.peephole;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.util.*;
-import proguard.classfile.editor.*;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-/**
- * This InstructionVisitor inlines simple getter and setter methods.
- *
- * @author Eric Lafortune
- */
-public class GetterSetterInliner
-implements   InstructionVisitor,
-             CpInfoVisitor
-{
-    private static final String SETTER_RETURN_TYPE = "V";
-
-    private ConstantPoolEditor constantPoolEditor  = new ConstantPoolEditor();
-    private MemberInfoVisitor  getterSetterChecker = new AllAttrInfoVisitor(
-                                                     new MyGetterSetterChecker());
-    private MemberFinder       memberFinder        = new MemberFinder();
-
-    private boolean            allowAccessModification;
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
-
-
-    // Return values of the getter/setter checker.
-    private byte       getFieldPutFieldOpcode;
-    private int        referencedFieldIndex;
-    private ClassFile  referencedClassFile;
-    private MemberInfo referencedFieldInfo;
-
-
-    /**
-     * Creates a new GetterSetterInliner.
-     * @param allowAccessModification indicates whether the access modifiers of
-     *                                a field can be changed in order to inline
-     *                                its getter or setter.
-     * @param codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
-     */
-    public GetterSetterInliner(boolean            allowAccessModification,
-                               CodeAttrInfoEditor codeAttrInfoEditor)
-    {
-        this(allowAccessModification, codeAttrInfoEditor, null);
-    }
-
-
-    /**
-     * Creates a new GetterSetterInliner.
-     * @param allowAccessModification indicates whether the access modifiers of
-     *                                a field can be changed in order to inline
-     *                                its getter or setter.
-     * @param codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
-     * @param extraInstructionVisitor an optional extra visitor for all replaced
-     *                                store instructions.
-     */
-    public GetterSetterInliner(boolean            allowAccessModification,
-                               CodeAttrInfoEditor codeAttrInfoEditor,
-                               InstructionVisitor extraInstructionVisitor)
-    {
-        this.allowAccessModification = allowAccessModification;
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
-        this.extraInstructionVisitor = extraInstructionVisitor;
-    }
-
-
-    // Implementations for InstructionVisitor.
-
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-    {
-        byte opcode = cpInstruction.opcode;
-
-        // Is this instruction a non-static invoke instruction?
-        if (opcode == InstructionConstants.OP_INVOKEVIRTUAL ||
-            opcode == InstructionConstants.OP_INVOKESPECIAL)
-        {
-            // Check if it's a getter or setter that can be inlined.
-            getFieldPutFieldOpcode = 0;
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-
-            // Do we have a getfield or putfield instruction to inline?
-            if (getFieldPutFieldOpcode != 0)
-            {
-                // Reuse or create the field reference in this class.
-                int fieldrefCpInfoIndex = classFile.equals(referencedClassFile) ?
-                    referencedFieldIndex :
-                    constantPoolEditor.addFieldrefCpInfo((ProgramClassFile)classFile,
-                                                         referencedClassFile.getName(),
-                                                         referencedFieldInfo.getName(referencedClassFile),
-                                                         referencedFieldInfo.getDescriptor(referencedClassFile),
-                                                         referencedClassFile,
-                                                         referencedFieldInfo);
-
-                // Inline the getfield or putfield instruction.
-                Instruction replacementInstruction = new CpInstruction(getFieldPutFieldOpcode,
-                                                                       fieldrefCpInfoIndex).shrink();
-
-                codeAttrInfoEditor.replaceInstruction(offset, replacementInstruction);
-
-                // Visit the instruction, if required.
-                if (extraInstructionVisitor != null)
-                {
-                    extraInstructionVisitor.visitCpInstruction(classFile, methodInfo, codeAttrInfo, offset, cpInstruction);
-                }
-            }
-        }
-    }
-
-
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
-    {
-        String descriptor = methodrefCpInfo.getType(classFile);
-
-        // A getter or a setter can't have more than one parameter.
-        int parameterCount = ClassUtil.internalMethodParameterCount(descriptor);
-        if (parameterCount > 1)
-        {
-            return;
-        }
-
-        // A getter must return a value, a setter must return void.
-        String returnType = ClassUtil.internalMethodReturnType(descriptor);
-        if ((parameterCount > 0) ^ returnType.equals(SETTER_RETURN_TYPE))
-        {
-            return;
-        }
-
-        // The referenced method must be present and private or final.
-        MemberInfo referencedMethodInfo = methodrefCpInfo.referencedMemberInfo;
-        if (referencedMethodInfo == null ||
-            (referencedMethodInfo.getAccessFlags() &
-             (ClassConstants.INTERNAL_ACC_PRIVATE |
-              ClassConstants.INTERNAL_ACC_FINAL)) == 0)
-        {
-            return;
-        }
-
-        // Check if the method can be inlined.
-        referencedMethodInfo.accept(methodrefCpInfo.referencedClassFile,
-                                    getterSetterChecker);
-
-        // Do we have a getfield or putfield instruction to inline?
-        if (getFieldPutFieldOpcode == 0)
-        {
-            return;
-        }
-
-        // Doesn't the field allow at least the same access as the getter or
-        // setter?
-        if (AccessUtil.accessLevel(referencedFieldInfo.getAccessFlags()) <
-            AccessUtil.accessLevel(referencedMethodInfo.getAccessFlags()))
-        {
-            // Are we allowed to fix the access?
-            if (allowAccessModification)
-            {
-                // Is the field access private, and is the field shadowed by
-                // a non-private field in a subclass?
-                if (AccessUtil.accessLevel(referencedFieldInfo.getAccessFlags()) == AccessUtil.PRIVATE &&
-                    memberFinder.isShadowed(referencedClassFile, (FieldInfo)referencedFieldInfo))
-                {
-                    // Cancel the inlining.
-                    getFieldPutFieldOpcode = 0;
-                }
-
-                if (getFieldPutFieldOpcode != 0)
-                {
-                    // Fix the access.
-                    ((ProgramFieldInfo)referencedFieldInfo).u2accessFlags =
-                        AccessUtil.replaceAccessFlags(referencedFieldInfo.getAccessFlags(),
-                                                      referencedMethodInfo.getAccessFlags());
-                }
-            }
-            else
-            {
-                // We can't give the field the access of the getter or setter.
-                // Forget about inlining it after all.
-                getFieldPutFieldOpcode = 0;
-            }
-        }
-    }
-
-
-    /**
-     * This MemberInfoVisitor checks whether the visited member is a simple
-     * getter or setter.
-     */
-    private class MyGetterSetterChecker
-    implements    AttrInfoVisitor,
-                  InstructionVisitor,
-                  CpInfoVisitor
-    {
-        // Implementations for AttrInfoVisitor.
-
-        public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-        public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-        public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-        public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-        public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-        public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-        public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-        public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-        public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-        public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-        public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-        public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-        public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-        public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-        public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-        public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-        public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-        public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-        public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-        {
-            byte[] code = codeAttrInfo.code;
-
-            int         offset = 0;
-            Instruction instruction;
-
-            // TODO: Handle static getters and setters.
-
-            // Check the first instruction.
-            instruction = InstructionFactory.create(code, offset);
-            if (instruction.opcode != InstructionConstants.OP_ALOAD_0)
-            {
-                return;
-            }
-
-            offset += instruction.length(offset);
-
-            // Start remembering if this is a getter or a setter.
-            boolean getter = true;
-
-            // TODO: Handle array getters and setters.
-
-            // Check the second instruction.
-            instruction = InstructionFactory.create(code, offset);
-            byte opcode = instruction.opcode;
-            if (opcode == InstructionConstants.OP_ILOAD_1 ||
-                opcode == InstructionConstants.OP_LLOAD_1 ||
-                opcode == InstructionConstants.OP_FLOAD_1 ||
-                opcode == InstructionConstants.OP_DLOAD_1 ||
-                opcode == InstructionConstants.OP_ALOAD_1)
-            {
-                // This instruction is loading the argument. Skip it.
-                offset += instruction.length(offset);
-
-                instruction = InstructionFactory.create(code, offset);
-                opcode      = instruction.opcode;
-
-                // So this is a setter.
-                getter = false;
-            }
-
-            // Check the next instruction.
-            if (getter ? opcode != InstructionConstants.OP_GETFIELD :
-                         opcode != InstructionConstants.OP_PUTFIELD)
-            {
-                return;
-            }
-
-            // Retrieve the field class, name and type.
-            instruction.accept(classFile, methodInfo, codeAttrInfo, offset, this);
-
-            // Check the class.
-            // TODO: Handle fields that are in super classes.
-            if (!classFile.equals(referencedClassFile))
-            {
-                return;
-            }
-
-            offset += instruction.length(offset);
-
-            // Remember the instruction opcode.
-            getFieldPutFieldOpcode = opcode;
-
-            // Check the last instruction.
-            instruction = InstructionFactory.create(code, offset);
-            opcode      = instruction.opcode;
-            if (getter ? opcode != InstructionConstants.OP_IRETURN &&
-                         opcode != InstructionConstants.OP_LRETURN &&
-                         opcode != InstructionConstants.OP_FRETURN &&
-                         opcode != InstructionConstants.OP_DRETURN &&
-                         opcode != InstructionConstants.OP_ARETURN :
-                         opcode != InstructionConstants.OP_RETURN)
-            {
-                // This isn't a simple getter or setter.
-                // Forget about inlining it.
-                getFieldPutFieldOpcode = 0;
-            }
-        }
-
-
-        // Implementations for InstructionVisitor.
-
-        public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-        public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-        public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-        public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-        public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
-
-
-        public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
-        {
-            // Remember the index of the field reference.
-            referencedFieldIndex = cpInstruction.cpIndex;
-
-            // Retrieve the referenced field and its class file.
-            classFile.constantPoolEntryAccept(cpInstruction.cpIndex, this);
-        }
-
-
-        // Implementations for CpInfoVisitor.
-
-        public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-        public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-        public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-        public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-        public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-        public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-        public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-        public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-        public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-        public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
-
-        public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
-        {
-            // Remember the class file and the field.
-            referencedClassFile = fieldrefCpInfo.referencedClassFile;
-            referencedFieldInfo = fieldrefCpInfo.referencedMemberInfo;
-        }
-    }
-}
diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
index 7cd8c9a..d699c4e 100644
--- a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
+++ b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java
@@ -1,6 +1,6 @@
-/* $Id: GotoCommonCodeReplacer.java,v 1.2.2.6 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,91 +21,100 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.editor.CodeAttrInfoEditor;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
- * This InstructionVisitor redirects unconditional branches so any common code
- * is shared, and the code preceding the branch can be removed.
+ * This AttributeVisitor redirects unconditional branches so any common code
+ * is shared, and the code preceding the branch can be removed, in the code
+ * attributes that it visits.
  *
  * @author Eric Lafortune
  */
-public class GotoCommonCodeReplacer implements InstructionVisitor
+public class GotoCommonCodeReplacer
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor
 {
+    //*
     private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
 
 
-    private BranchTargetFinder branchTargetFinder;
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
-
-    /**
-     * Creates a new GotoCommonCodeReplacer.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
-     */
-    public GotoCommonCodeReplacer(BranchTargetFinder branchTargetFinder,
-                                  CodeAttrInfoEditor codeAttrInfoEditor)
-    {
-        this(branchTargetFinder, codeAttrInfoEditor, null);
-    }
+    private final BranchTargetFinder  branchTargetFinder  = new BranchTargetFinder();
+    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
 
 
     /**
      * Creates a new GotoCommonCodeReplacer.
-     * @param branchTargetFinder      a branch target finder that has been
-     *                                initialized to indicate branch targets
-     *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
      * @param extraInstructionVisitor an optional extra visitor for all replaced
      *                                goto instructions.
      */
-    public GotoCommonCodeReplacer(BranchTargetFinder branchTargetFinder,
-                                  CodeAttrInfoEditor codeAttrInfoEditor,
-                                  InstructionVisitor extraInstructionVisitor)
+    public GotoCommonCodeReplacer(InstructionVisitor  extraInstructionVisitor)
     {
-        this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        // Mark all branch targets.
+        branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Reset the code attribute editor.
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
+
+        // Remap the variables of the instructions.
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        // Apply the code atribute editor.
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+    }
+
+
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         // Check if the instruction is an unconditional goto instruction that
         // isn't the target of a branch itself.
         byte opcode = branchInstruction.opcode;
         if ((opcode == InstructionConstants.OP_GOTO ||
              opcode == InstructionConstants.OP_GOTO_W) &&
-             !branchTargetFinder.isBranchTarget(offset))
+            !branchTargetFinder.isBranchTarget(offset))
         {
             int branchOffset = branchInstruction.branchOffset;
             int targetOffset = offset + branchOffset;
 
             // Get the number of common bytes.
-            int commonCount = commonByteCodeCount(codeAttrInfo, offset, targetOffset);
+            int commonCount = commonByteCodeCount(codeAttribute, offset, targetOffset);
 
             if (commonCount > 0 &&
-                !exceptionBoundary(codeAttrInfo, offset, targetOffset))
+                !exceptionBoundary(codeAttribute, offset, targetOffset))
             {
                 if (DEBUG)
                 {
-                    System.out.println("GotoCommonCodeReplacer: "+classFile.getName()+"."+methodInfo.getName(classFile)+" ("+commonCount+" instructions)");
+                    System.out.println("GotoCommonCodeReplacer: "+clazz.getName()+"."+method.getName(clazz)+" (["+(offset-commonCount)+"] - "+branchInstruction.toString(offset)+" -> "+targetOffset+")");
                 }
 
                 // Delete the common instructions.
@@ -114,11 +123,11 @@ public class GotoCommonCodeReplacer implements InstructionVisitor
                     int deleteOffset = offset - delta;
                     if (branchTargetFinder.isInstruction(deleteOffset))
                     {
-                        codeAttrInfoEditor.replaceInstruction(     deleteOffset, null);
-                        codeAttrInfoEditor.insertBeforeInstruction(deleteOffset, null);
-                        codeAttrInfoEditor.insertAfterInstruction( deleteOffset, null);
+                        codeAttributeEditor.replaceInstruction(     deleteOffset, null);
+                        codeAttributeEditor.insertBeforeInstruction(deleteOffset, null);
+                        codeAttributeEditor.insertAfterInstruction( deleteOffset, null);
 
-                        codeAttrInfoEditor.deleteInstruction(deleteOffset);
+                        codeAttributeEditor.deleteInstruction(deleteOffset);
                     }
                 }
 
@@ -128,14 +137,14 @@ public class GotoCommonCodeReplacer implements InstructionVisitor
                 {
                     Instruction newGotoInstruction =
                          new BranchInstruction(opcode, newBranchOffset);
-                    codeAttrInfoEditor.replaceInstruction(offset,
-                                                          newGotoInstruction);
+                    codeAttributeEditor.replaceInstruction(offset,
+                                                           newGotoInstruction);
                 }
 
                 // Visit the instruction, if required.
                 if (extraInstructionVisitor != null)
                 {
-                    extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+                    extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
                 }
             }
         }
@@ -148,10 +157,10 @@ public class GotoCommonCodeReplacer implements InstructionVisitor
      * Returns the number of common bytes preceding the given offsets,
      * avoiding branches and exception blocks.
      */
-    private int commonByteCodeCount(CodeAttrInfo codeAttrInfo, int offset1, int offset2)
+    private int commonByteCodeCount(CodeAttribute codeAttribute, int offset1, int offset2)
     {
         // Find the block of common instructions preceding it.
-        byte[] code = codeAttrInfo.code;
+        byte[] code = codeAttribute.code;
 
         int successfulDelta = 0;
 
@@ -232,7 +241,7 @@ public class GotoCommonCodeReplacer implements InstructionVisitor
      * Returns the whether there is a boundary of an exception block between
      * the given offsets (including both).
      */
-    private boolean exceptionBoundary(CodeAttrInfo codeAttrInfo, int offset1, int offset2)
+    private boolean exceptionBoundary(CodeAttribute codeAttribute, int offset1, int offset2)
     {
         // Swap the offsets if the second one is smaller than the first one.
         if (offset2 < offset1)
diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java
index d2982f9..d2de2d0 100644
--- a/src/proguard/optimize/peephole/GotoGotoReplacer.java
+++ b/src/proguard/optimize/peephole/GotoGotoReplacer.java
@@ -1,6 +1,6 @@
-/* $Id: GotoGotoReplacer.java,v 1.2.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,11 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.editor.*;
+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 simplifies unconditional branches to other
@@ -31,48 +33,46 @@ import proguard.classfile.instruction.*;
  *
  * @author Eric Lafortune
  */
-public class GotoGotoReplacer implements InstructionVisitor
+public class GotoGotoReplacer
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
 
     /**
      * Creates a new GotoGotoReplacer.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      */
-    public GotoGotoReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
+    public GotoGotoReplacer(CodeAttributeEditor codeAttributeEditor)
     {
-        this(codeAttrInfoEditor, null);
+        this(codeAttributeEditor, null);
     }
 
 
     /**
      * Creates a new GotoGotoReplacer.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      * @param extraInstructionVisitor an optional extra visitor for all replaced
      *                                goto instructions.
      */
-    public GotoGotoReplacer(CodeAttrInfoEditor codeAttrInfoEditor,
-                            InstructionVisitor extraInstructionVisitor)
+    public GotoGotoReplacer(CodeAttributeEditor codeAttributeEditor,
+                            InstructionVisitor  extraInstructionVisitor)
     {
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         // Check if the instruction is an unconditional goto instruction.
         byte opcode = branchInstruction.opcode;
@@ -85,26 +85,27 @@ public class GotoGotoReplacer implements InstructionVisitor
             int targetOffset = offset + branchOffset;
 
             if (branchOffset != branchInstruction.length(offset) &&
-                !codeAttrInfoEditor.isModified(offset) &&
-                !codeAttrInfoEditor.isModified(targetOffset))
+                !codeAttributeEditor.isModified(offset) &&
+                !codeAttributeEditor.isModified(targetOffset))
             {
-                Instruction targetInstruction = InstructionFactory.create(codeAttrInfo.code,
-                                                                          targetOffset);
+                Instruction targetInstruction =
+                    InstructionFactory.create(codeAttribute.code, targetOffset);
+
                 if (targetInstruction.opcode == InstructionConstants.OP_GOTO)
                 {
                     // Simplify the goto instruction.
-                    int targetBranchOffset   = ((BranchInstruction)targetInstruction).branchOffset;
+                    int targetBranchOffset = ((BranchInstruction)targetInstruction).branchOffset;
 
                     Instruction newBranchInstruction =
                          new BranchInstruction(opcode,
                                                (branchOffset + targetBranchOffset));
-                    codeAttrInfoEditor.replaceInstruction(offset,
-                                                          newBranchInstruction);
+                    codeAttributeEditor.replaceInstruction(offset,
+                                                           newBranchInstruction);
 
                     // Visit the instruction, if required.
                     if (extraInstructionVisitor != null)
                     {
-                        extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+                        extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
                     }
                 }
             }
diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java
index ae3fbb1..cdae3e8 100644
--- a/src/proguard/optimize/peephole/GotoReturnReplacer.java
+++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java
@@ -1,6 +1,6 @@
-/* $Id: GotoReturnReplacer.java,v 1.8.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,11 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.editor.*;
+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 unconditional branches to return instructions
@@ -31,48 +33,46 @@ import proguard.classfile.instruction.*;
  *
  * @author Eric Lafortune
  */
-public class GotoReturnReplacer implements InstructionVisitor
+public class GotoReturnReplacer
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
 
     /**
      * Creates a new GotoReturnReplacer.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      */
-    public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor)
+    public GotoReturnReplacer(CodeAttributeEditor codeAttributeEditor)
     {
-        this(codeAttrInfoEditor, null);
+        this(codeAttributeEditor, null);
     }
 
 
     /**
      * Creates a new GotoReturnReplacer.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      * @param extraInstructionVisitor an optional extra visitor for all replaced
      *                                goto instructions.
      */
-    public GotoReturnReplacer(CodeAttrInfoEditor codeAttrInfoEditor,
-                              InstructionVisitor extraInstructionVisitor)
+    public GotoReturnReplacer(CodeAttributeEditor codeAttributeEditor,
+                              InstructionVisitor  extraInstructionVisitor)
     {
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction)
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
     {
         // Check if the instruction is an unconditional goto instruction.
         byte opcode = branchInstruction.opcode;
@@ -82,10 +82,10 @@ public class GotoReturnReplacer implements InstructionVisitor
             // Check if the goto instruction points to a return instruction.
             int targetOffset = offset + branchInstruction.branchOffset;
 
-            if (!codeAttrInfoEditor.isModified(offset) &&
-                !codeAttrInfoEditor.isModified(targetOffset))
+            if (!codeAttributeEditor.isModified(offset) &&
+                !codeAttributeEditor.isModified(targetOffset))
             {
-                Instruction targetInstruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction targetInstruction = InstructionFactory.create(codeAttribute.code,
                                                                           targetOffset);
                 switch (targetInstruction.opcode)
                 {
@@ -98,13 +98,13 @@ public class GotoReturnReplacer implements InstructionVisitor
                         // Replace the goto instruction by the return instruction.
                         Instruction returnInstruction =
                              new SimpleInstruction(targetInstruction.opcode);
-                        codeAttrInfoEditor.replaceInstruction(offset,
-                                                              returnInstruction);
+                        codeAttributeEditor.replaceInstruction(offset,
+                                                               returnInstruction);
 
                         // Visit the instruction, if required.
                         if (extraInstructionVisitor != null)
                         {
-                            extraInstructionVisitor.visitBranchInstruction(classFile, methodInfo, codeAttrInfo, offset, branchInstruction);
+                            extraInstructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
                         }
 
                         break;
diff --git a/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
new file mode 100644
index 0000000..e340e02
--- /dev/null
+++ b/src/proguard/optimize/peephole/InstructionSequenceConstants.java
@@ -0,0 +1,2870 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.constant.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.InstructionSequenceMatcher;
+
+/**
+ * This class contains a set of instruction sequences and their suggested
+ * replacements.
+ *
+ * @see InstructionSequencesReplacer
+ * @author Eric Lafortune
+ */
+public class InstructionSequenceConstants
+{
+    public static final int X = InstructionSequenceMatcher.X;
+    public static final int Y = InstructionSequenceMatcher.Y;
+    public static final int Z = InstructionSequenceMatcher.Z;
+
+    public static final int A = InstructionSequenceMatcher.A;
+    public static final int B = InstructionSequenceMatcher.B;
+    public static final int C = InstructionSequenceMatcher.C;
+    public static final int D = InstructionSequenceMatcher.D;
+
+
+    private static final int I_32768              =  0;
+    private static final int I_65536              =  1;
+    private static final int I_16777216           =  2;
+
+//  private static final int I_0x000000ff
+    private static final int I_0x0000ff00         =  3;
+    private static final int I_0x00ff0000         =  4;
+    private static final int I_0xff000000         =  5;
+    private static final int I_0x0000ffff         =  6;
+    private static final int I_0xffff0000         =  7;
+
+    private static final int L_M1                 =  8;
+    private static final int L_2                  =  9;
+    private static final int L_4                  = 10;
+    private static final int L_8                  = 11;
+    private static final int L_16                 = 12;
+    private static final int L_32                 = 13;
+    private static final int L_64                 = 14;
+    private static final int L_128                = 15;
+    private static final int L_256                = 16;
+    private static final int L_512                = 17;
+    private static final int L_1024               = 18;
+    private static final int L_2048               = 19;
+    private static final int L_4096               = 20;
+    private static final int L_8192               = 21;
+    private static final int L_16384              = 22;
+    private static final int L_32768              = 23;
+    private static final int L_65536              = 24;
+    private static final int L_16777216           = 25;
+    private static final int L_4294967296         = 26;
+
+    private static final int L_0x00000000ffffffff = 27;
+    private static final int L_0xffffffff00000000 = 28;
+
+    private static final int F_M1                 = 29;
+
+    private static final int D_M1                 = 30;
+
+    private static final int FIELD_I              = 31;
+    private static final int FIELD_L              = 32;
+    private static final int FIELD_F              = 33;
+    private static final int FIELD_D              = 34;
+
+    private static final int NAME_AND_TYPE_I      = 35;
+    private static final int NAME_AND_TYPE_L      = 36;
+    private static final int NAME_AND_TYPE_F      = 37;
+    private static final int NAME_AND_TYPE_D      = 38;
+
+    private static final int TYPE_I               = 39;
+    private static final int TYPE_L               = 40;
+    private static final int TYPE_F               = 41;
+    private static final int TYPE_D               = 42;
+
+
+    public static final Constant[] PATTERN_CONSTANTS = new Constant[]
+    {
+        new IntegerConstant(32768),
+        new IntegerConstant(65536),
+        new IntegerConstant(16777216),
+
+        new IntegerConstant(0x0000ff00),
+        new IntegerConstant(0x00ff0000),
+        new IntegerConstant(0xff000000),
+        new IntegerConstant(0x0000ffff),
+        new IntegerConstant(0xffff0000),
+
+        new LongConstant(-1L),
+        new LongConstant(2L),
+        new LongConstant(4L),
+        new LongConstant(8L),
+        new LongConstant(16L),
+        new LongConstant(32L),
+        new LongConstant(64L),
+        new LongConstant(128L),
+        new LongConstant(256L),
+        new LongConstant(512L),
+        new LongConstant(1024L),
+        new LongConstant(2048L),
+        new LongConstant(4096L),
+        new LongConstant(8192L),
+        new LongConstant(16384L),
+        new LongConstant(32768L),
+        new LongConstant(65536L),
+        new LongConstant(16777216L),
+        new LongConstant(4294967296L),
+
+        new LongConstant(0x00000000ffffffffL),
+        new LongConstant(0xffffffff00000000L),
+
+        new FloatConstant(-1f),
+
+        new DoubleConstant(-1d),
+
+        new FieldrefConstant(X, NAME_AND_TYPE_I, null, null),
+        new FieldrefConstant(X, NAME_AND_TYPE_L, null, null),
+        new FieldrefConstant(X, NAME_AND_TYPE_F, null, null),
+        new FieldrefConstant(X, NAME_AND_TYPE_D, null, null),
+
+        new NameAndTypeConstant(Y, TYPE_I),
+        new NameAndTypeConstant(Y, TYPE_L),
+        new NameAndTypeConstant(Y, TYPE_F),
+        new NameAndTypeConstant(Y, TYPE_D),
+
+        new Utf8Constant("I"),
+        new Utf8Constant("J"),
+        new Utf8Constant("F"),
+        new Utf8Constant("D"),
+    };
+
+
+    public static final Instruction[][][] INSTRUCTION_SEQUENCES =
+        new Instruction[][][]
+    {
+        {   // nop = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_NOP),
+            },{
+                // Nothing.
+            },
+        },
+        {   // i = i = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // l = l = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // f = f = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // d = d = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // a = a = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // istore/istore = pop/istore
+            {
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },
+        },
+        {   // lstore/lstore = pop2/lstore
+            {
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+            },
+        },
+        {   // fstore/fstore = pop/fstore
+            {
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+            },
+        },
+        {   // dstore/dstore = pop2/dstore
+            {
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+            },
+        },
+        {   // astore/astore = pop/astore
+            {
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+            },
+        },
+        {   // istore/iload = dup/istore
+            {
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },
+        },
+        {   // lstore/lload = dup2/lstore
+            {
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP2),
+                new VariableInstruction(InstructionConstants.OP_LSTORE, X),
+            },
+        },
+        {   // fstore/fload = dup/fstore
+            {
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP),
+                new VariableInstruction(InstructionConstants.OP_FSTORE, X),
+            },
+        },
+        {   // dstore/dload = dup2/dstore
+            {
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP2),
+                new VariableInstruction(InstructionConstants.OP_DSTORE, X),
+            },
+        },
+        {   // astore/aload = dup/astore
+            {
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP),
+                new VariableInstruction(InstructionConstants.OP_ASTORE, X),
+            },
+        },
+        {   // c + i = i + c
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },
+        },
+        {   // b + i = i + b
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },
+        },
+        {   // s + i = i + s
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },
+        },
+        {   // c + i = i + c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },
+        },
+        {   // c * i = i * c
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },
+        },
+        {   // b * i = i * b
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },
+        },
+        {   // s * i = i * s
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },
+        },
+        {   // c * i = i * c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },
+        },
+        {   // c + l = l + c
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },
+        },
+        {   // c + l = l + c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },
+        },
+        {   // c * l = l * c
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_LLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },
+        },
+        {   // c + f = f + c
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },
+        },
+        {   // c + f = f + c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },
+        },
+        {   // c * f = f * c
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_FMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_FMUL),
+            },
+        },
+        {   // c * f = f * c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_FLOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC, A),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },
+        },
+        {   // c + d = d + c
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },
+        },
+        {   // c + d = d + c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },{
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },
+        },
+        {   // c * d = d * c
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0, A),
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },
+        },
+        {   // c * d = d * c
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },{
+                new VariableInstruction(InstructionConstants.OP_DLOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, A),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },
+        },
+        {   // i = i + c = i += c
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, A),
+            },
+        },
+        {   // i = i + b = i += b
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, A),
+            },
+        },
+        {   // i = i + s = i += s
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, A),
+            },
+        },
+        {   // i = i - -1 = i++
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, 1),
+            },
+        },
+        {   // i = i - 1 = i--
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, -1),
+            },
+        },
+        {   // i = i - 2 = i -= 2
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, -2),
+            },
+        },
+        {   // i = i - 3 = i -= 3
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, -3),
+            },
+        },
+        {   // i = i - 4 = i -= 4
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_4),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, -4),
+            },
+        },
+        {   // i = i - 5 = i -= 5
+            {
+                new VariableInstruction(InstructionConstants.OP_ILOAD, X),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_5),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+                new VariableInstruction(InstructionConstants.OP_ISTORE, X),
+            },{
+                new VariableInstruction(InstructionConstants.OP_IINC, X, -5),
+            },
+        },
+        {   // ... + 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... + 0L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... + 0f = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... + 0d = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... - 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... - 0L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LSUB),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... - 0f = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_FSUB),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... - 0d = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_DSUB),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... * -1 = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_INEG),
+            },
+        },
+        {   // ... * 0 = 0
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+            },
+        },
+        {   // ... * 1 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... * 2 = ... << 1
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 4 = ... << 2
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_4),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 8 = ... << 3
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 16 = ... << 4
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 4),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 32 = ... << 5
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 5),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 64 = ... << 6
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 64),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 6),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 128 = ... << 7
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 128),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 7),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 256 = ... << 8
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 256),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 512 = ... << 9
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 512),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 9),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 1024 = ... << 10
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 1024),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 10),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 2048 = ... << 11
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 2048),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 11),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 4096 = ... << 12
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 4096),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 12),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 8192 = ... << 13
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 8192),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 13),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 16384 = ... << 14
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 16384),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 14),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 32768 = ... << 15
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_32768),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 15),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 65536 = ... << 16
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_65536),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * 16777216 = ... << 24
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_16777216),
+                new SimpleInstruction(InstructionConstants.OP_IMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },
+        },
+        {   // ... * -1L = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_M1),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_LNEG),
+            },
+        },
+        {   // ... * 0L = 0L
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+            },
+        },
+        {   // ... * 1L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... * 2L = ... << 1
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_2),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 4L = ... << 2
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 8L = ... << 3
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_8),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 16L = ... << 4
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 4),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 32L = ... << 5
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_32),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 5),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 64L = ... << 6
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_64),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 6),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 128L = ... << 7
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_128),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 7),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 256L = ... << 8
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_256),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 512L = ... << 9
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_512),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 9),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 1024L = ... << 10
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_1024),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 10),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 2048L = ... << 11
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_2048),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 11),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 4096L = ... << 12
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4096),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 12),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 8192L = ... << 13
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_8192),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 13),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 16384L = ... << 14
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16384),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 14),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 32768L = ... << 15
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_32768),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 15),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 65536LL = ... << 16
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_65536),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 16777216L = ... << 24
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16777216),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * 4294967296L = ... << 32
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4294967296),
+                new SimpleInstruction(InstructionConstants.OP_LMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },
+        },
+        {   // ... * -1f = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, F_M1),
+                new SimpleInstruction(InstructionConstants.OP_FMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_FNEG),
+            },
+        },
+//        {   // ... * 0f = 0f (or NaN)
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+//                new SimpleInstruction(InstructionConstants.OP_FMUL),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_POP),
+//                new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+//            },
+//        },
+        {   // ... * 1f = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_FMUL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... * -1d = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, D_M1),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DNEG),
+            },
+        },
+//        {   // ... * 0d = 0d (or NaN)
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+//                new SimpleInstruction(InstructionConstants.OP_DMUL),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_POP2),
+//                new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+//            },
+//        },
+        {   // ... * 1d = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_DMUL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... / -1 = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new SimpleInstruction(InstructionConstants.OP_IDIV),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_INEG),
+            },
+        },
+        {   // ... / 1 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_IDIV),
+            },{
+                // Nothing.
+            },
+        },
+//        {   // ... / 2 = ... >> 1
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 4 = ... >> 2
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_4),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 8 = ... >> 3
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 16 = ... >> 4
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 4),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 32 = ... >> 5
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 5),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 64 = ... >> 6
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 64),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 6),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 128 = ... >> 7
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 128),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 7),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 256 = ... >> 8
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 256),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 512 = ... >> 9
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 512),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 9),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 1024 = ... >> 10
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 1024),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 10),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 2048 = ... >> 11
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 2048),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 11),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 4096 = ... >> 12
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 4096),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 12),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 8192 = ... >> 13
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 8192),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 13),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 16384 = ... >> 14
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 16384),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 14),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 32768 = ... >> 15
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC, I_32768),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 15),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 65536 = ... >> 16
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC, I_65536),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+//        {   // ... / 16777216 = ... >> 24
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC, I_16777216),
+//                new SimpleInstruction(InstructionConstants.OP_IDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+//                new SimpleInstruction(InstructionConstants.OP_ISHR),
+//            },
+//        },
+        {   // ... / -1L = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_M1),
+                new SimpleInstruction(InstructionConstants.OP_LDIV),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_LNEG),
+            },
+        },
+        {   // ... / 1L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_LDIV),
+            },{
+                // Nothing.
+            },
+        },
+//        {   // ... / 2L = ... >> 1
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_2),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 4L = ... >> 2
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 8L = ... >> 3
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_8),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 16L = ... >> 4
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 4),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 32L = ... >> 5
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_32),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 5),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 64L = ... >> 6
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_64),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 6),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 128L = ... >> 7
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_128),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 7),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 256L = ... >> 8
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_256),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 512L = ... >> 9
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_512),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 9),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 1024L = ... >> 10
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_1024),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 10),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 2048L = ... >> 11
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_2048),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 11),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 4096L = ... >> 12
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4096),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 12),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 8192L = ... >> 13
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_8192),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 13),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 16384L = ... >> 14
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16384),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 14),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 32768L = ... >> 15
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_32768),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 15),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 65536LL = ... >> 16
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_65536),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 16777216L = ... >> 24
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_16777216),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+//        {   // ... / 4294967296L = ... >> 32
+//            {
+//                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_4294967296),
+//                new SimpleInstruction(InstructionConstants.OP_LDIV),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+//                new SimpleInstruction(InstructionConstants.OP_LSHR),
+//            },
+//        },
+        {   // ... / -1f = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, F_M1),
+                new SimpleInstruction(InstructionConstants.OP_FDIV),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_FNEG),
+            },
+        },
+        {   // ... / 1f = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_FDIV),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... / -1d = -...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, D_M1),
+                new SimpleInstruction(InstructionConstants.OP_DDIV),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DNEG),
+            },
+        },
+        {   // ... / 1d = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_DDIV),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... % 1 = 0
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+                new SimpleInstruction(InstructionConstants.OP_IREM),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+            },
+        },
+//        {   // ... % 2 = ... & 0x1
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_2),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 4 = ... & 0x3
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_4),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_ICONST_3),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 8 = ... & 0x07
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 0x07),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 16 = ... & 0x0f
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 0x0f),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 32 = ... & 0x1f
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 0x1f),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 64 = ... & 0x3f
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 64),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 0x3f),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 128 = ... & 0x7f
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 128),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 0x7f),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 256 = ... & 0x00ff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 256),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x00ff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 512 = ... & 0x01ff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 512),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x01ff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 1024 = ... & 0x03ff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 1024),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x03ff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 2048 = ... & 0x07ff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 2048),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x07ff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 4096 = ... & 0x0fff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 4096),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x0fff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 8192 = ... & 0x1fff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 8192),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x1fff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+//        {   // ... % 16384 = ... & 0x3fff
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 16384),
+//                new SimpleInstruction(InstructionConstants.OP_IREM),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0x3fff),
+//                new SimpleInstruction(InstructionConstants.OP_IAND),
+//            },
+//        },
+        {   // ... % 1L = 0L
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_LREM),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+            },
+        },
+        {   // ... % 1f = 0f
+            {
+                new SimpleInstruction(InstructionConstants.OP_FCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_FREM),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new SimpleInstruction(InstructionConstants.OP_FCONST_0),
+            },
+        },
+        {   // ... % 1d = 0d
+            {
+                new SimpleInstruction(InstructionConstants.OP_DCONST_1),
+                new SimpleInstruction(InstructionConstants.OP_DREM),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new SimpleInstruction(InstructionConstants.OP_DCONST_0),
+            },
+        },
+        {   // -(-...) = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_INEG),
+                new SimpleInstruction(InstructionConstants.OP_INEG),
+            },{
+                // Nothing.
+            },
+        },
+        {   // -(-...) = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LNEG),
+                new SimpleInstruction(InstructionConstants.OP_LNEG),
+            },{
+                // Nothing.
+            },
+        },
+        {   // -(-...) = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FNEG),
+                new SimpleInstruction(InstructionConstants.OP_FNEG),
+            },{
+                // Nothing.
+            },
+        },
+        {   // -(-...) = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DNEG),
+                new SimpleInstruction(InstructionConstants.OP_DNEG),
+            },{
+                // Nothing.
+            },
+        },
+        {   // +(-...) = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_INEG),
+                new SimpleInstruction(InstructionConstants.OP_IADD),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_ISUB),
+            },
+        },
+        {   // +(-...) = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LNEG),
+                new SimpleInstruction(InstructionConstants.OP_LADD),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_LSUB),
+            },
+        },
+        {   // +(-...) = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_FNEG),
+                new SimpleInstruction(InstructionConstants.OP_FADD),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_FSUB),
+            },
+        },
+        {   // +(-...) = -...
+            {
+                new SimpleInstruction(InstructionConstants.OP_DNEG),
+                new SimpleInstruction(InstructionConstants.OP_DADD),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DSUB),
+            },
+        },
+        {   // ... << 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... << 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... >> 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... >> 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LSHR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... >>> 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... >>> 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LUSHR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... & -1 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... & 0 = 0
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+            },
+        },
+        {   // ... & -1L = ...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_M1),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... & 0L = 0L
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+            },
+        },
+        {   // ... | -1 = -1
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+                new SimpleInstruction(InstructionConstants.OP_IOR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new SimpleInstruction(InstructionConstants.OP_ICONST_M1),
+            },
+        },
+        {   // ... | 0 = ...
+            {
+               new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+               new SimpleInstruction(InstructionConstants.OP_IOR),
+           },{
+                // Nothing.
+            },
+        },
+        {   // ... | -1L = -1L
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_M1),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_M1),
+            },
+        },
+        {   // ... | 0L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LOR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... ^ 0 = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new SimpleInstruction(InstructionConstants.OP_IXOR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ... ^ 0L = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_LCONST_0),
+                new SimpleInstruction(InstructionConstants.OP_LXOR),
+            },{
+                // Nothing.
+            },
+        },
+        {   // (... & 0x0000ff00) >> 8 = (... >> 8) & 0xff
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x0000ff00),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },
+        },
+        {   // (... & 0x0000ff00) >>> 8 = (... >>> 8) & 0xff
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x0000ff00),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 8),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },
+        },
+        {   // (... & 0x00ff0000) >> 16 = (... >> 16) & 0xff
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x00ff0000),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },
+        },
+        {   // (... & 0x00ff0000) >>> 16 = (... >>> 16) & 0xff
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x00ff0000),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },
+        },
+        {   // (... & 0xff000000) >> 24 = ... >> 24
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0xff000000),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // (... & 0xffff0000) >> 16 = ... >> 16
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0xffff0000),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // (... & 0xffff0000) >>> 16 = ... >>> 16
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0xffff0000),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },
+        },
+        {   // (... >> 24) & 0xff = ... >>> 24
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },
+        },
+        {   // (... >>> 24) & 0xff = ... >>> 24
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },
+        },
+        {   // (byte)(... & 0x000000ff) = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, 0xff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // (char)(... & 0x0000ffff) = (char)...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x0000ffff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },
+        },
+        {   // (short)(... & 0x0000ffff) = (short)...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC, I_0x0000ffff),
+                new SimpleInstruction(InstructionConstants.OP_IAND),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },
+        },
+        {   // (byte)(... >> 24) = ... >> 24
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // (byte)(... >>> 24) = ... >> 24
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // (char)(... >> 16) = ... >>> 16
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },
+        },
+        {   // (char)(... >>> 16) = ... >>> 16
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },
+        },
+        {   // (short)(... >> 16) = ... >> 16
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // (short)(... >>> 16) = ... >> 16
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },
+        },
+        {   // ... << 24 >> 24 = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 24),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // ... << 16 >>> 16 = (char)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_IUSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },
+        },
+        {   // ... << 16 >> 16 = (short)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHL),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 16),
+                new SimpleInstruction(InstructionConstants.OP_ISHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },
+        },
+        {   // ... << 32 >> 32 = (long)(int)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LSHL),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_L2I),
+                new SimpleInstruction(InstructionConstants.OP_I2L),
+            },
+        },
+        {   // (int)(... & 0x00000000ffffffffL) = (int)...
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_0x00000000ffffffff),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+                new SimpleInstruction(InstructionConstants.OP_L2I),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_L2I),
+            },
+        },
+        {   // (... & 0xffffffff00000000L) >> 32 = ... >> 32
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_0xffffffff00000000),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LSHR),
+            },
+        },
+        {   // (... & 0xffffffff00000000L) >>> 32 = ... >>> 32
+            {
+                new ConstantInstruction(InstructionConstants.OP_LDC2_W, L_0xffffffff00000000),
+                new SimpleInstruction(InstructionConstants.OP_LAND),
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LUSHR),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, 32),
+                new SimpleInstruction(InstructionConstants.OP_LUSHR),
+            },
+        },
+        {   // ... += 0 = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_IINC, X, 0),
+            },{
+                // Nothing.
+            },
+        },
+        {   // getfield/putfield = nothing
+            {
+                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Y),
+                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+            },{
+                // Nothing.
+            },
+        },
+//        {   // putfield_L/putfield_L = pop2_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_L),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_L),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_POP2),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_L),
+//            },
+//        },
+//        {   // putfield_D/putfield_D = pop2_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_D),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_D),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_POP2),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_D),
+//            },
+//        },
+//        {   // putfield/putfield = pop_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_POP),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+//            },
+//        },
+//        {   // putfield_L/getfield_L = dup2_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_L),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                new ConstantInstruction(InstructionConstants.OP_GETFIELD, FIELD_L),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_DUP2_X1),
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_L),
+//            },
+//        },
+//        {   // putfield_D/getfield_D = dup2_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_D),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                new ConstantInstruction(InstructionConstants.OP_GETFIELD, FIELD_D),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_DUP2_X1),
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, FIELD_D),
+//            },
+//        },
+//        {   // putfield/getfield = dup_x1/putfield
+//            {
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                new ConstantInstruction(InstructionConstants.OP_GETFIELD, Y),
+//            },{
+//                new VariableInstruction(InstructionConstants.OP_ALOAD, X),
+//                // ...
+//                new SimpleInstruction(InstructionConstants.OP_DUP_X1),
+//                new ConstantInstruction(InstructionConstants.OP_PUTFIELD, Y),
+//            },
+//        },
+        {   // getstatic/putstatic = nothing
+            {
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, X),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // putstatic_L/putstatic_L = pop2/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_L),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_L),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_L),
+            },
+        },
+        {   // putstatic_D/putstatic_D = pop2/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_D),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_D),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_D),
+            },
+        },
+        {   // putstatic/putstatic = pop/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+            },
+        },
+        {   // putstatic_L/getstatic_L = dup2/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_L),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, FIELD_L),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP2),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_L),
+            },
+        },
+        {   // putstatic_D/getstatic_D = dup2/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_D),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, FIELD_D),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP2),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, FIELD_D),
+            },
+        },
+        {   // putstatic/getstatic = dup/putstatic
+            {
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+                new ConstantInstruction(InstructionConstants.OP_GETSTATIC, X),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_DUP),
+                new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, X),
+            },
+        },
+        {   // (byte)(byte)... = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // (byte)(char)... = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // (byte)(short)... = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // (char)(char)... = (char)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },
+        },
+        {   // (char)(short)... = (char)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+            },
+        },
+        {   // (short)(byte)... = (byte)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2B),
+            },
+        },
+        {   // (short)(char)... = (short)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2C),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },
+        },
+        {   // (short)(short)... = (short)...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_I2S),
+            },
+        },
+        {   // (int)(long)... = ...
+            {
+                new SimpleInstruction(InstructionConstants.OP_I2L),
+                new SimpleInstruction(InstructionConstants.OP_L2I),
+            },{
+                // Nothing.
+            },
+        },
+        // Not handled correctly in all cases by VMs prior to Java 6...
+//        {   // (byte)bytes[...] = bytes[...]
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_BALOAD),
+//                new SimpleInstruction(InstructionConstants.OP_I2B),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BALOAD),
+//            },
+//        },
+//        {   // (short)bytes[...] = bytes[...]
+//            {
+//                 new SimpleInstruction(InstructionConstants.OP_BALOAD),
+//                 new SimpleInstruction(InstructionConstants.OP_I2S),
+//             },{
+//                new SimpleInstruction(InstructionConstants.OP_BALOAD),
+//            },
+//        },
+//        {   // (char)chars[...] = chars[...]
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_CALOAD),
+//                new SimpleInstruction(InstructionConstants.OP_I2C),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_CALOAD),
+//            },
+//        },
+//        {   // (short)shorts[...] = shorts[...]
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_SALOAD),
+//                new SimpleInstruction(InstructionConstants.OP_I2S),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SALOAD),
+//            },
+//        },
+//        {   // bytes[...] = (byte)... = bytes[...] = ...
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_I2B),
+//                new SimpleInstruction(InstructionConstants.OP_BASTORE),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_BASTORE),
+//            },
+//        },
+//        {   // chars[...] = (char)... = chars[...] = ...
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_I2C),
+//                new SimpleInstruction(InstructionConstants.OP_CASTORE),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_CASTORE),
+//            },
+//        },
+//        {   // shorts[...] = (short)... = shorts[...] = ...
+//            {
+//                new SimpleInstruction(InstructionConstants.OP_I2S),
+//                new SimpleInstruction(InstructionConstants.OP_SASTORE),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SASTORE),
+//            },
+//        },
+        {   // goto +3 = nothing
+            {
+                new BranchInstruction(InstructionConstants.OP_GOTO, 3),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ifeq +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFEQ, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ifne +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // iflt +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFLT, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ifge +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFGE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ifgt +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFGT, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ifle +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFLE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ificmpeq +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPEQ, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ificmpne +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPNE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ificmplt +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ificmpge +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ificmpgt +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ificmple +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ifacmpeq +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFACMPEQ, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ifacmpne +3 = pop2
+            {
+                new BranchInstruction(InstructionConstants.OP_IFACMPNE, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP2),
+            },
+        },
+        {   // ifnull +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNULL, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // ifnonnull +3 = pop
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNONNULL, 3),
+            },{
+                new SimpleInstruction(InstructionConstants.OP_POP),
+            },
+        },
+        {   // if (... == 0) = ifeq
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPEQ, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },
+        },
+        {   // if (... != 0) = ifne
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPNE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },
+        },
+        {   // if (... < 0) = iflt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
+        {   // if (... >= 0) = ifge
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
+        {   // if (... > 0) = ifgt
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
+        {   // if (... <= 0) = ifle
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
+        {   // if (... == null) = ifnull
+            {
+                new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
+                new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNULL, X),
+            },
+        },
+        {   // if (... != null) = ifnonnull
+            {
+                new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
+                new BranchInstruction(InstructionConstants.OP_IFACMPNE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNONNULL, X),
+            },
+        },
+        {   // iconst_0/ifeq = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // iconst/ifeq = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // bipush/ifeq = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // sipush/ifeq = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // iconst_0/ifne = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // iconst/ifne = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0, A),
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // bipush/ifne = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_BIPUSH, A),
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // sipush/ifne = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_SIPUSH, A),
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // iconst_0/iflt = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // iconst_0/ifge = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // iconst_0/ifgt = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // iconst_0/ifle = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_ICONST_0),
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // aconst_null/ifnull = goto
+            {
+                new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
+                new BranchInstruction(InstructionConstants.OP_IFNULL, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },
+        },
+        {   // aconst_null/ifnonnul = nothing
+            {
+                new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
+                new BranchInstruction(InstructionConstants.OP_IFNONNULL, X),
+            },{
+                // Nothing.
+            },
+        },
+        {   // ifeq/goto = ifne
+            {
+                new BranchInstruction(InstructionConstants.OP_IFEQ, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNE, X),
+            },
+        },
+        {   // ifne/goto = ifeq
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFEQ, X),
+            },
+        },
+        {   // iflt/goto = ifge
+            {
+                new BranchInstruction(InstructionConstants.OP_IFLT, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGE, X),
+            },
+        },
+        {   // ifge/goto = iflt
+            {
+                new BranchInstruction(InstructionConstants.OP_IFGE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLT, X),
+            },
+        },
+        {   // ifgt/goto = ifle
+            {
+                new BranchInstruction(InstructionConstants.OP_IFGT, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFLE, X),
+            },
+        },
+        {   // ifle/goto = ifgt
+            {
+                new BranchInstruction(InstructionConstants.OP_IFLE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFGT, X),
+            },
+        },
+        {   // ificmpeq/goto = ificmpne
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPEQ, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPNE, X),
+            },
+        },
+        {   // ificmpne/goto = ificmpeq
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPNE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPEQ, X),
+            },
+        },
+        {   // ificmplt/goto = ificmpge
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, X),
+            },
+        },
+        {   // ificmpge/goto = ificmplt
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPGE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPLT, X),
+            },
+        },
+        {   // ificmpgt/goto = ificmple
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, X),
+            },
+        },
+        {   // ificmple/goto = ificmpgt
+            {
+                new BranchInstruction(InstructionConstants.OP_IFICMPLE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFICMPGT, X),
+            },
+        },
+        {   // ifacmpeq/goto = ifacmpne
+            {
+                new BranchInstruction(InstructionConstants.OP_IFACMPEQ, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFACMPNE, X),
+            },
+        },
+        {   // ifacmpne/goto = ifacmpeq
+            {
+                new BranchInstruction(InstructionConstants.OP_IFACMPNE, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFACMPEQ, X),
+            },
+        },
+        {   // ifnull/goto = ifnonnull
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNULL, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNONNULL, X),
+            },
+        },
+        {   // ifnonnull/goto = ifnull
+            {
+                new BranchInstruction(InstructionConstants.OP_IFNONNULL, 6),
+                new BranchInstruction(InstructionConstants.OP_GOTO, X),
+            },{
+                new BranchInstruction(InstructionConstants.OP_IFNULL, X),
+            },
+        },
+//        {   // switch (...) { default: ... } = pop/goto ...
+//            {
+//                new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, A, X, Y, 0, new int[0]),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_POP),
+//                new BranchInstruction(InstructionConstants.OP_GOTO, A),
+//            },
+//        },
+//        {   // switch (...) { default: ... } = pop/goto ...
+//            {
+//                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, 0, new int[0], new int[0]),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_POP),
+//                new BranchInstruction(InstructionConstants.OP_GOTO, A),
+//            },
+//        },
+        {   // switch (...) { case/case/default: ... } = switch (...) { case/default: ... }
+            {
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, new int[] { X, Y }, new int[] { A, B }),
+            },{
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, new int[] { Y }, new int[] { B }),
+            },
+        },
+        {   // switch (...) { case/case/default: ... } = switch (...) { case/default: ... }
+            {
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, B, new int[] { X, Y }, new int[] { A, B }),
+            },{
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, B, new int[] { X }, new int[] { A }),
+            },
+        },
+        {   // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... }
+            {
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, new int[] { X, Y, Z }, new int[] { A, B, C }),
+            },{
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, new int[] { Y, Z }, new int[] { B, C }),
+            },
+        },
+        {   // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... }
+            {
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, B, new int[] { X, Y, Z }, new int[] { A, B, C }),
+            },{
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, B, new int[] { X, Z }, new int[] { A, C }),
+            },
+        },
+        {   // switch (...) { case/case/case/default: ... } = switch (...) { case/case/default: ... }
+            {
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, C, new int[] { X, Y, Z }, new int[] { A, B, C }),
+            },{
+                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, C, new int[] { X, Y }, new int[] { A, B }),
+            },
+        },
+//        {   // switch (...) { case ...: ...  default:  ... }
+//            // = if (... == ...) ... else ...
+//            {
+//                new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, A, X, Y, 1, new int[] { B }),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, X),
+//                new BranchInstruction(InstructionConstants.OP_IFICMPNE, A),
+//                new BranchInstruction(InstructionConstants.OP_GOTO, B),
+//            },
+//        },
+//        {   // switch (...) { case ...: ...  default:  ... }
+//            // = if (... == ...) ... else ...
+//            {
+//                new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, A, 1, new int[] { X }, new int[] { B }),
+//            },{
+//                new SimpleInstruction(InstructionConstants.OP_SIPUSH, X),
+//                new BranchInstruction(InstructionConstants.OP_IFICMPNE, A),
+//                new BranchInstruction(InstructionConstants.OP_GOTO, B),
+//            },
+//        }
+    };
+}
\ No newline at end of file
diff --git a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java
new file mode 100644
index 0000000..f69d4c9
--- /dev/null
+++ b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java
@@ -0,0 +1,278 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.constant.Constant;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+
+/**
+ * This InstructionVisitor replaces a given pattern instruction sequence by
+ * another given replacement instruction sequence. The arguments of the
+ * instruction sequences can be wildcards that are matched and replaced.
+ *
+ * @see InstructionSequenceMatcher
+ * @author Eric Lafortune
+ */
+public class InstructionSequenceReplacer
+extends      SimplifiedVisitor
+implements   InstructionVisitor,
+             ConstantVisitor
+{
+    private static final boolean DEBUG = false;
+
+
+    private final InstructionSequenceMatcher instructionSequenceMatcher;
+    private final Instruction[]              replacementInstructions;
+    private final BranchTargetFinder         branchTargetFinder;
+    private final CodeAttributeEditor        codeAttributeEditor;
+    private final InstructionVisitor         extraInstructionVisitor;
+
+    private final MyReplacementInstructionFactory replacementInstructionFactory = new MyReplacementInstructionFactory();
+
+
+    /**
+     * Creates a new InstructionSequenceReplacer.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @param patternInstructions     the pattern instruction sequence.
+     * @param replacementInstructions the replacement instruction sequence.
+     * @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 InstructionSequenceReplacer(Constant[]          patternConstants,
+                                       Instruction[]       patternInstructions,
+                                       Instruction[]       replacementInstructions,
+                                       BranchTargetFinder  branchTargetFinder,
+                                       CodeAttributeEditor codeAttributeEditor)
+    {
+        this(patternConstants,
+             patternInstructions,
+             replacementInstructions,
+             branchTargetFinder,
+             codeAttributeEditor,
+             null);
+    }
+
+
+    /**
+     * Creates a new InstructionSequenceReplacer.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @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 InstructionSequenceReplacer(Constant[]          patternConstants,
+                                       Instruction[]       patternInstructions,
+                                       Instruction[]       replacementInstructions,
+                                       BranchTargetFinder  branchTargetFinder,
+                                       CodeAttributeEditor codeAttributeEditor,
+                                       InstructionVisitor  extraInstructionVisitor)
+    {
+        this.instructionSequenceMatcher = new InstructionSequenceMatcher(patternConstants, patternInstructions);
+        this.replacementInstructions    = replacementInstructions;
+        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)
+    {
+        // Reset the instruction sequence matcher if the instruction is a branch
+        // target or if it has already been modified.
+        if (branchTargetFinder.isTarget(offset) ||
+            codeAttributeEditor.isModified(offset))
+        {
+            instructionSequenceMatcher.reset();
+        }
+
+        // Try to match the instruction.
+        instruction.accept(clazz, method, codeAttribute, offset, instructionSequenceMatcher);
+
+        // Did the instruction sequence match and is it still unmodified?
+        if (instructionSequenceMatcher.isMatching() &&
+            matchedInstructionsUnmodified())
+        {
+            if (DEBUG)
+            {
+                System.out.println("InstructionSequenceReplacer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+                System.out.println("  Matched:");
+                for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
+                {
+                    int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
+                    System.out.println("    "+InstructionFactory.create(codeAttribute.code, matchedOffset).toString(matchedOffset));
+                }
+                System.out.println("  Replacement:");
+                for (int index = 0; index < replacementInstructions.length; index++)
+                {
+                    int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index);
+                    System.out.println("    "+replacementInstructionFactory.create(index).shrink().toString(matchedOffset));
+                }
+            }
+
+            // Replace the instruction sequence.
+            for (int index = 0; index < replacementInstructions.length; index++)
+            {
+                codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index),
+                                                       replacementInstructionFactory.create(index).shrink());
+            }
+
+            // Delete any remaining instructions in the from sequence.
+            for (int index = replacementInstructions.length; index < instructionSequenceMatcher.instructionCount(); index++)
+            {
+                codeAttributeEditor.deleteInstruction(instructionSequenceMatcher.matchedInstructionOffset(index));
+            }
+
+            // Visit the instruction, if required.
+            if (extraInstructionVisitor != null)
+            {
+                instruction.accept(clazz,
+                                   method,
+                                   codeAttribute,
+                                   offset,
+                                   extraInstructionVisitor);
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the matched pattern instructions haven't been modified
+     * before.
+     */
+    private boolean matchedInstructionsUnmodified()
+    {
+        for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++)
+        {
+            if (codeAttributeEditor.isModified(instructionSequenceMatcher.matchedInstructionOffset(index)))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * This class creates replacement instructions for matched sequences, with
+     * any matched arguments filled out.
+     */
+    private class MyReplacementInstructionFactory
+    implements    InstructionVisitor
+    {
+        private Instruction replacementInstruction;
+
+
+        /**
+         * Creates the replacement instruction for the given index in the
+         * instruction sequence.
+         */
+        public Instruction create(int index)
+        {
+            // Create the instruction.
+            replacementInstructions[index].accept(null,
+                                                  null,
+                                                  null,
+                                                  instructionSequenceMatcher.matchedInstructionOffset(index),
+                                                  this);
+
+            // Return it.
+            return replacementInstruction.shrink();
+        }
+
+
+        // Implementations for InstructionVisitor.
+
+        public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+        {
+            replacementInstruction =
+                new SimpleInstruction(simpleInstruction.opcode,
+                                      instructionSequenceMatcher.matchedArgument(simpleInstruction.constant));
+        }
+
+
+        public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+        {
+            replacementInstruction =
+                new VariableInstruction(variableInstruction.opcode,
+                                        instructionSequenceMatcher.matchedArgument(variableInstruction.variableIndex),
+                                        instructionSequenceMatcher.matchedArgument(variableInstruction.constant));
+        }
+
+
+        public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+        {
+            replacementInstruction =
+                new ConstantInstruction(constantInstruction.opcode,
+                                        instructionSequenceMatcher.matchedConstantIndex(constantInstruction.constantIndex),
+                                        instructionSequenceMatcher.matchedArgument(constantInstruction.constant));
+        }
+
+
+        public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+        {
+            replacementInstruction =
+                new BranchInstruction(branchInstruction.opcode,
+                                      instructionSequenceMatcher.matchedBranchOffset(offset, branchInstruction.branchOffset));
+        }
+
+
+        public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
+        {
+            replacementInstruction =
+                new TableSwitchInstruction(tableSwitchInstruction.opcode,
+                                           instructionSequenceMatcher.matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset),
+                                           instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase),
+                                           instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase),
+                                           instructionSequenceMatcher.matchedJumpOffsets(offset, tableSwitchInstruction.jumpOffsets));
+
+        }
+
+
+        public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
+        {
+            replacementInstruction =
+                new LookUpSwitchInstruction(lookUpSwitchInstruction.opcode,
+                                            instructionSequenceMatcher.matchedBranchOffset(offset, lookUpSwitchInstruction.defaultOffset),
+                                            instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases),
+                                            instructionSequenceMatcher.matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets));
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java
new file mode 100644
index 0000000..d68d914
--- /dev/null
+++ b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java
@@ -0,0 +1,138 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.constant.Constant;
+import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.instruction.Instruction;
+import proguard.classfile.instruction.visitor.*;
+
+/**
+ * This InstructionVisitor replaces multiple instruction sequences at once.
+ *
+ * @see InstructionSequenceReplacer
+ * @author Eric Lafortune
+ */
+public class InstructionSequencesReplacer
+extends      MultiInstructionVisitor
+implements   InstructionVisitor
+{
+    private static final int PATTERN_INDEX     = 0;
+    private static final int REPLACEMENT_INDEX = 1;
+
+
+    /**
+     * Creates a new InstructionSequencesReplacer.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @param instructionSequences    the instruction sequences to be replaced,
+     *                                with subsequently the sequence pair index,
+     *                                the patten/replacement index (0 or 1),
+     *                                and the instruction index in the sequence.
+     * @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 InstructionSequencesReplacer(Constant[]          patternConstants,
+                                        Instruction[][][]   instructionSequences,
+                                        BranchTargetFinder  branchTargetFinder,
+                                        CodeAttributeEditor codeAttributeEditor)
+    {
+        this(patternConstants,
+             instructionSequences,
+             branchTargetFinder,
+             codeAttributeEditor,
+             null);
+    }
+
+
+    /**
+     * Creates a new InstructionSequenceReplacer.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @param instructionSequences    the instruction sequences to be replaced,
+     *                                with subsequently the sequence pair index,
+     *                                the patten/replacement index (0 or 1),
+     *                                and the instruction index in the sequence.
+     * @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 InstructionSequencesReplacer(Constant[]          patternConstants,
+                                        Instruction[][][]   instructionSequences,
+                                        BranchTargetFinder  branchTargetFinder,
+                                        CodeAttributeEditor codeAttributeEditor,
+                                        InstructionVisitor  extraInstructionVisitor)
+    {
+        super(createInstructionSequenceReplacers(patternConstants,
+                                                 instructionSequences,
+                                                 branchTargetFinder,
+                                                 codeAttributeEditor,
+                                                 extraInstructionVisitor));
+    }
+
+
+    /**
+     * Creates an array of InstructionSequenceReplacer instances.
+     * @param patternConstants        any constants referenced by the pattern
+     *                                instruction.
+     * @param instructionSequences    the instruction sequences to be replaced,
+     *                                with subsequently the sequence pair index,
+     *                                the from/to index (0 or 1), and the
+     *                                instruction index in the sequence.
+     * @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.
+     */
+    private static InstructionVisitor[] createInstructionSequenceReplacers(Constant[]          patternConstants,
+                                                                           Instruction[][][]   instructionSequences,
+                                                                           BranchTargetFinder  branchTargetFinder,
+                                                                           CodeAttributeEditor codeAttributeEditor,
+                                                                           InstructionVisitor  extraInstructionVisitor)
+    {
+        InstructionVisitor[] instructionSequenceReplacers =
+            new InstructionSequenceReplacer[instructionSequences.length];
+
+        for (int index = 0; index < instructionSequenceReplacers.length; index++)
+        {
+            Instruction[][] instructionSequencePair = instructionSequences[index];
+            instructionSequenceReplacers[index] =
+                new InstructionSequenceReplacer(patternConstants,
+                                                instructionSequencePair[PATTERN_INDEX],
+                                                instructionSequencePair[REPLACEMENT_INDEX],
+                                                branchTargetFinder,
+                                                codeAttributeEditor,
+                                                extraInstructionVisitor);
+        }
+
+        return instructionSequenceReplacers;
+    }
+}
diff --git a/src/proguard/optimize/peephole/LoadStoreRemover.java b/src/proguard/optimize/peephole/LoadStoreRemover.java
index 4c26f6e..e9286cb 100644
--- a/src/proguard/optimize/peephole/LoadStoreRemover.java
+++ b/src/proguard/optimize/peephole/LoadStoreRemover.java
@@ -1,6 +1,6 @@
-/* $Id: LoadStoreRemover.java,v 1.10.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,20 +21,24 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.editor.CodeAttrInfoEditor;
+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 implements InstructionVisitor
+public class LoadStoreRemover
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
-    private BranchTargetFinder branchTargetFinder;
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final BranchTargetFinder  branchTargetFinder;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
 
     /**
@@ -42,13 +46,13 @@ public class LoadStoreRemover implements InstructionVisitor
      * @param branchTargetFinder      a branch target finder that has been
      *                                initialized to indicate branch targets
      *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      */
-    public LoadStoreRemover(BranchTargetFinder branchTargetFinder,
-                            CodeAttrInfoEditor codeAttrInfoEditor)
+    public LoadStoreRemover(BranchTargetFinder  branchTargetFinder,
+                            CodeAttributeEditor codeAttributeEditor)
     {
-        this(branchTargetFinder, codeAttrInfoEditor, null);
+        this(branchTargetFinder, codeAttributeEditor, null);
     }
 
 
@@ -57,31 +61,27 @@ public class LoadStoreRemover implements InstructionVisitor
      * @param branchTargetFinder      a branch target finder that has been
      *                                initialized to indicate branch targets
      *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @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,
-                            CodeAttrInfoEditor codeAttrInfoEditor,
-                            InstructionVisitor extraInstructionVisitor)
+    public LoadStoreRemover(BranchTargetFinder  branchTargetFinder,
+                            CodeAttributeEditor codeAttributeEditor,
+                            InstructionVisitor  extraInstructionVisitor)
     {
         this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         // Is this instruction a load instruction?
         if (variableInstruction.isLoad() &&
@@ -91,12 +91,12 @@ public class LoadStoreRemover implements InstructionVisitor
 
             int nextOffset = offset + variableInstruction.length(offset);
 
-            if (!codeAttrInfoEditor.isModified(offset)     &&
-                !codeAttrInfoEditor.isModified(nextOffset) &&
+            if (!codeAttributeEditor.isModified(offset)     &&
+                !codeAttributeEditor.isModified(nextOffset) &&
                 !branchTargetFinder.isTarget(nextOffset))
             {
                 // Is the next instruction a corresponding store instruction?
-                Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
                                                                         nextOffset);
 
                 if (nextInstruction instanceof VariableInstruction)
@@ -107,13 +107,13 @@ public class LoadStoreRemover implements InstructionVisitor
                         variableInstruction.variableIndex == variableIndex)
                     {
                         // Delete both instructions.
-                        codeAttrInfoEditor.deleteInstruction(offset);
-                        codeAttrInfoEditor.deleteInstruction(nextOffset);
+                        codeAttributeEditor.deleteInstruction(offset);
+                        codeAttributeEditor.deleteInstruction(nextOffset);
 
                         // Visit the instruction, if required.
                         if (extraInstructionVisitor != null)
                         {
-                            extraInstructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+                            extraInstructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
                         }
                     }
                 }
diff --git a/src/proguard/optimize/peephole/MemberPrivatizer.java b/src/proguard/optimize/peephole/MemberPrivatizer.java
new file mode 100644
index 0000000..5d4db27
--- /dev/null
+++ b/src/proguard/optimize/peephole/MemberPrivatizer.java
@@ -0,0 +1,108 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.editor.MethodInvocationFixer;
+import proguard.classfile.util.*;
+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.
+ *
+ * @see NonPrivateMemberMarker
+ * @see MethodInvocationFixer
+ * @author Eric Lafortune
+ */
+public class MemberPrivatizer
+extends      SimplifiedVisitor
+implements   MemberVisitor
+{
+    private final MemberVisitor extraFieldVisitor;
+    private final MemberVisitor extraMethodVisitor;
+
+
+    /**
+     * Creates a new MemberPrivatizer.
+     */
+    public MemberPrivatizer()
+    {
+        this(null, 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.
+     */
+    public MemberPrivatizer(MemberVisitor extraFieldVisitor,
+                            MemberVisitor extraMethodVisitor)
+    {
+        this.extraFieldVisitor  = extraFieldVisitor;
+        this.extraMethodVisitor = extraMethodVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        // Is the field unmarked?
+        if (NonPrivateMemberMarker.canBeMadePrivate(programField))
+        {
+            // Make the field private.
+            programField.u2accessFlags =
+                AccessUtil.replaceAccessFlags(programField.u2accessFlags,
+                                              ClassConstants.INTERNAL_ACC_PRIVATE);
+
+            // Visit the field, if required.
+            if (extraFieldVisitor != null)
+            {
+                extraFieldVisitor.visitProgramField(programClass, programField);
+            }
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        // Is the method unmarked?
+        if (NonPrivateMemberMarker.canBeMadePrivate(programMethod))
+        {
+            // Make the method private.
+            programMethod.u2accessFlags =
+                AccessUtil.replaceAccessFlags(programMethod.u2accessFlags,
+                                              ClassConstants.INTERNAL_ACC_PRIVATE);
+
+            // Visit the method, if required.
+            if (extraMethodVisitor != null)
+            {
+                extraMethodVisitor.visitProgramMethod(programClass, programMethod);
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java
new file mode 100644
index 0000000..0194da7
--- /dev/null
+++ b/src/proguard/optimize/peephole/MethodInliner.java
@@ -0,0 +1,554 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.info.*;
+
+import java.util.Stack;
+
+/**
+ * This AttributeVisitor inlines short methods in the code attributes that it
+ * visits.
+ *
+ * @author Eric Lafortune
+ */
+public class MethodInliner
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor,
+             ConstantVisitor,
+             MemberVisitor
+{
+    private static final int MAXIMUM_INLINING_CODE_LENGTH  = 8;
+    private static final int MAXIMUM_CODE_EXPANSION        = 2;
+    private static final int MAXIMUM_EXTRA_CODE_LENGTH     = 128;
+
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+
+    private final boolean            allowAccessModification;
+    private final boolean            inlineSingleInvocations;
+    private final InstructionVisitor extraInlinedInvocationVisitor;
+
+    private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
+    private final AccessMethodMarker    accessMethodMarker    = new AccessMethodMarker();
+    private final CatchExceptionMarker  catchExceptionMarker  = new CatchExceptionMarker();
+    private final ConstantAdder         constantAdder         = new ConstantAdder();
+    private final StackSizeComputer     stackSizeComputer     = new StackSizeComputer();
+
+    private Clazz   targetClass;
+    private Method  targetMethod;
+    private boolean inlining;
+    private Stack   inliningMethods = new Stack();
+    private boolean emptyInvokingStack;
+    private int     uninitializedObjectCount;
+    private int     variableOffset;
+    private boolean inlined;
+    private boolean inlinedAny;
+
+
+    /**
+     * Creates a new MethodInliner.
+     * @param allowAccessModification indicates whether the access modifiers of
+     *                                classes and class members can be changed
+     *                                in order to inline methods.
+     */
+    public MethodInliner(boolean allowAccessModification,
+                         boolean inlineSingleInvocations)
+    {
+        this(allowAccessModification, inlineSingleInvocations, null);
+    }
+
+
+    /**
+     * Creates a new MethodInliner.
+     * @param allowAccessModification indicates whether the access modifiers of
+     *                                classes and class members can be changed
+     *                                in order to inline methods.
+     * @param allowAccessModification indicates whether the single invocations
+     *                                should be inlined, or, alternatively,
+     *                                short methods.
+     * @param extraInlinedInvocationVisitor an optional extra visitor for all
+     *                                      inlined invocation instructions.
+     */
+    public MethodInliner(boolean            allowAccessModification,
+                         boolean            inlineSingleInvocations,
+                         InstructionVisitor extraInlinedInvocationVisitor)
+    {
+        this.allowAccessModification       = allowAccessModification;
+        this.inlineSingleInvocations       = inlineSingleInvocations;
+        this.extraInlinedInvocationVisitor = extraInlinedInvocationVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (!inlining)
+        {
+//            codeAttributeComposer.DEBUG = DEBUG =
+//                clazz.getName().equals("abc/Def") &&
+//                method.getName(clazz).equals("abc");
+
+            targetClass              = clazz;
+            targetMethod             = method;
+            inliningMethods.clear();
+            uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0;
+            inlinedAny               = false;
+            codeAttributeComposer.reset();
+            constantAdder.setTargetClass((ProgramClass)clazz);
+            stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute);
+
+            // Append the body of the code.
+            copyCode(clazz, method, codeAttribute);
+
+            targetClass  = null;
+            targetMethod = null;
+            constantAdder.setTargetClass(null);
+
+            // Update the code attribute if any code has been inlined.
+            if (inlinedAny)
+            {
+                codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
+
+                // Update the accessing flags.
+                codeAttribute.instructionsAccept(clazz, method, accessMethodMarker);
+
+                // Update the exception catching flags.
+                catchExceptionMarker.visitCodeAttribute(clazz, method, codeAttribute);
+            }
+        }
+
+        // Only inline the method if it is invoked once or if it is short.
+        // TODO: Check total code length after inlining.
+        else if (inlineSingleInvocations ?
+            MethodInvocationMarker.getInvocationCount(method) == 1 :
+            codeAttribute.u4codeLength <= MAXIMUM_INLINING_CODE_LENGTH)
+        {
+            if (DEBUG)
+            {
+                System.out.println("MethodInliner: inlining ["+
+                                   clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] in ["+
+                                   targetClass.getName()+"."+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]");
+            }
+
+            // Append instructions to store the parameters.
+            storeParameters(clazz, method);
+
+            // Inline the body of the code.
+            copyCode(clazz, method, codeAttribute);
+
+            inlined    = true;
+            inlinedAny = true;
+        }
+    }
+
+
+    /**
+     * Appends instructions to pop the parameters for the given method, storing
+     * them in new local variables.
+     */
+    private void storeParameters(Clazz clazz, Method method)
+    {
+
+        String descriptor = method.getDescriptor(clazz);
+
+        boolean isStatic =
+            (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0;
+
+        // Count the number of parameters, taking into account their categories.
+        int parameterCount  = ClassUtil.internalMethodParameterCount(descriptor);
+        int parameterSize   = ClassUtil.internalMethodParameterSize(descriptor);
+        int parameterOffset = isStatic ? 0 : 1;
+
+        // Store the parameter types.
+        String[] parameterTypes = new String[parameterSize];
+
+        InternalTypeEnumeration internalTypeEnumeration =
+            new InternalTypeEnumeration(descriptor);
+
+        for (int parameterIndex = 0; parameterIndex < parameterSize; parameterIndex++)
+        {
+            String parameterType = internalTypeEnumeration.nextType();
+            parameterTypes[parameterIndex] = parameterType;
+            if (ClassUtil.internalTypeSize(parameterType) == 2)
+            {
+                parameterIndex++;
+            }
+        }
+
+        codeAttributeComposer.beginCodeFragment((parameterOffset + parameterCount) * 4);
+
+        // Go over the parameter types backward, storing the stack entries
+        // in their corresponding variables.
+        for (int parameterIndex = parameterSize-1; parameterIndex >= 0; parameterIndex--)
+        {
+            String parameterType = parameterTypes[parameterIndex];
+            if (parameterType != null)
+            {
+                byte opcode;
+                switch (parameterType.charAt(0))
+                {
+                    case ClassConstants.INTERNAL_TYPE_BOOLEAN:
+                    case ClassConstants.INTERNAL_TYPE_BYTE:
+                    case ClassConstants.INTERNAL_TYPE_CHAR:
+                    case ClassConstants.INTERNAL_TYPE_SHORT:
+                    case ClassConstants.INTERNAL_TYPE_INT:
+                        opcode = InstructionConstants.OP_ISTORE;
+                        break;
+
+                    case ClassConstants.INTERNAL_TYPE_LONG:
+                        opcode = InstructionConstants.OP_LSTORE;
+                        break;
+
+                    case ClassConstants.INTERNAL_TYPE_FLOAT:
+                        opcode = InstructionConstants.OP_FSTORE;
+                        break;
+
+                    case ClassConstants.INTERNAL_TYPE_DOUBLE:
+                        opcode = InstructionConstants.OP_DSTORE;
+                        break;
+
+                    default:
+                        opcode = InstructionConstants.OP_ASTORE;
+                        break;
+                }
+
+                codeAttributeComposer.appendInstruction(parameterSize-parameterIndex-1,
+                                                        new VariableInstruction(opcode, variableOffset + parameterOffset + parameterIndex).shrink());
+            }
+        }
+
+        // Put the 'this' reference in variable 0 (plus offset).
+        if (!isStatic)
+        {
+            codeAttributeComposer.appendInstruction(parameterSize,
+                                                    new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset).shrink());
+        }
+
+        codeAttributeComposer.endCodeFragment();
+    }
+
+
+    /**
+     * Appends the code of the given code attribute.
+     */
+    private void copyCode(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // The code may expand, due to expanding constant and variable
+        // instructions.
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength * MAXIMUM_CODE_EXPANSION +
+                                                MAXIMUM_EXTRA_CODE_LENGTH);
+
+        // Copy the instructions.
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        // Copy the exceptions.
+        codeAttribute.exceptionsAccept(clazz, method, this);
+
+        // Append a label just after the code.
+        codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
+
+        codeAttributeComposer.endCodeFragment();
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        codeAttributeComposer.appendInstruction(offset, instruction.shrink());
+    }
+
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        // Are we inlining this instruction?
+        if (inlining)
+        {
+            // Replace any return instructions by branches to the end of the code.
+            switch (simpleInstruction.opcode)
+            {
+                case InstructionConstants.OP_IRETURN:
+                case InstructionConstants.OP_LRETURN:
+                case InstructionConstants.OP_FRETURN:
+                case InstructionConstants.OP_DRETURN:
+                case InstructionConstants.OP_ARETURN:
+                case InstructionConstants.OP_RETURN:
+                    // Are we not at the last instruction?
+                    if (offset < codeAttribute.u4codeLength-1)
+                    {
+                        // Replace the return instruction by a branch instruction.
+                        Instruction branchInstruction =
+                            new BranchInstruction(InstructionConstants.OP_GOTO_W,
+                                                  codeAttribute.u4codeLength - offset);
+
+                        codeAttributeComposer.appendInstruction(offset,
+                                                                branchInstruction.shrink());
+                    }
+                    else
+                    {
+                        // Just leave out the instruction, but put in a label,
+                        // for the sake of any other branch instructions.
+                        codeAttributeComposer.appendLabel(offset);
+                    }
+
+                    return;
+            }
+        }
+
+        codeAttributeComposer.appendInstruction(offset, simpleInstruction.shrink());
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        // Are we inlining this instruction?
+        if (inlining)
+        {
+            // Update the variable index.
+            variableInstruction.variableIndex += variableOffset;
+        }
+
+        codeAttributeComposer.appendInstruction(offset, variableInstruction.shrink());
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+        // Is it a method invocation?
+        switch (constantInstruction.opcode)
+        {
+            case InstructionConstants.OP_NEW:
+                uninitializedObjectCount++;
+                break;
+
+            case InstructionConstants.OP_INVOKEVIRTUAL:
+            case InstructionConstants.OP_INVOKESPECIAL:
+            case InstructionConstants.OP_INVOKESTATIC:
+            case InstructionConstants.OP_INVOKEINTERFACE:
+                // See if we can inline it.
+                inlined = false;
+
+                // Append a label, in case the invocation will be inlined.
+                codeAttributeComposer.appendLabel(offset);
+
+                emptyInvokingStack =
+                    !inlining &&
+                    stackSizeComputer.isReachable(offset) &&
+                    stackSizeComputer.getStackSize(offset) == 0;
+
+                variableOffset += codeAttribute.u2maxLocals;
+
+                clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
+
+                variableOffset -= codeAttribute.u2maxLocals;
+
+                // Was the method inlined?
+                if (inlined)
+                {
+                    if (extraInlinedInvocationVisitor != null)
+                    {
+                        extraInlinedInvocationVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction);
+                    }
+
+                    // The invocation itself is no longer necessary.
+                    return;
+                }
+
+                break;
+        }
+
+        // Are we inlining this instruction?
+        if (inlining)
+        {
+            // Make sure the constant is present in the constant pool of the
+            // target class.
+            clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantAdder);
+
+            // Let the instruction point to this constant.
+            constantInstruction.constantIndex = constantAdder.getConstantIndex();
+        }
+
+        codeAttributeComposer.appendInstruction(offset, constantInstruction.shrink());
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        int catchType = exceptionInfo.u2catchType;
+
+        if (inlining && catchType != 0)
+        {
+            // Make sure the constant is present in the constant pool of the
+            // target class.
+            clazz.constantPoolEntryAccept(catchType, constantAdder);
+
+            // Let the exception point to this constant.
+            catchType = constantAdder.getConstantIndex();
+        }
+
+        codeAttributeComposer.appendException(new ExceptionInfo(exceptionInfo.u2startPC,
+                                                                exceptionInfo.u2endPC,
+                                                                exceptionInfo.u2handlerPC,
+                                                                catchType));
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) {}
+
+
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
+    {
+        methodrefConstant.referencedMemberAccept(this);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitAnyMember(Clazz Clazz, Member member) {}
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        int accessFlags = programMethod.getAccessFlags();
+
+        if (// Only inline the method if it is private, static, or final.
+            (accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE |
+                            ClassConstants.INTERNAL_ACC_STATIC  |
+                            ClassConstants.INTERNAL_ACC_FINAL)) != 0                               &&
+
+            // Only inline the method if it is not synchronized, etc.
+            (accessFlags & (ClassConstants.INTERNAL_ACC_SYNCHRONIZED |
+                            ClassConstants.INTERNAL_ACC_NATIVE       |
+                            ClassConstants.INTERNAL_ACC_INTERFACE    |
+                            ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0                            &&
+
+            // Don't inline an <init> method, except in an <init> method in the
+            // same class.
+//            (!programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ||
+//             (programClass.equals(targetClass) &&
+//              targetMethod.getName(targetClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) &&
+            !programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)  &&
+
+            // Don't inline a method into itself.
+            (!programMethod.equals(targetMethod) ||
+             !programClass.equals(targetClass))                                                    &&
+
+            // Only inline the method if it isn't recursing.
+            !inliningMethods.contains(programMethod)                                               &&
+
+            // Only inline the method if it doesn't invoke a super method, or if
+            // it is in the same class.
+            (!SuperInvocationMarker.invokesSuperMethods(programMethod) ||
+             programClass.equals(targetClass))                                                     &&
+
+            // Only inline the method if it doesn't branch backward while there
+            // are uninitialized objects.
+            (!BackwardBranchMarker.branchesBackward(programMethod) ||
+             uninitializedObjectCount == 0)                                                        &&
+
+            // Only inline if the code access of the inlined method allows it.
+            (allowAccessModification ||
+             ((!AccessMethodMarker.accessesPrivateCode(programMethod) ||
+               programClass.equals(targetClass)) &&
+
+              (!AccessMethodMarker.accessesPackageCode(programMethod) ||
+               ClassUtil.internalPackageName(programClass.getName()).equals(
+               ClassUtil.internalPackageName(targetClass.getName())))))                            &&
+
+//               (!AccessMethodMarker.accessesProtectedCode(programMethod) ||
+//                targetClass.extends_(programClass) ||
+//                targetClass.implements_(programClass)) ||
+            (!AccessMethodMarker.accessesProtectedCode(programMethod) ||
+             programClass.equals(targetClass))                                                     &&
+
+            // Only inline the method if it doesn't catch exceptions, or if it
+            // is invoked with an empty stack.
+            (!CatchExceptionMarker.catchesExceptions(programMethod) ||
+             emptyInvokingStack)                                                                   &&
+
+            // Only inline the method if it comes from the same class or from
+            // a class with a static initializer.
+            (programClass.equals(targetClass) ||
+             programClass.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
+                                     ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) == null))
+        {
+//            System.out.print("MethodInliner: inlining ");
+//            programMethod.accept(programClass, new SimpleClassPrinter(true));
+//            System.out.print("               in       ");
+//            targetMethod.accept(targetClass, new SimpleClassPrinter(true));
+//
+//            System.out.println("  Private:   "+
+//                               (!AccessMethodMarker.accessesPrivateCode(programMethod) ||
+//                                programClass.equals(targetClass)));
+//
+//            System.out.println("  Package:   "+
+//                               (!AccessMethodMarker.accessesPackageCode(programMethod) ||
+//                                ClassUtil.internalPackageName(programClass.getName()).equals(
+//                                ClassUtil.internalPackageName(targetClass.getName()))));
+//
+//            System.out.println("  Protected: "+
+//                               ((!AccessMethodMarker.accessesProtectedCode(programMethod) ||
+//                                 targetClass.extends_(programClass) ||
+//                                 targetClass.implements_(programClass)) ||
+//                                ClassUtil.internalPackageName(programClass.getName()).equals(
+//                                ClassUtil.internalPackageName(targetClass.getName()))));
+
+            boolean oldInlining = inlining;
+            inlining = true;
+            inliningMethods.push(programMethod);
+
+            // Inline the method body.
+            programMethod.attributesAccept(programClass, this);
+
+            inlining = oldInlining;
+            inliningMethods.pop();
+        }
+        else if (programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+        {
+            uninitializedObjectCount--;
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/MethodPrivatizer.java b/src/proguard/optimize/peephole/MethodPrivatizer.java
deleted file mode 100644
index 8525ab5..0000000
--- a/src/proguard/optimize/peephole/MethodPrivatizer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/* $Id: MethodPrivatizer.java,v 1.7.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.optimize.peephole;
-
-import proguard.classfile.*;
-import proguard.classfile.util.AccessUtil;
-import proguard.classfile.visitor.MemberInfoVisitor;
-import proguard.optimize.NonPrivateMethodMarker;
-
-/**
- * This MemberInfoVisitor makes all final methods that it visits private,
- * unless they have been marked by a NonPrivateMethodMarker.
- *
- * @see NonPrivateMethodMarker
- * @author Eric Lafortune
- */
-public class MethodPrivatizer
-  implements MemberInfoVisitor
-{
-    private MemberInfoVisitor extraMemberInfoVisitor;
-
-
-    /**
-     * Creates a new MethodPrivatizer.
-     */
-    public MethodPrivatizer()
-    {
-        this(null);
-    }
-
-
-    /**
-     * Creates a new MethodPrivatizer.
-     * @param extraMemberInfoVisitor an optional extra visitor for all privatized
-     *                               methods.
-     */
-    public MethodPrivatizer(MemberInfoVisitor extraMemberInfoVisitor)
-    {
-        this.extraMemberInfoVisitor = extraMemberInfoVisitor;
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        // Is the method unmarked?
-        if (NonPrivateMethodMarker.canBeMadePrivate(programMethodInfo))
-        {
-            // Make the method private.
-            programMethodInfo.u2accessFlags =
-                AccessUtil.replaceAccessFlags(programMethodInfo.u2accessFlags,
-                                              ClassConstants.INTERNAL_ACC_PRIVATE);
-
-            // Visit the method, if required.
-            if (extraMemberInfoVisitor != null)
-            {
-                extraMemberInfoVisitor.visitProgramMethodInfo(programClassFile, programMethodInfo);
-            }
-        }
-    }
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-}
diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java
index 0b72a09..0beebc8 100644
--- a/src/proguard/optimize/peephole/NopRemover.java
+++ b/src/proguard/optimize/peephole/NopRemover.java
@@ -1,6 +1,6 @@
-/* $Id: NopRemover.java,v 1.7.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,69 +21,68 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.editor.*;
+import proguard.classfile.attribute.CodeAttribute;
+import proguard.classfile.editor.CodeAttributeEditor;
 import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 
 /**
  * This InstructionVisitor removes all nop instructions that it encounters.
  *
  * @author Eric Lafortune
  */
-public class NopRemover implements InstructionVisitor
+public class NopRemover
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
 
     /**
      * Creates a new NopRemover.
-     * @param codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
+     * @param codeAttributeEditor a code editor that can be used for
+     *                            accumulating changes to the code.
      */
-    public NopRemover(CodeAttrInfoEditor codeAttrInfoEditor)
+    public NopRemover(CodeAttributeEditor codeAttributeEditor)
     {
-        this(codeAttrInfoEditor, null);
+        this(codeAttributeEditor, null);
     }
 
 
     /**
      * Creates a new NopRemover.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      * @param extraInstructionVisitor an optional extra visitor for all removed
      *                                nop instructions.
      */
-    public NopRemover(CodeAttrInfoEditor codeAttrInfoEditor,
-                      InstructionVisitor extraInstructionVisitor)
+    public NopRemover(CodeAttributeEditor codeAttributeEditor,
+                      InstructionVisitor  extraInstructionVisitor)
     {
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         // Check if the instruction is a nop instruction.
         if (simpleInstruction.opcode == InstructionConstants.OP_NOP &&
-            !codeAttrInfoEditor.isModified(offset))
+            !codeAttributeEditor.isModified(offset))
         {
-            codeAttrInfoEditor.deleteInstruction(offset);
+            codeAttributeEditor.deleteInstruction(offset);
 
             // Visit the instruction, if required.
             if (extraInstructionVisitor != null)
             {
-                extraInstructionVisitor.visitSimpleInstruction(classFile, methodInfo, codeAttrInfo, offset, simpleInstruction);
+                extraInstructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
             }
         }
     }
diff --git a/src/proguard/optimize/peephole/PeepholeOptimizer.java b/src/proguard/optimize/peephole/PeepholeOptimizer.java
new file mode 100644
index 0000000..968a69a
--- /dev/null
+++ b/src/proguard/optimize/peephole/PeepholeOptimizer.java
@@ -0,0 +1,103 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This AttributeVisitor sets up and applies the peephole optimizations of its
+ * instruction visitor. The instruction visitor should be using the same
+ * (optional) branch target finder and code attribute editor.
+ *
+ * @author Eric Lafortune
+ */
+public class PeepholeOptimizer
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private final BranchTargetFinder  branchTargetFinder;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  instructionVisitor;
+
+
+    /**
+     * Creates a new PeepholeOptimizer.
+     * @param codeAttributeEditor the code attribute editor that will be reset
+     *                            and then executed.
+     * @param instructionVisitor  the instruction visitor that performs
+     *                            peephole optimizations using the above code
+     *                            attribute editor.
+     */
+    public PeepholeOptimizer(CodeAttributeEditor codeAttributeEditor,
+                             InstructionVisitor  instructionVisitor)
+    {
+        this(null, codeAttributeEditor, instructionVisitor);
+    }
+
+
+    /**
+     * Creates a new PeepholeOptimizer.
+     * @param branchTargetFinder  branch target finder that will be initialized
+     *                            to indicate branch targets in the visited code.
+     * @param codeAttributeEditor the code attribute editor that will be reset
+     *                            and then executed.
+     * @param instructionVisitor  the instruction visitor that performs
+     *                            peephole optimizations using the above code
+     *                            attribute editor.
+     */
+    public PeepholeOptimizer(BranchTargetFinder  branchTargetFinder,
+                             CodeAttributeEditor codeAttributeEditor,
+                             InstructionVisitor  instructionVisitor)
+    {
+        this.branchTargetFinder  = branchTargetFinder;
+        this.codeAttributeEditor = codeAttributeEditor;
+        this.instructionVisitor  = instructionVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (branchTargetFinder != null)
+        {
+            // Set up the branch target finder.
+            branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
+        }
+
+        // Set up the code attribute editor.
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
+
+        // Find the peephole optimizations.
+        codeAttribute.instructionsAccept(clazz, method, instructionVisitor);
+
+        // Apply the peephole optimizations.
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+    }
+}
diff --git a/src/proguard/optimize/peephole/PushPopRemover.java b/src/proguard/optimize/peephole/PushPopRemover.java
index 25a2df6..a5e454d 100644
--- a/src/proguard/optimize/peephole/PushPopRemover.java
+++ b/src/proguard/optimize/peephole/PushPopRemover.java
@@ -1,6 +1,6 @@
-/* $Id: PushPopRemover.java,v 1.11.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,11 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.editor.*;
+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
@@ -32,25 +34,27 @@ import proguard.classfile.instruction.*;
  *
  * @author Eric Lafortune
  */
-public class PushPopRemover implements InstructionVisitor
+public class PushPopRemover
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
-    private BranchTargetFinder branchTargetFinder;
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    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 codeAttrInfoEditor      a code editor that can be used for
-     *                                accumulating changes to the code.
+     * @param branchTargetFinder    a branch target finder that has been
+     *                              initialized to indicate branch targets
+     *                              in the visited code.
+     * @param codeAttributeEditor   a code editor that can be used for
+     *                              accumulating changes to the code.
      */
-    public PushPopRemover(BranchTargetFinder branchTargetFinder,
-                          CodeAttrInfoEditor codeAttrInfoEditor)
+    public PushPopRemover(BranchTargetFinder  branchTargetFinder,
+                          CodeAttributeEditor codeAttributeEditor)
     {
-        this(branchTargetFinder, codeAttrInfoEditor, null);
+        this(branchTargetFinder, codeAttributeEditor, null);
     }
 
 
@@ -59,30 +63,27 @@ public class PushPopRemover implements InstructionVisitor
      * @param branchTargetFinder      a branch target finder that has been
      *                                initialized to indicate branch targets
      *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @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,
-                          CodeAttrInfoEditor codeAttrInfoEditor,
-                          InstructionVisitor extraInstructionVisitor)
+    public PushPopRemover(BranchTargetFinder  branchTargetFinder,
+                          CodeAttributeEditor codeAttributeEditor,
+                          InstructionVisitor  extraInstructionVisitor)
     {
         this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction)
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
     {
         switch (simpleInstruction.opcode)
         {
@@ -109,18 +110,18 @@ public class PushPopRemover implements InstructionVisitor
             case InstructionConstants.OP_LDC_W:
             case InstructionConstants.OP_LDC2_W:
                 // All these simple instructions are pushing instructions.
-                deleteWithSubsequentPop(classFile, methodInfo, codeAttrInfo, offset, simpleInstruction);
+                deleteWithSubsequentPop(clazz, method, codeAttribute, offset, simpleInstruction);
                 break;
         }
     }
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    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(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+            deleteWithSubsequentPop(clazz, method, codeAttribute, offset, variableInstruction);
         }
     }
 
@@ -131,35 +132,35 @@ public class PushPopRemover implements InstructionVisitor
      * Deletes the given instruction and its subsequent compatible pop instruction,
      * if any, and if the latter is not a branch target.
      */
-    private void deleteWithSubsequentPop(ClassFile    classFile,
-                                         MethodInfo   methodInfo,
-                                         CodeAttrInfo codeAttrInfo,
-                                         int          offset,
-                                         Instruction  instruction)
+    private void deleteWithSubsequentPop(Clazz         clazz,
+                                         Method        method,
+                                         CodeAttribute codeAttribute,
+                                         int           offset,
+                                         Instruction   instruction)
     {
         boolean isCategory2 = instruction.isCategory2();
 
         int nextOffset = offset + instruction.length(offset);
 
-        if (!codeAttrInfoEditor.isModified(offset)     &&
-            !codeAttrInfoEditor.isModified(nextOffset) &&
+        if (!codeAttributeEditor.isModified(offset)     &&
+            !codeAttributeEditor.isModified(nextOffset) &&
             !branchTargetFinder.isTarget(nextOffset))
         {
-            Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+            Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
                                                                     nextOffset);
             int nextOpcode = nextInstruction.opcode;
             if ((nextOpcode == InstructionConstants.OP_POP ||
                  nextOpcode == InstructionConstants.OP_POP2) &&
-                nextInstruction.isCategory2() == isCategory2)
+                                                             nextInstruction.isCategory2() == isCategory2)
             {
                 // Delete the pushing instruction and the pop instruction.
-                codeAttrInfoEditor.deleteInstruction(offset);
-                codeAttrInfoEditor.deleteInstruction(nextOffset);
+                codeAttributeEditor.deleteInstruction(offset);
+                codeAttributeEditor.deleteInstruction(nextOffset);
 
                 // Visit the instruction, if required.
                 if (extraInstructionVisitor != null)
                 {
-                    instruction.accept(classFile, methodInfo, codeAttrInfo, offset, extraInstructionVisitor);
+                    instruction.accept(clazz, method, codeAttribute, offset, extraInstructionVisitor);
                 }
             }
         }
diff --git a/src/proguard/optimize/peephole/ReachableCodeMarker.java b/src/proguard/optimize/peephole/ReachableCodeMarker.java
new file mode 100644
index 0000000..ad66b3f
--- /dev/null
+++ b/src/proguard/optimize/peephole/ReachableCodeMarker.java
@@ -0,0 +1,263 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+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;
+
+/**
+ * This AttributeVisitor finds all instruction offsets, branch targets, and
+ * exception targets in the CodeAttribute objects that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class ReachableCodeMarker
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor
+{
+    private boolean[] isReachable = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
+
+    private boolean next;
+    private boolean evaluateExceptions;
+
+
+    /**
+     * Returns whether the instruction at the given offset is reachable in
+     * the CodeAttribute that was visited most recently.
+     */
+    public boolean isReachable(int offset)
+    {
+        return isReachable[offset];
+    }
+
+
+    /**
+     * Returns whether any of the instructions at the given offsets are
+     * reachable in the CodeAttribute that was visited most recently.
+     */
+    public boolean isReachable(int startOffset, int endOffset)
+    {
+        // Check if any of the instructions is reachable.
+        for (int offset = startOffset; offset < endOffset; offset++)
+        {
+            if (isReachable[offset])
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Make sure there is a sufficiently large array.
+        int codeLength = codeAttribute.u4codeLength;
+        if (isReachable.length < codeLength)
+        {
+            // Create a new array.
+            isReachable = new boolean[codeLength];
+        }
+        else
+        {
+            // Reset the array.
+            for (int index = 0; index < codeLength; index++)
+            {
+                isReachable[index] = false;
+            }
+        }
+
+        // Mark the code, starting at the entry point.
+        markCode(clazz, method, codeAttribute, 0);
+
+        // Mark the exception handlers, iterating as long as necessary.
+        do
+        {
+            evaluateExceptions = false;
+
+            codeAttribute.exceptionsAccept(clazz, method, this);
+        }
+        while (evaluateExceptions);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+    {
+        byte opcode = simpleInstruction.opcode;
+        if (opcode == InstructionConstants.OP_IRETURN ||
+            opcode == InstructionConstants.OP_LRETURN ||
+            opcode == InstructionConstants.OP_FRETURN ||
+            opcode == InstructionConstants.OP_DRETURN ||
+            opcode == InstructionConstants.OP_ARETURN ||
+            opcode == InstructionConstants.OP_RETURN  ||
+            opcode == InstructionConstants.OP_ATHROW)
+        {
+            next = false;
+        }
+    }
+
+
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+    {
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        if (variableInstruction.opcode == InstructionConstants.OP_RET)
+        {
+            next = false;
+        }
+    }
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        // Mark the branch target.
+        markBranchTarget(clazz,
+                         method,
+                         codeAttribute,
+                         offset + branchInstruction.branchOffset);
+
+        byte opcode = branchInstruction.opcode;
+        if (opcode == InstructionConstants.OP_GOTO ||
+            opcode == InstructionConstants.OP_GOTO_W)
+        {
+            next = false;
+        }
+    }
+
+
+    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+    {
+        // Mark the branch targets of the default jump offset.
+        markBranchTarget(clazz,
+                         method,
+                         codeAttribute,
+                         offset + switchInstruction.defaultOffset);
+
+        // Mark the branch targets of the jump offsets.
+        markBranchTargets(clazz,
+                          method,
+                          codeAttribute,
+                          offset,
+                          switchInstruction.jumpOffsets);
+
+        next = false;
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        // Mark the exception handler if it's relevant.
+        if (!isReachable(exceptionInfo.u2handlerPC) &&
+            isReachable(exceptionInfo.u2startPC, exceptionInfo.u2endPC))
+        {
+            markCode(clazz, method, codeAttribute, exceptionInfo.u2handlerPC);
+
+            evaluateExceptions = true;
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Marks the branch targets of the given jump offsets for the instruction
+     * at the given offset.
+     */
+    private void markBranchTargets(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int[] jumpOffsets)
+    {
+        for (int index = 0; index < jumpOffsets.length; index++)
+        {
+            markCode(clazz, method, codeAttribute, offset + jumpOffsets[index]);
+        }
+    }
+
+
+    /**
+     * Marks the branch target at the given offset.
+     */
+    private void markBranchTarget(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset)
+    {
+        boolean oldNext = next;
+
+        markCode(clazz, method, codeAttribute, offset);
+
+        next = oldNext;
+    }
+
+
+    /**
+     * Marks the code starting at the given offset.
+     */
+    private void markCode(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset)
+    {
+        boolean oldNext = next;
+
+        byte[] code = codeAttribute.code;
+
+        // Continue with the current instruction as long as we haven't marked it
+        // yet.
+        while (!isReachable[offset])
+        {
+            // Get the current instruction.
+            Instruction instruction = InstructionFactory.create(code, offset);
+
+            // Mark it as reachable.
+            isReachable[offset] = true;
+
+            // By default, we'll assume we can continue with the next
+            // instruction in a moment.
+            next = true;
+
+            // Mark the branch targets, if any.
+            instruction.accept(clazz, method, codeAttribute, offset, this);
+
+            // Can we really continue with the next instruction?
+            if (!next)
+            {
+                break;
+            }
+
+            // Go to the next instruction.
+            offset += instruction.length(offset);
+        }
+
+        next = oldNext;
+    }
+}
diff --git a/src/proguard/optimize/peephole/SingleImplementationFixer.java b/src/proguard/optimize/peephole/SingleImplementationFixer.java
index 809ba67..680f290 100644
--- a/src/proguard/optimize/peephole/SingleImplementationFixer.java
+++ b/src/proguard/optimize/peephole/SingleImplementationFixer.java
@@ -1,6 +1,6 @@
-/* $Id: SingleImplementationFixer.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,14 +21,15 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.editor.*;
-import proguard.classfile.editor.ConstantPoolEditor;
-import proguard.classfile.visitor.*;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
+import proguard.optimize.info.SingleImplementationMarker;
 
 /**
- * This ClassFileVisitor cleans up after the SingleImplementationInliner.
+ * This ClassVisitor cleans up after the SingleImplementationInliner.
  * It fixes the names of interfaces that have single implementations, lets
  * the implementations and fields references point to them again. This is
  * necessary after the SingleImplementationInliner has overzealously renamed
@@ -37,70 +38,57 @@ import proguard.classfile.visitor.*;
  * single implementations.
  *
  * @see SingleImplementationInliner
- * @see ClassFileReferenceFixer
+ * @see ClassReferenceFixer
  * @author Eric Lafortune
  */
 public class SingleImplementationFixer
-implements   ClassFileVisitor,
-             CpInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor
 {
-    private ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Is this an interface with a single implementation?
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(programClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(programClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
             // Fix the reference to its own name.
-            fixThisClassReference(programClassFile);
+            fixThisClassReference(programClass);
 
             // Fix the reference from its single interface or implementation.
-            fixInterfaceReference((ProgramClassFile)programClassFile.subClasses[0],
-                                  programClassFile);
+            fixInterfaceReference((ProgramClass)programClass.subClasses[0],
+                                  programClass);
         }
 
         // Fix the field references in the constant pool.
-        programClassFile.constantPoolEntriesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
+        programClass.constantPoolEntriesAccept(this);
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
-        // Update the referenced class file if it is an interface with a single
+        // Update the referenced class if it is an interface with a single
         // implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(fieldrefCpInfo.referencedClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(fieldrefConstant.referencedClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
             // Fix the reference to the interface.
-            fixFieldrefClassReference((ProgramClassFile)classFile,
-                                      fieldrefCpInfo);
+            fixFieldrefClassReference((ProgramClass)clazz,
+                                      fieldrefConstant);
         }
     }
 
@@ -108,48 +96,48 @@ implements   ClassFileVisitor,
     // Small utility methods.
 
     /**
-     * Fixes the given class file, so its name points to itself again.
+     * Fixes the given class, so its name points to itself again.
      */
-    private void fixThisClassReference(ProgramClassFile programClassFile)
+    private void fixThisClassReference(ProgramClass programClass)
     {
         // We have to add a new class entry to avoid an existing entry with the
         // same name being reused. The names have to be fixed later, based on
-        // their referenced class files.
+        // their referenced classes.
         int nameIndex =
-            constantPoolEditor.addUtf8CpInfo(programClassFile,
-                                             programClassFile.getName());
-        programClassFile.u2thisClass =
-            constantPoolEditor.addCpInfo(programClassFile,
-                                         new ClassCpInfo(nameIndex,
-                                                         programClassFile));
+            constantPoolEditor.addUtf8Constant(programClass,
+                                               programClass.getName());
+        programClass.u2thisClass =
+            constantPoolEditor.addConstant(programClass,
+                                           new ClassConstant(nameIndex,
+                                                             programClass));
     }
 
 
     /**
-     * Fixes the given class file, so it points to the given interface again.
+     * Fixes the given class, so it points to the given interface again.
      */
-    private void fixInterfaceReference(ProgramClassFile programClassFile,
-                                       ProgramClassFile interfaceClassFile)
+    private void fixInterfaceReference(ProgramClass programClass,
+                                       ProgramClass interfaceClass)
     {
         // Make sure the class refers to the given interface again.
-        String interfaceName = interfaceClassFile.getName();
+        String interfaceName = interfaceClass.getName();
 
-        int interfacesCount = programClassFile.u2interfacesCount;
+        int interfacesCount = programClass.u2interfacesCount;
         for (int index = 0; index < interfacesCount; index++)
         {
-            if (interfaceName.equals(programClassFile.getInterfaceName(index)))
+            if (interfaceName.equals(programClass.getInterfaceName(index)))
             {
                 // Update the class index.
                 // We have to add a new class entry to avoid an existing entry
                 // with the same name being reused. The names have to be fixed
-                // later, based on their referenced class files.
+                // later, based on their referenced classes.
                 int nameIndex =
-                    constantPoolEditor.addUtf8CpInfo(programClassFile,
-                                                     interfaceName);
-                programClassFile.u2interfaces[index] =
-                    constantPoolEditor.addCpInfo(programClassFile,
-                                                 new ClassCpInfo(nameIndex,
-                                                                 interfaceClassFile));
+                    constantPoolEditor.addUtf8Constant(programClass,
+                                                       interfaceName);
+                programClass.u2interfaces[index]       =
+                    constantPoolEditor.addConstant(programClass,
+                                                   new ClassConstant(nameIndex,
+                                                                     interfaceClass));
                 break;
 
             }
@@ -160,22 +148,22 @@ implements   ClassFileVisitor,
     /**
      * Fixes the given field reference, so its class index points to its
      * class again. Note that this could be a different class than the one
-     * in the original class file.
+     * in the original class.
      */
-    private void fixFieldrefClassReference(ProgramClassFile programClassFile,
-                                           FieldrefCpInfo   fieldrefCpInfo)
+    private void fixFieldrefClassReference(ProgramClass     programClass,
+                                           FieldrefConstant fieldrefConstant)
     {
-        ClassFile referencedClassFile = fieldrefCpInfo.referencedClassFile;
+        Clazz referencedClass = fieldrefConstant.referencedClass;
 
         // We have to add a new class entry to avoid an existing entry with the
         // same name being reused. The names have to be fixed later, based on
-        // their referenced class files.
+        // their referenced classes.
         int nameIndex =
-            constantPoolEditor.addUtf8CpInfo(programClassFile,
-                                             fieldrefCpInfo.getClassName(programClassFile));
-        fieldrefCpInfo.u2classIndex =
-            constantPoolEditor.addCpInfo(programClassFile,
-                                         new ClassCpInfo(nameIndex,
-                                                         referencedClassFile));
+            constantPoolEditor.addUtf8Constant(programClass,
+                                               fieldrefConstant.getClassName(programClass));
+        fieldrefConstant.u2classIndex =
+            constantPoolEditor.addConstant(programClass,
+                                           new ClassConstant(nameIndex,
+                                                             referencedClass));
     }
 }
diff --git a/src/proguard/optimize/peephole/SingleImplementationInliner.java b/src/proguard/optimize/peephole/SingleImplementationInliner.java
index 6cd498f..3e05a3d 100644
--- a/src/proguard/optimize/peephole/SingleImplementationInliner.java
+++ b/src/proguard/optimize/peephole/SingleImplementationInliner.java
@@ -1,6 +1,6 @@
-/* $Id: SingleImplementationInliner.java,v 1.10.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,311 +23,283 @@ package proguard.optimize.peephole;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.editor.ClassFileReferenceFixer;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.ClassReferenceFixer;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.*;
+import proguard.optimize.info.SingleImplementationMarker;
 
 /**
- * This ClassFileVisitor replaces all references to interfaces that have single
+ * This ClassVisitor replaces all references to interfaces that have single
  * implementations by references to those implementations. The names will then
  * have to be fixed, based on the new references.
  *
+ * @see SingleImplementationMarker
  * @see SingleImplementationFixer
- * @see ClassFileReferenceFixer
+ * @see ClassReferenceFixer
  * @author Eric Lafortune
  */
 public class SingleImplementationInliner
-implements   ClassFileVisitor,
-             CpInfoVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor,
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor,
+             MemberVisitor,
+             AttributeVisitor,
              LocalVariableInfoVisitor,
              LocalVariableTypeInfoVisitor,
              AnnotationVisitor,
              ElementValueVisitor
 {
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
         // Update the constant pool.
-        programClassFile.constantPoolEntriesAccept(this);
+        programClass.constantPoolEntriesAccept(this);
 
         // Update the class members.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
 
         // Update the attributes.
-        programClassFile.attributesAccept(this);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
+        programClass.attributesAccept(this);
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitAnyConstant(Clazz clazz, Constant constant) {}
 
 
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-        // Update the referenced class file if it is an interface with a single
-        // implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(stringCpInfo.referencedClassFile);
-
-        if (singleImplementationClassFile != null)
+        if (stringConstant.referencedMember == null)
         {
-            stringCpInfo.referencedClassFile = singleImplementationClassFile;
+            // Update the referenced class if it is an interface with a single
+            // implementation.
+            Clazz singleImplementationClass =
+                SingleImplementationMarker.singleImplementation(stringConstant.referencedClass);
+
+            if (singleImplementationClass != null)
+            {
+                stringConstant.referencedClass = singleImplementationClass;
+            }
         }
     }
 
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
         // Update the referenced interface if it has a single implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(interfaceMethodrefCpInfo.referencedClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(interfaceMethodrefConstant.referencedClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
             // We know the single implementation contains the method.
-            String name = interfaceMethodrefCpInfo.getName(classFile);
-            String type = interfaceMethodrefCpInfo.getType(classFile);
+            String name = interfaceMethodrefConstant.getName(clazz);
+            String type = interfaceMethodrefConstant.getType(clazz);
 
-            interfaceMethodrefCpInfo.referencedClassFile  = singleImplementationClassFile;
-            interfaceMethodrefCpInfo.referencedMemberInfo = singleImplementationClassFile.findMethod(name, type);
+            interfaceMethodrefConstant.referencedClass  = singleImplementationClass;
+            interfaceMethodrefConstant.referencedMember = singleImplementationClass.findMethod(name, type);
         }
     }
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        // Update the referenced class file if it is an interface with a single
+        // Update the referenced class if it is an interface with a single
         // implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(classCpInfo.referencedClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(classConstant.referencedClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
-            classCpInfo.referencedClassFile = singleImplementationClassFile;
+            classConstant.referencedClass = singleImplementationClass;
         }
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        // Update the referenced class file if the type is an interface with a
+        // Update the referenced class if the type is an interface with a
         // single implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(programFieldInfo.referencedClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(programField.referencedClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
-            programFieldInfo.referencedClassFile = singleImplementationClassFile;
+            programField.referencedClass = singleImplementationClass;
         }
 
         // Update the attributes.
-        programFieldInfo.attributesAccept(programClassFile, this);
+        programField.attributesAccept(programClass, this);
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        // Update the referenced class files if the descriptor contains
+        // Update the referenced classes if the descriptor contains
         // interfaces with single implementations.
-        updateReferencedClassFiles(programMethodInfo.referencedClassFiles);
+        updateReferencedClasses(programMethod.referencedClasses);
 
         // Update the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
+        programMethod.attributesAccept(programClass, this);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
+    // Implementations for AttributeVisitor.
 
-    // Implementations for AttrInfoVisitor.
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
 
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Update the referenced class files of the local variables.
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
-    }
-
-
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        // Update the referenced class files of the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        // Update the referenced classes of the local variables.
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-        // Update the referenced class files of the local variable types.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        // Update the referenced classes of the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-        // Update the referenced class files.
-        updateReferencedClassFiles(signatureAttrInfo.referencedClassFiles);
+        // Update the referenced classes of the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
     {
-        // Update the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
-    }
-
-
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
-    {
-        // Update the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Update the referenced classes.
+        updateReferencedClasses(signatureAttribute.referencedClasses);
     }
 
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
     {
         // Update the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        annotationsAttribute.annotationsAccept(clazz, this);
     }
 
 
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
     {
         // Update the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
     }
 
 
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
     {
         // Update the annotation.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
     }
 
 
     // Implementations for LocalVariableInfoVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
     {
-        // Update the referenced class file if it is an interface with a single
+        // Update the referenced class if it is an interface with a single
         // implementation.
-        ClassFile singleImplementationClassFile =
-            SingleImplementationMarker.singleImplementation(localVariableInfo.referencedClassFile);
+        Clazz singleImplementationClass =
+            SingleImplementationMarker.singleImplementation(localVariableInfo.referencedClass);
 
-        if (singleImplementationClassFile != null)
+        if (singleImplementationClass != null)
         {
-            localVariableInfo.referencedClassFile = singleImplementationClassFile;
+            localVariableInfo.referencedClass = singleImplementationClass;
         }
     }
 
 
     // Implementations for LocalVariableTypeInfoVisitor.
 
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
     {
-        // Update the referenced class files.
-        updateReferencedClassFiles(localVariableTypeInfo.referencedClassFiles);
+        // Update the referenced classes.
+        updateReferencedClasses(localVariableTypeInfo.referencedClasses);
     }
 
 
     // Implementations for AnnotationVisitor.
 
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
     {
-        // Update the referenced class files.
-        updateReferencedClassFiles(annotation.referencedClassFiles);
+        // Update the referenced classes.
+        updateReferencedClasses(annotation.referencedClasses);
 
         // Update the element values.
-        annotation.elementValuesAccept(classFile, this);
+        annotation.elementValuesAccept(clazz, this);
     }
 
 
     // Implementations for ElementValueVisitor.
 
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
     {
     }
 
 
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
     {
-        // Update the referenced class files.
-        updateReferencedClassFiles(enumConstantElementValue.referencedClassFiles);
+        // Update the referenced classes.
+        updateReferencedClasses(enumConstantElementValue.referencedClasses);
     }
 
 
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
     {
-        // Update the referenced class files.
-        updateReferencedClassFiles(classElementValue.referencedClassFiles);
+        // Update the referenced classes.
+        updateReferencedClasses(classElementValue.referencedClasses);
     }
 
 
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
     {
         // Update the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
+        annotationElementValue.annotationAccept(clazz, this);
     }
 
 
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
     {
         // Update the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
     }
 
 
     // Small utility methods.
 
     /**
-     * Updates the given array of referenced class files, replacing references
+     * Updates the given array of referenced classes, replacing references
      * to a interfaces with single implementations by these implementations.
      */
-    private void updateReferencedClassFiles(ClassFile[] referencedClassFiles)
+    private void updateReferencedClasses(Clazz[] referencedClasses)
     {
         // Update all referenced classes.
-        if (referencedClassFiles != null)
+        if (referencedClasses != null)
         {
-            for (int index = 0; index < referencedClassFiles.length; index++)
+            for (int index = 0; index < referencedClasses.length; index++)
             {
                 // See if we have is an interface with a single implementation.
-                ClassFile singleImplementationClassFile =
-                    SingleImplementationMarker.singleImplementation(referencedClassFiles[index]);
+                Clazz singleImplementationClass =
+                    SingleImplementationMarker.singleImplementation(referencedClasses[index]);
 
-                // Update or copy the referenced class file.
-                if (singleImplementationClassFile != null)
+                // Update or copy the referenced class.
+                if (singleImplementationClass != null)
                 {
-                    referencedClassFiles[index] = singleImplementationClassFile;
+                    referencedClasses[index] = singleImplementationClass;
                 }
             }
         }
diff --git a/src/proguard/optimize/peephole/StoreLoadReplacer.java b/src/proguard/optimize/peephole/StoreLoadReplacer.java
index 9298e6b..4b96fe6 100644
--- a/src/proguard/optimize/peephole/StoreLoadReplacer.java
+++ b/src/proguard/optimize/peephole/StoreLoadReplacer.java
@@ -1,6 +1,6 @@
-/* $Id: StoreLoadReplacer.java,v 1.11.2.3 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,9 +21,11 @@
 package proguard.optimize.peephole;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.CodeAttrInfo;
-import proguard.classfile.editor.CodeAttrInfoEditor;
+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
@@ -31,15 +33,17 @@ import proguard.classfile.instruction.*;
  *
  * @author Eric Lafortune
  */
-public class StoreLoadReplacer implements InstructionVisitor
+public class StoreLoadReplacer
+extends      SimplifiedVisitor
+implements   InstructionVisitor
 {
     // Some Instruction objects that can be reused.
-    private Instruction dupInstruction  = new SimpleInstruction(InstructionConstants.OP_DUP);
-    private Instruction dup2Instruction = new SimpleInstruction(InstructionConstants.OP_DUP2);
+    private final Instruction dupInstruction  = new SimpleInstruction(InstructionConstants.OP_DUP);
+    private final Instruction dup2Instruction = new SimpleInstruction(InstructionConstants.OP_DUP2);
 
-    private BranchTargetFinder branchTargetFinder;
-    private CodeAttrInfoEditor codeAttrInfoEditor;
-    private InstructionVisitor extraInstructionVisitor;
+    private final BranchTargetFinder  branchTargetFinder;
+    private final CodeAttributeEditor codeAttributeEditor;
+    private final InstructionVisitor  extraInstructionVisitor;
 
 
     /**
@@ -47,13 +51,13 @@ public class StoreLoadReplacer implements InstructionVisitor
      * @param branchTargetFinder      a branch target finder that has been
      *                                initialized to indicate branch targets
      *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @param codeAttributeEditor     a code editor that can be used for
      *                                accumulating changes to the code.
      */
-    public StoreLoadReplacer(BranchTargetFinder branchTargetFinder,
-                             CodeAttrInfoEditor codeAttrInfoEditor)
+    public StoreLoadReplacer(BranchTargetFinder  branchTargetFinder,
+                             CodeAttributeEditor codeAttributeEditor)
     {
-        this(branchTargetFinder, codeAttrInfoEditor, null);
+        this(branchTargetFinder, codeAttributeEditor, null);
     }
 
 
@@ -62,31 +66,27 @@ public class StoreLoadReplacer implements InstructionVisitor
      * @param branchTargetFinder      a branch target finder that has been
      *                                initialized to indicate branch targets
      *                                in the visited code.
-     * @param codeAttrInfoEditor      a code editor that can be used for
+     * @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,
-                             CodeAttrInfoEditor codeAttrInfoEditor,
-                             InstructionVisitor extraInstructionVisitor)
+    public StoreLoadReplacer(BranchTargetFinder  branchTargetFinder,
+                             CodeAttributeEditor codeAttributeEditor,
+                             InstructionVisitor  extraInstructionVisitor)
     {
         this.branchTargetFinder      = branchTargetFinder;
-        this.codeAttrInfoEditor      = codeAttrInfoEditor;
+        this.codeAttributeEditor     = codeAttributeEditor;
         this.extraInstructionVisitor = extraInstructionVisitor;
     }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction)
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
     {
         // Is this instruction a regular store instruction?
         if (!variableInstruction.isLoad() &&
@@ -97,12 +97,12 @@ public class StoreLoadReplacer implements InstructionVisitor
 
             int nextOffset = offset + variableInstruction.length(offset);
 
-            if (!codeAttrInfoEditor.isModified(offset)     &&
-                !codeAttrInfoEditor.isModified(nextOffset) &&
+            if (!codeAttributeEditor.isModified(offset)     &&
+                !codeAttributeEditor.isModified(nextOffset) &&
                 !branchTargetFinder.isTarget(nextOffset))
             {
                 // Is the next instruction a corresponding load instruction?
-                Instruction nextInstruction = InstructionFactory.create(codeAttrInfo.code,
+                Instruction nextInstruction = InstructionFactory.create(codeAttribute.code,
                                                                         nextOffset);
 
                 if (nextInstruction instanceof VariableInstruction)
@@ -114,24 +114,24 @@ public class StoreLoadReplacer implements InstructionVisitor
                     {
                         // Replace the store instruction by a matching dup instruction.
                         Instruction matchingDupInstruction = variableInstruction.isCategory2() ?
-                            dup2Instruction :
-                            dupInstruction;
+                                                             dup2Instruction :
+                                                             dupInstruction;
 
-                        codeAttrInfoEditor.replaceInstruction(offset,
-                                                              matchingDupInstruction);
+                        codeAttributeEditor.replaceInstruction(offset,
+                                                               matchingDupInstruction);
 
                         // Replace the load instruction by the store instruction.
                         Instruction storeInstruction =
                              new VariableInstruction(opcode,
                                                      variableInstruction.variableIndex).shrink();
 
-                        codeAttrInfoEditor.replaceInstruction(nextOffset,
-                                                              storeInstruction);
+                        codeAttributeEditor.replaceInstruction(nextOffset,
+                                                               storeInstruction);
 
                         // Visit the instruction, if required.
                         if (extraInstructionVisitor != null)
                         {
-                            extraInstructionVisitor.visitVariableInstruction(classFile, methodInfo, codeAttrInfo, offset, variableInstruction);
+                            extraInstructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
                         }
                     }
                 }
diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/src/proguard/optimize/peephole/UnreachableCodeRemover.java
new file mode 100644
index 0000000..529d3c0
--- /dev/null
+++ b/src/proguard/optimize/peephole/UnreachableCodeRemover.java
@@ -0,0 +1,143 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.CodeAttributeEditor;
+import proguard.classfile.instruction.Instruction;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+
+/**
+ * This InstructionVisitor deletes blocks of code that can never be reached by
+ * regular calls or branches.
+ *
+ * @author Eric Lafortune
+ */
+public class UnreachableCodeRemover
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+    private final InstructionVisitor  extraInstructionVisitor;
+
+    private final ReachableCodeMarker reachableCodeMarker = new ReachableCodeMarker();
+    private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();
+
+
+    /**
+     * Creates a new UnreachableCodeRemover.
+     */
+    public UnreachableCodeRemover()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new UnreachableCodeRemover.
+     * @param extraInstructionVisitor an optional extra visitor for all
+     *                                deleted instructions.
+     */
+    public UnreachableCodeRemover(InstructionVisitor  extraInstructionVisitor)
+    {
+        this.extraInstructionVisitor = extraInstructionVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        // TODO: Remove this when the code has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
+        {
+            System.err.println("Unexpected error while removing unreachable code:");
+            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()+")");
+
+            throw ex;
+        }
+    }
+
+
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if (DEBUG)
+        {
+            System.out.println("UnreachableCodeRemover: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+        }
+
+        reachableCodeMarker.visitCodeAttribute(clazz, method, codeAttribute);
+
+        codeAttributeEditor.reset(codeAttribute.u4codeLength);
+
+        codeAttribute.instructionsAccept(clazz, method, this);
+
+        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        if (DEBUG)
+        {
+            System.out.println("  "+(reachableCodeMarker.isReachable(offset) ? "+" : "-")+" "+instruction.toString(offset));
+        }
+
+        // Is this instruction unreachable?
+        if (!reachableCodeMarker.isReachable(offset))
+        {
+            // Then delete it.
+            codeAttributeEditor.deleteInstruction(offset);
+
+            // Visit the instruction, if required.
+            if (extraInstructionVisitor != null)
+            {
+                instruction.accept(clazz, method, codeAttribute, offset, extraInstructionVisitor);
+            }
+        }
+    }
+}
diff --git a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java
new file mode 100644
index 0000000..03c4b83
--- /dev/null
+++ b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java
@@ -0,0 +1,163 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.peephole;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.optimize.info.ExceptionInstructionChecker;
+
+/**
+ * This AttributeVisitor removes exception handlers that are unreachable in the
+ * code attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class UnreachableExceptionRemover
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             ExceptionInfoVisitor
+{
+    private final ExceptionInfoVisitor extraExceptionInfoVisitor;
+
+
+    private final ExceptionInstructionChecker exceptionInstructionChecker = new ExceptionInstructionChecker();
+
+
+    /**
+     * Creates a new UnreachableExceptionRemover.
+     */
+    public UnreachableExceptionRemover()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new UnreachableExceptionRemover.
+     * @param extraExceptionInfoVisitor an optional extra visitor for all
+     *                                  removed exceptions.
+     */
+    public UnreachableExceptionRemover(ExceptionInfoVisitor extraExceptionInfoVisitor)
+    {
+        this.extraExceptionInfoVisitor = extraExceptionInfoVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Go over the exception table.
+        codeAttribute.exceptionsAccept(clazz, method, this);
+
+        // Remove exceptions with empty code blocks.
+        codeAttribute.u2exceptionTableLength =
+            removeEmptyExceptions(codeAttribute.exceptionTable,
+                                  codeAttribute.u2exceptionTableLength);
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        if (!mayThrowExceptions(clazz,
+                                method,
+                                codeAttribute,
+                                exceptionInfo.u2startPC,
+                                exceptionInfo.u2endPC))
+        {
+            // Make the code block empty.
+            exceptionInfo.u2endPC = exceptionInfo.u2startPC;
+
+            if (extraExceptionInfoVisitor != null)
+            {
+                extraExceptionInfoVisitor.visitExceptionInfo(clazz, method, codeAttribute, exceptionInfo);
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the specified block of code may throw exceptions.
+     */
+    private boolean mayThrowExceptions(Clazz         clazz,
+                                       Method        method,
+                                       CodeAttribute codeAttribute,
+                                       int           startOffset,
+                                       int           endOffset)
+    {
+        byte[] code = codeAttribute.code;
+
+        // Go over all instructions.
+        int offset = startOffset;
+        while (offset < endOffset)
+        {
+            // Get the current instruction.
+            Instruction instruction = InstructionFactory.create(code, offset);
+
+            // Check if it may be throwing exceptions.
+            if (exceptionInstructionChecker.mayThrowExceptions(clazz,
+                                                               method,
+                                                               codeAttribute,
+                                                               offset,
+                                                               instruction))
+            {
+                return true;
+            }
+
+            // Go to the next instruction.
+            offset += instruction.length(offset);
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the given list of exceptions, without the ones that have empty
+     * code blocks.
+     */
+    private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
+                                      int             exceptionInfoCount)
+    {
+        // Overwrite all empty exceptions.
+        int newIndex = 0;
+        for (int index = 0; index < exceptionInfoCount; index++)
+        {
+            ExceptionInfo exceptionInfo = exceptionInfos[index];
+            if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
+            {
+                exceptionInfos[newIndex++] = exceptionInfo;
+            }
+        }
+
+        return newIndex;
+    }
+}
diff --git a/src/proguard/optimize/peephole/VariableShrinker.java b/src/proguard/optimize/peephole/VariableShrinker.java
new file mode 100644
index 0000000..3418452
--- /dev/null
+++ b/src/proguard/optimize/peephole/VariableShrinker.java
@@ -0,0 +1,131 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; 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.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.VariableEditor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.MemberVisitor;
+import proguard.optimize.*;
+import proguard.optimize.info.*;
+
+/**
+ * This MemberVisitor removes unused local variables from the code of the methods
+ * that it visits.
+ *
+ * @see ParameterUsageMarker
+ * @see MethodStaticizer
+ * @see MethodDescriptorShrinker
+ * @author Eric Lafortune
+ */
+public class VariableShrinker
+extends      SimplifiedVisitor
+implements   AttributeVisitor
+{
+    private static final boolean DEBUG = false;
+
+
+    private final MemberVisitor extraVariableMemberVisitor;
+
+    private final VariableUsageMarker variableUsageMarker = new VariableUsageMarker();
+    private final VariableEditor      variableEditor      = new VariableEditor();
+
+
+    /**
+     * Creates a new VariableShrinker.
+     */
+    public VariableShrinker()
+    {
+        this(null);
+    }
+
+
+    /**
+     * Creates a new VariableShrinker with an extra visitor.
+     * @param extraVariableMemberVisitor an optional extra visitor for all
+     *                                   removed variables.
+     */
+    public VariableShrinker(MemberVisitor extraVariableMemberVisitor)
+    {
+        this.extraVariableMemberVisitor = extraVariableMemberVisitor;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0)
+        {
+            // Figure out the local variables that are used by the code.
+            variableUsageMarker.visitCodeAttribute(clazz, method, codeAttribute);
+
+            // Get the total size of the local variable frame.
+            int variablesSize = variableUsageMarker.getVariablesSize();
+
+            // The descriptor may have been been shrunk already, so get the
+            // original parameter size.
+            int parameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz));
+
+            if (DEBUG)
+            {
+                System.out.println("VariableShrinker: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+                System.out.println("  parameter size = " + parameterSize);
+                System.out.println("  variables size = " + variablesSize);
+            }
+
+            // Make sure the size of the local variable frame is set correctly.
+            codeAttribute.u2maxLocals = variablesSize;
+
+            // Delete unused local variables from the local variable frame.
+            variableEditor.reset(variablesSize);
+
+            for (int variableIndex = parameterSize+1; variableIndex < variablesSize; variableIndex++)
+            {
+                // Is the variable not required?
+                if (!variableUsageMarker.isVariableUsed(variableIndex))
+                {
+                    if (DEBUG)
+                    {
+                        System.out.println("  Deleting local variable #"+variableIndex);
+                    }
+
+                    // Delete the unused variable.
+                    variableEditor.deleteVariable(variableIndex);
+
+                    // Visit the method, if required.
+                    if (extraVariableMemberVisitor != null)
+                    {
+                        method.accept(clazz, extraVariableMemberVisitor);
+                    }
+                }
+            }
+
+            // Shift all remaining parameters and variables in the byte code.
+            variableEditor.visitCodeAttribute(clazz, method, codeAttribute);
+        }
+    }
+}
diff --git a/src/proguard/preverify/CodePreverifier.java b/src/proguard/preverify/CodePreverifier.java
new file mode 100644
index 0000000..bb2a85e
--- /dev/null
+++ b/src/proguard/preverify/CodePreverifier.java
@@ -0,0 +1,603 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.preverify;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.InstructionConstants;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+import proguard.evaluation.*;
+import proguard.evaluation.value.*;
+import proguard.optimize.evaluation.*;
+
+import java.util.*;
+
+/**
+ * This class can preverify methods in program class pools, according to a given
+ * specification.
+ *
+ * @author Eric Lafortune
+ */
+public class CodePreverifier
+extends      SimplifiedVisitor
+implements   MemberVisitor,
+             AttributeVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+
+    private final boolean microEdition;
+
+    private final PartialEvaluator   partialEvaluator   = new PartialEvaluator();
+    private final LivenessAnalyzer   livenessAnalyzer   = new LivenessAnalyzer(partialEvaluator);
+    private final ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor();
+    private final AttributesEditor   attributesEditor   = new AttributesEditor();
+
+
+    /**
+     * Creates a new CodePreverifier.
+     */
+    public CodePreverifier(boolean microEdition)
+    {
+        this.microEdition = microEdition;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // TODO: Remove this when the preverifier has stabilized.
+        // Catch any unexpected exceptions from the actual visiting method.
+        try
+        {
+            // Process the code.
+            visitCodeAttribute0(clazz, method, codeAttribute);
+        }
+        catch (RuntimeException ex)
+        {
+            System.err.println("Unexpected error while preverifying:");
+            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()+")");
+
+            throw ex;
+        }
+    }
+
+
+    public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        ProgramClass  programClass  = (ProgramClass)clazz;
+        ProgramMethod programMethod = (ProgramMethod)method;
+
+        // Evaluate the method.
+        //partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
+        livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Collect the stack map frames.
+        List stackMapFrameList = new ArrayList();
+
+        for (int offset = 0; offset < codeAttribute.u4codeLength; offset++)
+        {
+            // Only store frames at the beginning of code blocks.
+            if (partialEvaluator.isTraced(offset) &&
+                partialEvaluator.isBranchOrExceptionTarget(offset))
+            {
+                // Convert the variable values to types.
+                VerificationType[] variableTypes =
+                    correspondingVerificationTypes(programClass,
+                                                   programMethod,
+                                                   codeAttribute,
+                                                   offset,
+                                                   partialEvaluator.getVariablesBefore(offset));
+
+                // Convert the stack values to types.
+                VerificationType[] stackTypes =
+                    correspondingVerificationTypes(programClass,
+                                                   programMethod,
+                                                   codeAttribute,
+                                                   offset,
+                                                   partialEvaluator.getStackBefore(offset));
+                // Create and store a new frame.
+                stackMapFrameList.add(new FullFrame(offset,
+                                                    variableTypes,
+                                                    stackTypes));
+            }
+        }
+
+        // Compress the stack map frames if the target is not Java Micro Edition.
+        if (!microEdition && !stackMapFrameList.isEmpty())
+        {
+            // Convert the initial variable values to types.
+            VerificationType[] initialVariables =
+                correspondingVerificationTypes(programClass,
+                                               programMethod,
+                                               codeAttribute,
+                                               PartialEvaluator.AT_METHOD_ENTRY,
+                                               partialEvaluator.getVariablesBefore(0));
+
+            // Special case: the <init> method.
+            if (method.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
+            {
+                initialVariables[0] = VerificationTypeFactory.createUninitializedThisType();
+            }
+
+            compressStackMapFrames(initialVariables,
+                                   stackMapFrameList);
+        }
+
+        // Get the proper name for the attribute to be added/replaced/deleted.
+        String stackMapAttributeName = microEdition ?
+             ClassConstants.ATTR_StackMap :
+             ClassConstants.ATTR_StackMapTable;
+
+        int frameCount = stackMapFrameList.size();
+
+        if (DEBUG)
+        {
+            Attribute originalStackMapAttribute = codeAttribute.getAttribute(clazz,
+                                                                             stackMapAttributeName);
+
+            if (originalStackMapAttribute != null)
+            {
+                int originalFrameCount = microEdition ?
+                    ((StackMapAttribute)originalStackMapAttribute).u2stackMapFramesCount :
+                    ((StackMapTableAttribute)originalStackMapAttribute).u2stackMapFramesCount;
+
+                StackMapFrame[] originalFrames = microEdition ?
+                    ((StackMapAttribute)originalStackMapAttribute).stackMapFrames :
+                    ((StackMapTableAttribute)originalStackMapAttribute).stackMapFrames;
+
+                if (frameCount != originalFrameCount ||
+                    !Arrays.equals(stackMapFrameList.toArray(), originalFrames))
+                {
+                    System.out.println("Original preverification ["+clazz.getName()+"]:");
+                    new ClassPrinter().visitProgramMethod(programClass, programMethod);
+                }
+            }
+            else if (frameCount != 0)
+            {
+                System.out.println("Original preverification empty ["+clazz.getName()+"."+method.getName(clazz)+"]");
+            }
+        }
+
+        if (frameCount == 0)
+        {
+            // Remove any stack map (table) attribute from the code attribute.
+            attributesEditor.deleteAttribute(programClass,
+                                             programMethod,
+                                             codeAttribute,
+                                             stackMapAttributeName);
+        }
+        else
+        {
+            Attribute stackMapAttribute;
+
+            // Create the appropriate attribute.
+            if (microEdition)
+            {
+                // Copy the frames into an array.
+                FullFrame[] stackMapFrames = new FullFrame[frameCount];
+                stackMapFrameList.toArray(stackMapFrames);
+
+                // Put the frames into a stack map attribute.
+                stackMapAttribute = new StackMapAttribute(stackMapFrames);
+            }
+            else
+            {
+                // Copy the frames into an array.
+                StackMapFrame[] stackMapFrames = new StackMapFrame[frameCount];
+                stackMapFrameList.toArray(stackMapFrames);
+
+                // Put the frames into a stack map table attribute.
+                stackMapAttribute = new StackMapTableAttribute(stackMapFrames);
+            }
+
+            // Fill out the name of the stack map attribute.
+            stackMapAttribute.u2attributeNameIndex =
+                constantPoolEditor.addUtf8Constant(programClass, stackMapAttributeName);
+
+            // Add the new stack map (table) attribute to the code attribute.
+            attributesEditor.addAttribute(programClass,
+                                          programMethod,
+                                          codeAttribute,
+                                          stackMapAttribute);
+
+            if (DEBUG)
+            {
+                System.out.println("Preverifier ["+programClass.getName()+"."+programMethod.getName(programClass)+"]:");
+                stackMapAttribute.accept(programClass, programMethod, codeAttribute, new ClassPrinter());
+            }
+        }
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Creates and returns the verification types corresponding to the given
+     * variables. If necessary, class constants are added to the constant pool
+     * of the given class.
+     */
+    private VerificationType[] correspondingVerificationTypes(ProgramClass    programClass,
+                                                              ProgramMethod   programMethod,
+                                                              CodeAttribute   codeAttribute,
+                                                              int             offset,
+                                                              TracedVariables variables)
+    {
+        int maximumVariablesSize = variables.size();
+        int typeCount = 0;
+        int typeIndex = 0;
+
+        // Count the the number of verification types, ignoring any nulls at
+        // the end.
+        for (int index = 0; index < maximumVariablesSize; index++)
+        {
+            Value value = variables.getValue(index);
+
+            typeIndex++;
+
+            // Remember the maximum live type index.
+            if (value != null &&
+                (offset == PartialEvaluator.AT_METHOD_ENTRY ||
+                 livenessAnalyzer.isAliveBefore(offset, index)))
+            {
+                typeCount = typeIndex;
+
+                // Category 2 types that are alive are stored as single entries.
+                if (value.isCategory2())
+                {
+                    index++;
+                }
+            }
+        }
+
+        // Create and fill out the verification types.
+        VerificationType[] types = new VerificationType[typeCount];
+
+        typeIndex = 0;
+
+        // Note the slightly different terminating condition, because the
+        // types may have been truncated.
+        for (int index = 0; typeIndex < typeCount; index++)
+        {
+            Value value         = variables.getValue(index);
+            Value producerValue = variables.getProducerValue(index);
+
+            // Fill out the type.
+            VerificationType type;
+
+            if (value != null &&
+                (offset == PartialEvaluator.AT_METHOD_ENTRY ||
+                 livenessAnalyzer.isAliveBefore(offset, index)))
+            {
+                type = correspondingVerificationType(programClass,
+                                                     programMethod,
+                                                     codeAttribute,
+                                                     offset,
+                                                     value,
+                                                     producerValue);
+
+                // Category 2 types that are alive are stored as single entries.
+                if (value.isCategory2())
+                {
+                    index++;
+                }
+            }
+            else
+            {
+                type = VerificationTypeFactory.createTopType();
+            }
+
+            types[typeIndex++] = type;
+        }
+
+        return types;
+    }
+
+
+    /**
+     * Creates and returns the verification types corresponding to the given
+     * stack. If necessary, class constants are added to the constant pool
+     * of the given class.
+     */
+    private VerificationType[] correspondingVerificationTypes(ProgramClass  programClass,
+                                                              ProgramMethod programMethod,
+                                                              CodeAttribute codeAttribute,
+                                                              int           offset,
+                                                              TracedStack   stack)
+    {
+        int maximumStackSize = stack.size();
+        int typeCount = 0;
+
+        // Count the the number of verification types.
+        for (int index = 0; index < maximumStackSize; index++)
+        {
+            // We have to work down from the top of the stack.
+            Value value = stack.getTop(index);
+
+            typeCount++;
+
+            // Category 2 types are stored as single entries.
+            if (value.isCategory2())
+            {
+                index++;
+            }
+        }
+
+        // Create and fill out the verification types.
+        VerificationType[] types = new VerificationType[typeCount];
+
+        int typeIndex = typeCount;
+
+        for (int index = 0; index < maximumStackSize; index++)
+        {
+            // We have to work down from the top of the stack.
+            Value value         = stack.getTop(index);
+            Value producerValue = stack.getTopProducerValue(index);
+
+            // Fill out the type.
+            types[--typeIndex] =
+                correspondingVerificationType(programClass,
+                                              programMethod,
+                                              codeAttribute,
+                                              offset,
+                                              value,
+                                              producerValue);
+
+            // Category 2 types are stored as single entries.
+            if (value.isCategory2())
+            {
+                index++;
+            }
+        }
+
+        return types;
+    }
+
+
+    /**
+     * Creates and returns the verification type corresponding to the given
+     * value. If necessary, a class constant is added to the constant pool of
+     * the given class.
+     */
+    private VerificationType correspondingVerificationType(ProgramClass  programClass,
+                                                           ProgramMethod programMethod,
+                                                           CodeAttribute codeAttribute,
+                                                           int           offset,
+                                                           Value         value,
+                                                           Value         producerValue)
+    {
+        if (value == null)
+        {
+            return VerificationTypeFactory.createTopType();
+        }
+
+        int type = value.computationalType();
+
+        switch (type)
+        {
+            case Value.TYPE_INSTRUCTION_OFFSET:
+            case Value.TYPE_INTEGER:   return VerificationTypeFactory.createIntegerType();
+            case Value.TYPE_LONG:      return VerificationTypeFactory.createLongType();
+            case Value.TYPE_FLOAT:     return VerificationTypeFactory.createFloatType();
+            case Value.TYPE_DOUBLE:    return VerificationTypeFactory.createDoubleType();
+            case Value.TYPE_TOP:       return VerificationTypeFactory.createTopType();
+            case Value.TYPE_REFERENCE:
+                // Is it a Null type?
+                ReferenceValue referenceValue = value.referenceValue();
+                if (referenceValue.isNull() == Value.ALWAYS)
+                {
+                    return VerificationTypeFactory.createNullType();
+                }
+
+                // Is the reference type newly created?
+                if (offset != PartialEvaluator.AT_METHOD_ENTRY)
+                {
+                    InstructionOffsetValue producers = producerValue.instructionOffsetValue();
+                    if (producers.instructionOffsetCount() == 1)
+                    {
+                        int producerOffset = producers.instructionOffset(0);
+
+                        // Special case: in an instance initialization method,
+                        // before the super initialization, loading "this"
+                        // produces an unitinitialized stack entry.
+                        if (partialEvaluator.isInitializer()                       &&
+                            offset <= partialEvaluator.superInitializationOffset() &&
+                            producerOffset > PartialEvaluator.AT_METHOD_ENTRY      &&
+                            codeAttribute.code[producerOffset] == InstructionConstants.OP_ALOAD_0)
+                        {
+                            producerOffset = PartialEvaluator.AT_METHOD_ENTRY;
+                        }
+
+                        int initializationOffset = producerOffset == PartialEvaluator.AT_METHOD_ENTRY ?
+                            partialEvaluator.superInitializationOffset() :
+                            partialEvaluator.initializationOffset(producerOffset);
+                        if (initializationOffset != PartialEvaluator.NONE)
+                        {
+                            // 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 ordinary Object type.
+                return VerificationTypeFactory.createObjectType(createClassConstant(programClass, referenceValue));
+        }
+
+        throw new IllegalArgumentException("Unknown computational type ["+type+"]");
+    }
+
+
+    /**
+     * Finds or creates a class constant for the given reference value, and
+     * returns its index in the constant pool.
+     */
+    private int createClassConstant(ProgramClass   programClass,
+                                    ReferenceValue referenceValue)
+    {
+        return constantPoolEditor.addClassConstant(programClass,
+                                                   referenceValue.getType(),
+                                                   referenceValue.getReferencedClass());
+    }
+
+
+    /**
+     * Compresses the given list of full frames, for use in a stack map table.
+     */
+    private void compressStackMapFrames(VerificationType[] initialVariableTypes,
+                                        List               stackMapFrameList)
+    {
+        int                previousVariablesCount = initialVariableTypes.length;
+        VerificationType[] previousVariableTypes  = initialVariableTypes;
+
+        int previousOffset = -1;
+
+        for (int index = 0; index < stackMapFrameList.size(); index++)
+        {
+            FullFrame fullFrame = (FullFrame)stackMapFrameList.get(index);
+
+            int                variablesCount = fullFrame.variablesCount;
+            VerificationType[] variables      = fullFrame.variables;
+            int                stackCount     = fullFrame.stackCount;
+            VerificationType[] stack          = fullFrame.stack;
+
+            // Start computing the compressed frame.
+            // The default is the full frame.
+            StackMapFrame compressedFrame = fullFrame;
+
+            // Are all variables equal?
+            if (variablesCount == previousVariablesCount &&
+                equalVerificationTypes(variables, previousVariableTypes, variablesCount))
+            {
+                // Are the stacks equal?
+                //if (stackCount == previousStackCount &&
+                //    equalVerificationTypes(stack, previousStack, stackCount))
+                //{
+                //    // Remove the identical frame.
+                //    stackMapFrameList.remove(index--);
+                //
+                //    // Move on to the next frame (at the same index).
+                //    continue;
+                //}
+                // Is the new stack empty?
+                //else
+                if (stackCount == 0)
+                {
+                    compressedFrame = new SameZeroFrame();
+                }
+                // Does the new stack contain a single element?
+                else if (stackCount == 1)
+                {
+                    compressedFrame = new SameOneFrame(stack[0]);
+                }
+            }
+            // Is the stack empty?
+            else if (stackCount == 0)
+            {
+                int additionalVariablesCount = variablesCount - previousVariablesCount;
+
+                // Are the variables chopped?
+                if (additionalVariablesCount < 0  &&
+                    additionalVariablesCount > -4 &&
+                    equalVerificationTypes(variables, previousVariableTypes, variablesCount))
+                {
+                    compressedFrame = new LessZeroFrame((byte)-additionalVariablesCount);
+                }
+                // Are the variables extended?
+                else if (//previousVariablesCount   > 0 &&
+                         additionalVariablesCount > 0 &&
+                         additionalVariablesCount < 4 &&
+                         equalVerificationTypes(variables, previousVariableTypes, previousVariablesCount))
+                {
+                    // Copy the additional variables into an array.
+                    VerificationType[] additionalVariables = new VerificationType[additionalVariablesCount];
+                    System.arraycopy(variables, variablesCount - additionalVariablesCount,
+                                     additionalVariables, 0,
+                                     additionalVariablesCount);
+
+                    compressedFrame = new MoreZeroFrame(additionalVariables);
+                }
+            }
+
+            // Compress the instruction offset.
+            int offset = fullFrame.u2offsetDelta;
+            compressedFrame.u2offsetDelta = offset - previousOffset - 1;
+            previousOffset = offset;
+
+            // Remember this frame.
+            previousVariablesCount = fullFrame.variablesCount;
+            previousVariableTypes     = fullFrame.variables;
+
+            // Replace the full frame.
+            stackMapFrameList.set(index, compressedFrame);
+        }
+    }
+
+
+    /**
+     * Returns whether the given arrays of verification types are equal, up to
+     * the given length.
+     */
+    private boolean equalVerificationTypes(VerificationType[] types1,
+                                           VerificationType[] types2,
+                                           int                length)
+    {
+        if (length > 0 &&
+            (types1.length < length ||
+             types2.length < length))
+        {
+            return false;
+        }
+
+        for (int index = 0; index < length; index++)
+        {
+            if (!types1[index].equals(types2[index]))
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/src/proguard/preverify/CodeSubroutineInliner.java b/src/proguard/preverify/CodeSubroutineInliner.java
new file mode 100644
index 0000000..5f448e3
--- /dev/null
+++ b/src/proguard/preverify/CodeSubroutineInliner.java
@@ -0,0 +1,378 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.preverify;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.editor.CodeAttributeComposer;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ExceptionExcludedOffsetFilter;
+import proguard.optimize.peephole.BranchTargetFinder;
+
+/**
+ * This AttributeVisitor inlines local subroutines (jsr/ret) in the code
+ * attributes that it visits.
+ *
+ * @author Eric Lafortune
+ */
+public class CodeSubroutineInliner
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InstructionVisitor,
+             ExceptionInfoVisitor
+{
+    //*
+    private static final boolean DEBUG = false;
+    /*/
+    private static       boolean DEBUG = true;
+    //*/
+
+
+    private final BranchTargetFinder    branchTargetFinder    = new BranchTargetFinder();
+    private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer();
+
+    private boolean              inlinedAny;
+    private ExceptionInfoVisitor subroutineExceptionInliner;
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+//        DEBUG =
+//            clazz.getName().equals("abc/Def") &&
+//            method.getName(clazz).equals("abc");
+
+        branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);
+
+        // Don't bother if there aren't any subroutines anyway.
+        if (!containsSubroutines(codeAttribute))
+        {
+            return;
+        }
+
+        inlinedAny                 = false;
+        subroutineExceptionInliner = this;
+        codeAttributeComposer.reset();
+
+        // Append the body of the code.
+        copyCode(clazz, method, codeAttribute);
+
+        // Update the code attribute if any code has been inlined.
+        if (inlinedAny)
+        {
+            codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
+        }
+    }
+
+
+    /**
+     * Returns whether the given code attribute contains any subroutines.
+     */
+    private boolean containsSubroutines(CodeAttribute codeAttribute)
+    {
+        for (int offset = 0; offset < codeAttribute.u4codeLength; offset++)
+        {
+            if (branchTargetFinder.isSubroutineInvocation(offset))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Appends the code of the given code attribute.
+     */
+    private void copyCode(Clazz         clazz,
+                          Method        method,
+                          CodeAttribute codeAttribute)
+    {
+        if (DEBUG)
+        {
+            System.out.println("SubroutineInliner: processing ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+        }
+
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
+
+        // Copy the non-subroutine instructions.
+        int offset  = 0;
+        while (offset < codeAttribute.u4codeLength)
+        {
+            Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
+            int instructionLength = instruction.length(offset);
+
+            // Is this returning subroutine?
+            if (branchTargetFinder.isSubroutine(offset) &&
+                branchTargetFinder.isSubroutineReturning(offset))
+            {
+                // Skip the subroutine.
+                if (DEBUG)
+                {
+                    System.out.println("Skipping subroutine at ["+offset+"]");
+                }
+
+                // Append a label at this offset instead.
+                codeAttributeComposer.appendLabel(offset);
+            }
+            else
+            {
+                // Copy the instruction, inlining any subroutine call recursively.
+                instruction.accept(clazz, method, codeAttribute, offset, this);
+            }
+
+            offset += instructionLength;
+        }
+
+        // Copy the exceptions. Note that exceptions with empty try blocks
+        // are automatically removed.
+        codeAttribute.exceptionsAccept(clazz, method, this);
+
+        if (DEBUG)
+        {
+            System.out.println("Appending label after code at ["+offset+"]");
+        }
+
+        // Append a label just after the code.
+        codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);
+
+        codeAttributeComposer.endCodeFragment();
+    }
+
+
+    /**
+     * Appends the specified subroutine.
+     */
+    private void inlineSubroutine(Clazz         clazz,
+                                  Method        method,
+                                  CodeAttribute codeAttribute,
+                                  int           subroutineInvocationOffset,
+                                  int           subroutineStart)
+    {
+        int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart);
+
+        if (DEBUG)
+        {
+            System.out.println("Inlining subroutine ["+subroutineStart+" -> "+subroutineEnd+"] at ["+subroutineInvocationOffset+"]");
+        }
+
+        // Don't go inlining exceptions that are already applicable to this
+        // subroutine invocation.
+        ExceptionInfoVisitor oldSubroutineExceptionInliner =
+            subroutineExceptionInliner;
+
+        subroutineExceptionInliner =
+            new ExceptionExcludedOffsetFilter(subroutineInvocationOffset,
+                                              subroutineExceptionInliner);
+
+        codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);
+
+        // Copy the subroutine instructions, inlining any subroutine calls
+        // recursively.
+        codeAttribute.instructionsAccept(clazz,
+                                         method,
+                                         subroutineStart,
+                                         subroutineEnd,
+                                         this);
+
+        if (DEBUG)
+        {
+            System.out.println("Appending label after inlined subroutine at ["+subroutineEnd+"]");
+        }
+
+        // Append a label just after the code.
+        codeAttributeComposer.appendLabel(subroutineEnd);
+
+        // We can again inline exceptions that are applicable to this
+        // subroutine invocation.
+        subroutineExceptionInliner = oldSubroutineExceptionInliner;
+
+        // Inline the subroutine exceptions.
+        codeAttribute.exceptionsAccept(clazz,
+                                       method,
+                                       subroutineStart,
+                                       subroutineEnd,
+                                       subroutineExceptionInliner);
+
+        codeAttributeComposer.endCodeFragment();
+
+        inlinedAny = true;
+    }
+
+
+    // Implementations for InstructionVisitor.
+
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
+    {
+        // Append the instruction.
+        codeAttributeComposer.appendInstruction(offset, instruction.shrink());
+    }
+
+
+    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+    {
+        byte opcode = variableInstruction.opcode;
+        if (opcode == InstructionConstants.OP_RET)
+        {
+            // Is the return instruction the last instruction of the subroutine?
+            if (branchTargetFinder.subroutineEnd(offset) == offset + variableInstruction.length(offset))
+            {
+                if (DEBUG)
+                {
+                    System.out.println("Replacing subroutine return at ["+offset+"] by a label");
+                }
+
+                // Append a label at this offset instead of the subroutine return.
+                codeAttributeComposer.appendLabel(offset);
+            }
+            else
+            {
+                if (DEBUG)
+                {
+                    System.out.println("Replacing subroutine return at ["+offset+"] by a simple branch");
+                }
+
+                // Replace the instruction by a branch.
+                Instruction replacementInstruction =
+                    new BranchInstruction(InstructionConstants.OP_GOTO,
+                                          branchTargetFinder.subroutineEnd(offset) - offset).shrink();
+
+                codeAttributeComposer.appendInstruction(offset, replacementInstruction);
+            }
+        }
+        else if (branchTargetFinder.isSubroutineStart(offset))
+        {
+            if (DEBUG)
+            {
+                System.out.println("Replacing first subroutine instruction at ["+offset+"] by a label");
+            }
+
+            // Append a label at this offset instead of saving the subroutine
+            // return address.
+            codeAttributeComposer.appendLabel(offset);
+        }
+        else
+        {
+            // Append the instruction.
+            codeAttributeComposer.appendInstruction(offset, variableInstruction);
+        }
+    }
+
+
+    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+    {
+        byte opcode = branchInstruction.opcode;
+        if (opcode == InstructionConstants.OP_JSR ||
+            opcode == InstructionConstants.OP_JSR_W)
+        {
+            int branchOffset = branchInstruction.branchOffset;
+            int branchTarget = offset + branchOffset;
+
+            // Is the subroutine ever returning?
+            if (branchTargetFinder.isSubroutineReturning(branchTarget))
+            {
+                // Append a label at this offset instead of the subroutine invocation.
+                codeAttributeComposer.appendLabel(offset);
+
+                // Inline the invoked subroutine.
+                inlineSubroutine(clazz,
+                                 method,
+                                 codeAttribute,
+                                 offset,
+                                 branchTarget);
+            }
+            else
+            {
+                if (DEBUG)
+                {
+                    System.out.println("Replacing subroutine invocation at ["+offset+"] by a simple branch");
+                }
+
+                // Replace the subroutine invocation by a simple branch.
+                Instruction replacementInstruction =
+                    new BranchInstruction(InstructionConstants.OP_GOTO,
+                                          branchOffset).shrink();
+
+                codeAttributeComposer.appendInstruction(offset, replacementInstruction);
+
+                inlinedAny = true;
+            }
+        }
+        else
+        {
+            // Append the instruction.
+            codeAttributeComposer.appendInstruction(offset, branchInstruction);
+        }
+    }
+
+
+    // Implementations for ExceptionInfoVisitor.
+
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
+    {
+        int startPC   = exceptionInfo.u2startPC;
+        int endPC     = exceptionInfo.u2endPC;
+        int handlerPC = exceptionInfo.u2handlerPC;
+        int catchType = exceptionInfo.u2catchType;
+
+        // Exclude any subroutine invocations that jump out of the try block,
+        // by adding a try block before (and later on, after) each invocation.
+        int offset = startPC;
+        while (offset < endPC)
+        {
+            Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
+            int instructionLength = instruction.length(offset);
+
+            // Is it a subroutine invocation?
+            if (branchTargetFinder.isSubroutineInvocation(offset) &&
+                !exceptionInfo.isApplicable(offset + ((BranchInstruction)instruction).branchOffset))
+            {
+                // Append a try block that ends before the subroutine invocation.
+                codeAttributeComposer.appendException(new ExceptionInfo(startPC,
+                                                                        offset,
+                                                                        handlerPC,
+                                                                        catchType));
+
+                // The next try block will start after the subroutine invocation.
+                startPC = offset + instructionLength;
+            }
+
+            offset += instructionLength;
+        }
+
+        // Append the exception. Note that exceptions with empty try blocks
+        // are automatically ignored.
+        codeAttributeComposer.appendException(new ExceptionInfo(startPC,
+                                                                endPC,
+                                                                handlerPC,
+                                                                catchType));
+
+        // TODO: While inlining a subroutine, its exception handler code may have to be inlined too.
+    }
+}
diff --git a/src/proguard/preverify/Preverifier.java b/src/proguard/preverify/Preverifier.java
new file mode 100644
index 0000000..3a2cc2b
--- /dev/null
+++ b/src/proguard/preverify/Preverifier.java
@@ -0,0 +1,74 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.preverify;
+
+import proguard.Configuration;
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AllAttributeVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This class can preverify methods in program class pools, according to a given
+ * configuration.
+ *
+ * @author Eric Lafortune
+ */
+public class Preverifier
+{
+    private final Configuration configuration;
+
+
+    /**
+     * Creates a new Preverifier.
+     */
+    public Preverifier(Configuration configuration)
+    {
+        this.configuration = configuration;
+    }
+
+
+    /**
+     * Performs preverification of the given program class pool.
+     */
+    public void execute(ClassPool programClassPool)
+    {
+        // Clean up any old visitor info.
+        programClassPool.classesAccept(new ClassCleaner());
+
+        // Preverify all methods.
+        ClassVisitor preverifier =
+            new AllMethodVisitor(
+            new AllAttributeVisitor(
+            new CodePreverifier(configuration.microEdition)));
+
+        // In Java Standard Edition, only class files from Java 6 or higher
+        // should be preverified.
+        if (!configuration.microEdition)
+        {
+            preverifier =
+                new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_6,
+                                       Integer.MAX_VALUE,
+                                       preverifier);
+        }
+
+        programClassPool.classesAccept(preverifier);
+    }
+}
diff --git a/src/proguard/preverify/SubroutineInliner.java b/src/proguard/preverify/SubroutineInliner.java
new file mode 100644
index 0000000..8e46fca
--- /dev/null
+++ b/src/proguard/preverify/SubroutineInliner.java
@@ -0,0 +1,74 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.preverify;
+
+import proguard.Configuration;
+import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.AllAttributeVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This class can inline subroutines in methods. This is generally useful (i.e.
+ * required) for preverifying code.
+ *
+ * @author Eric Lafortune
+ */
+public class SubroutineInliner
+{
+    private final Configuration configuration;
+
+
+    /**
+     * Creates a new SubroutineInliner.
+     */
+    public SubroutineInliner(Configuration configuration)
+    {
+        this.configuration = configuration;
+    }
+
+
+    /**
+     * Performs subroutine inlining of the given program class pool.
+     */
+    public void execute(ClassPool programClassPool)
+    {
+        // Clean up any old visitor info.
+        programClassPool.classesAccept(new ClassCleaner());
+
+        // Inline all subroutines.
+        ClassVisitor inliner =
+            new AllMethodVisitor(
+            new AllAttributeVisitor(
+            new CodeSubroutineInliner()));
+
+        // In Java Standard Edition, only class files from Java 6 or higher
+        // should be preverified.
+        if (!configuration.microEdition)
+        {
+            inliner =
+                new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_6,
+                                       Integer.MAX_VALUE,
+                                       inliner);
+        }
+
+        programClassPool.classesAccept(inliner);
+    }
+}
diff --git a/src/proguard/retrace/ReTrace.java b/src/proguard/retrace/ReTrace.java
index f758db2..f19c995 100644
--- a/src/proguard/retrace/ReTrace.java
+++ b/src/proguard/retrace/ReTrace.java
@@ -1,6 +1,6 @@
-/* $Id: ReTrace.java,v 1.10.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,10 +20,10 @@
  */
 package proguard.retrace;
 
-import java.io.*;
-
 import proguard.obfuscate.MappingReader;
 
+import java.io.*;
+
 
 /**
  * Tool for de-obfuscating stack traces of applications that were obfuscated
@@ -37,9 +37,9 @@ public class ReTrace
 
 
     // The class settings.
-    private boolean verbose;
-    private File    mappingFile;
-    private File    stackTraceFile;
+    private final boolean verbose;
+    private final File    mappingFile;
+    private final File    stackTraceFile;
 
 
     /**
@@ -122,7 +122,7 @@ public class ReTrace
 
         File mappingFile    = new File(args[argumentIndex++]);
         File stackTraceFile = argumentIndex < args.length ?
-            new File(args[argumentIndex++]) :
+            new File(args[argumentIndex]) :
             null;
 
         ReTrace reTrace = new ReTrace(verbose, mappingFile, stackTraceFile);
diff --git a/src/proguard/retrace/StackTrace.java b/src/proguard/retrace/StackTrace.java
index ce302a5..91332ae 100644
--- a/src/proguard/retrace/StackTrace.java
+++ b/src/proguard/retrace/StackTrace.java
@@ -1,6 +1,6 @@
-/* $Id: StackTrace.java,v 1.8.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,11 @@
  */
 package proguard.retrace;
 
+import proguard.obfuscate.MappingProcessor;
+
 import java.io.*;
 import java.util.*;
 
-import proguard.obfuscate.MappingProcessor;
-
 
 /**
  * This class represents an obfuscated stack trace. It can read, de-obfuscate,
@@ -32,13 +32,13 @@ import proguard.obfuscate.MappingProcessor;
  *
  * @author Eric Lafortune
  */
-class StackTrace implements MappingProcessor
+final class StackTrace implements MappingProcessor
 {
     // The stack trace settings.
-    private boolean verbose;
+    private final boolean verbose;
 
     // The stack trace items.
-    private List stackTraceItems = new ArrayList();
+    private final List stackTraceItems = new ArrayList();
 
 
     /**
@@ -105,6 +105,7 @@ class StackTrace implements MappingProcessor
                 }
                 catch (IOException ex)
                 {
+                    // This shouldn't happen.
                 }
             }
         }
@@ -128,8 +129,8 @@ class StackTrace implements MappingProcessor
 
     // Implementations for MappingProcessor.
 
-    public boolean processClassFileMapping(String className,
-                                           String newClassName)
+    public boolean processClassMapping(String className,
+                                       String newClassName)
     {
         // Delegate to each of the stack trace items.
         boolean present = false;
@@ -137,8 +138,8 @@ class StackTrace implements MappingProcessor
         {
             StackTraceItem item = (StackTraceItem)stackTraceItems.get(index);
 
-            present |= item.processClassFileMapping(className,
-                                                    newClassName);
+            present |= item.processClassMapping(className,
+                                                newClassName);
         }
 
         return present;
diff --git a/src/proguard/retrace/StackTraceItem.java b/src/proguard/retrace/StackTraceItem.java
index 8146bef..0153b98 100644
--- a/src/proguard/retrace/StackTraceItem.java
+++ b/src/proguard/retrace/StackTraceItem.java
@@ -1,6 +1,6 @@
-/* $Id: StackTraceItem.java,v 1.9.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,11 +20,11 @@
  */
 package proguard.retrace;
 
-import java.io.*;
-import java.util.*;
-
 import proguard.obfuscate.MappingProcessor;
 
+import java.io.IOException;
+import java.util.*;
+
 
 /**
  * This class represents an obfuscated stack trace item. It can read, de-obfuscate,
@@ -32,10 +32,10 @@ import proguard.obfuscate.MappingProcessor;
  *
  * @author Eric Lafortune
  */
-class StackTraceItem implements MappingProcessor
+final class StackTraceItem implements MappingProcessor
 {
     // The stack trace settings.
-    private boolean verbose;
+    private final boolean verbose;
 
     public String prefix;
     public String obfuscatedClassName;
@@ -177,36 +177,33 @@ class StackTraceItem implements MappingProcessor
             sourceFile + ":" + lineNumber :
             sourceFile;
 
-        // Print out the resolved stack trace
-        if (prefix != null)
-        {
-            System.out.print(prefix);
-        }
+        // 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)
+            if (methodName != 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);
+                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);
+            if (suffix != null)
+            {
+                System.out.print(suffix);
+            }
         }
 
         System.out.println();
@@ -225,8 +222,8 @@ class StackTraceItem implements MappingProcessor
 
     // Implementations for MappingProcessor.
 
-    public boolean processClassFileMapping(String className,
-                                           String newClassName)
+    public boolean processClassMapping(String className,
+                                       String newClassName)
     {
         boolean present = false;
 
diff --git a/src/proguard/shrink/AnnotationUsageMarker.java b/src/proguard/shrink/AnnotationUsageMarker.java
new file mode 100644
index 0000000..bdd7568
--- /dev/null
+++ b/src/proguard/shrink/AnnotationUsageMarker.java
@@ -0,0 +1,359 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.Attribute;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This AttributeVisitor recursively marks all necessary annotation information
+ * in the attributes that it visits.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class AnnotationUsageMarker
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor,
+             ConstantVisitor,
+             ClassVisitor,
+             MemberVisitor
+{
+    private final UsageMarker usageMarker;
+
+    // Fields acting as a return parameters for several methods.
+    private boolean attributeUsed;
+    private boolean annotationUsed;
+    private boolean elementValueUsed;
+    private boolean classUsed;
+    private boolean methodUsed;
+
+
+    /**
+     * Creates a new AnnotationUsageMarker.
+     * @param usageMarker the usage marker that is used to mark the classes
+     *                    and class members.
+     */
+    public AnnotationUsageMarker(UsageMarker usageMarker)
+    {
+        this.usageMarker = usageMarker;
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Mark the necessary annotation information.
+        attributeUsed = false;
+        annotationsAttribute.annotationsAccept(clazz, this);
+
+        if (attributeUsed)
+        {
+            // We got a positive used flag, so some annotation is being used.
+            // Mark this attribute as being used as well.
+            usageMarker.markAsUsed(annotationsAttribute);
+
+            markConstant(clazz, annotationsAttribute.u2attributeNameIndex);
+        }
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Mark the necessary annotation information.
+        attributeUsed = false;
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+
+        if (attributeUsed)
+        {
+            // We got a positive used flag, so some annotation is being used.
+            // Mark this attribute as being used as well.
+            usageMarker.markAsUsed(parameterAnnotationsAttribute);
+
+            markConstant(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
+        }
+    }
+
+
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
+    {
+        // Mark the necessary annotation information in any annotation elements.
+        annotationDefaultAttribute.defaultValueAccept(clazz, this);
+
+        // Always mark annotation defaults.
+        usageMarker.markAsUsed(annotationDefaultAttribute);
+
+        markConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        if (isReferencedClassUsed(annotation))
+        {
+            // Mark the annotation as being used.
+            usageMarker.markAsUsed(annotation);
+
+            markConstant(clazz, annotation.u2typeIndex);
+
+            // Mark the necessary element values.
+            annotation.elementValuesAccept(clazz, this);
+
+            // The return values.
+            annotationUsed = true;
+            attributeUsed  = true;
+        }
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+    {
+        if (isReferencedMethodUsed(constantElementValue))
+        {
+            // Mark the element value as being used.
+            usageMarker.markAsUsed(constantElementValue);
+
+            markConstant(clazz, constantElementValue.u2elementNameIndex);
+            markConstant(clazz, constantElementValue.u2constantValueIndex);
+
+            // The return value.
+            elementValueUsed = true;
+        }
+    }
+
+
+    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+    {
+        if (isReferencedMethodUsed(enumConstantElementValue))
+        {
+            // Check the referenced classes.
+            classUsed = true;
+            enumConstantElementValue.referencedClassesAccept(usageMarker);
+
+            if (classUsed)
+            {
+                // Mark the element value as being used.
+                usageMarker.markAsUsed(enumConstantElementValue);
+
+                markConstant(clazz, enumConstantElementValue.u2elementNameIndex);
+                markConstant(clazz, enumConstantElementValue.u2typeNameIndex);
+                markConstant(clazz, enumConstantElementValue.u2constantNameIndex);
+
+                // The return value.
+                elementValueUsed = true;
+            }
+        }
+    }
+
+
+    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+    {
+        if (isReferencedMethodUsed(classElementValue))
+        {
+            // Check the referenced classes.
+            classUsed = true;
+            classElementValue.referencedClassesAccept(usageMarker);
+
+            if (classUsed)
+            {
+                // Mark the element value as being used.
+                usageMarker.markAsUsed(classElementValue);
+
+                markConstant(clazz, classElementValue.u2elementNameIndex);
+                markConstant(clazz, classElementValue.u2classInfoIndex);
+
+                // The return value.
+                elementValueUsed = true;
+            }
+        }
+    }
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        if (isReferencedMethodUsed(annotationElementValue))
+        {
+            boolean oldAnnotationUsed = annotationUsed;
+
+            // Check and mark the contained annotation.
+            annotationUsed = false;
+            annotationElementValue.annotationAccept(clazz, this);
+
+            if (annotationUsed)
+            {
+                // Mark the element value as being used.
+                usageMarker.markAsUsed(annotationElementValue);
+
+                markConstant(clazz, annotationElementValue.u2elementNameIndex);
+
+                // The return value.
+                elementValueUsed = true;
+            }
+
+            annotationUsed = oldAnnotationUsed;
+        }
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        if (isReferencedMethodUsed(arrayElementValue))
+        {
+            boolean oldelementValueUsed = elementValueUsed;
+
+            // Check and mark the contained element values.
+            elementValueUsed = false;
+            arrayElementValue.elementValuesAccept(clazz, annotation, this);
+
+            if (elementValueUsed)
+            {
+                // Mark the element value as being used.
+                usageMarker.markAsUsed(arrayElementValue);
+
+                markConstant(clazz, arrayElementValue.u2elementNameIndex);
+
+                // The return value.
+                //elementValueUsed = true;
+            }
+            else
+            {
+                elementValueUsed = oldelementValueUsed;
+            }
+        }
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitAnyConstant(Clazz clazz, Constant constant)
+    {
+        usageMarker.markAsUsed(constant);
+    }
+
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        classUsed = usageMarker.isUsed(classConstant);
+
+        // Is the class constant marked as being used?
+        if (!classUsed)
+        {
+            // Check the referenced class.
+            classUsed = true;
+            classConstant.referencedClassAccept(this);
+
+            // Is the referenced class marked as being used?
+            if (classUsed)
+            {
+                // Mark the class constant and its Utf8 constant.
+                usageMarker.markAsUsed(classConstant);
+
+                markConstant(clazz, classConstant.u2nameIndex);
+            }
+        }
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        classUsed = usageMarker.isUsed(programClass);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        classUsed = true;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        methodUsed = usageMarker.isUsed(programMethod);
+    }
+
+
+    public void visitLibraryMethod(LibraryClass LibraryClass, LibraryMethod libraryMethod)
+    {
+        classUsed = true;
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the annotation class has been marked as being used.
+     */
+    private boolean isReferencedClassUsed(Annotation annotation)
+    {
+        // Check if the referenced class is being used.
+        classUsed = true;
+        annotation.referencedClassAccept(this);
+
+        return classUsed;
+    }
+
+
+    /**
+     * Returns whether the annotation method has been marked as being used.
+     */
+    private boolean isReferencedMethodUsed(ElementValue elementValue)
+    {
+        // Check if the referenced method is being used.
+        methodUsed = true;
+        elementValue.referencedMethodAccept(this);
+
+        return methodUsed;
+    }
+
+
+    /**
+     * Marks the specified constant pool entry.
+     */
+    private void markConstant(Clazz clazz, int index)
+    {
+        if (index > 0)
+        {
+            clazz.constantPoolEntryAccept(index, this);
+        }
+    }
+}
diff --git a/src/proguard/shrink/ClassFileShrinker.java b/src/proguard/shrink/ClassFileShrinker.java
deleted file mode 100644
index 32520f7..0000000
--- a/src/proguard/shrink/ClassFileShrinker.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/* $Id: ClassFileShrinker.java,v 1.27.2.3 2007/01/25 21:01:01 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.shrink;
-
-import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.editor.ConstantPoolRemapper;
-import proguard.classfile.instruction.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This ClassFileVisitor removes constant pool entries and class members that
- * are not marked as being used.
- *
- * @see UsageMarker
- *
- * @author Eric Lafortune
- */
-public class ClassFileShrinker
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             AttrInfoVisitor
-{
-    private UsageMarker          usageMarker;
-    private ConstantPoolRemapper constantPoolRemapper;
-    private int[]                cpIndexMap;
-
-
-    /**
-     * Creates a new ClassFileShrinker.
-     * @param usageMarker the usage marker that is used to mark the classes
-     *                    and class members.
-     * @param codeLength an estimate of the maximum length of all the code that
-     *                   will be edited.
-     */
-    public ClassFileShrinker(UsageMarker usageMarker,
-                             int         codeLength)
-    {
-        this.usageMarker          = usageMarker;
-        this.constantPoolRemapper = new ConstantPoolRemapper(codeLength);
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        // Shrink the arrays for constant pool, interfaces, fields, methods,
-        // and class attributes.
-        programClassFile.u2interfacesCount =
-            shrinkCpIndexArray(programClassFile.constantPool,
-                               programClassFile.u2interfaces,
-                               programClassFile.u2interfacesCount);
-
-        // Shrinking the constant pool also sets up an index map.
-        programClassFile.u2constantPoolCount =
-            shrinkConstantPool(programClassFile.constantPool,
-                               programClassFile.u2constantPoolCount);
-
-        programClassFile.u2fieldsCount =
-            shrinkArray(programClassFile.fields,
-                        programClassFile.u2fieldsCount);
-
-        programClassFile.u2methodsCount =
-            shrinkArray(programClassFile.methods,
-                        programClassFile.u2methodsCount);
-
-        programClassFile.u2attributesCount =
-            shrinkArray(programClassFile.attributes,
-                        programClassFile.u2attributesCount);
-
-        // Compact the remaining fields, methods, and attributes,
-        // and remap their references to the constant pool.
-        programClassFile.fieldsAccept(this);
-        programClassFile.methodsAccept(this);
-        programClassFile.attributesAccept(this);
-
-        // Remap all constant pool references.
-        constantPoolRemapper.setCpIndexMap(cpIndexMap);
-        constantPoolRemapper.visitProgramClassFile(programClassFile);
-
-        // Compact the extra field pointing to the subclasses of this class.
-        programClassFile.subClasses =
-            shrinkToNewArray(programClassFile.subClasses);
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // Library class files are left unchanged.
-
-        // Compact the extra field pointing to the subclasses of this class.
-        libraryClassFile.subClasses =
-            shrinkToNewArray(libraryClassFile.subClasses);
-    }
-
-
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
-    {
-        visitMemberInfo(programClassFile, programFieldInfo);
-    }
-
-
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
-    {
-        visitMemberInfo(programClassFile, programMethodInfo);
-    }
-
-
-    private void visitMemberInfo(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
-    {
-        // Compact the attributes array.
-        programMemberInfo.u2attributesCount =
-            shrinkArray(programMemberInfo.attributes,
-                        programMemberInfo.u2attributesCount);
-
-        // Compact any attributes.
-        programMemberInfo.attributesAccept(programClassFile, this);
-    }
-
-
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
-    {
-        // Library class files are left unchanged.
-    }
-
-
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
-    {
-        // Library class files are left unchanged.
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        // Compact the array of InnerClassesInfo objects.
-        innerClassesAttrInfo.u2numberOfClasses =
-            shrinkArray(innerClassesAttrInfo.classes,
-                        innerClassesAttrInfo.u2numberOfClasses);
-    }
-
-
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
-    {
-        // Sometimes, a class is still referenced (apparently as a dummy class),
-        // but its enclosing method is not. Then remove the reference to
-        // the enclosing method.
-        // E.g. the anonymous inner class javax.swing.JList$1 is defined inside
-        // a constructor of javax.swing.JList, but it is also referenced as a
-        // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler.
-        if (enclosingMethodAttrInfo.referencedMethodInfo != null &&
-            !usageMarker.isUsed(enclosingMethodAttrInfo.referencedMethodInfo))
-        {
-            enclosingMethodAttrInfo.u2nameAndTypeIndex = 0;
-
-            enclosingMethodAttrInfo.referencedMethodInfo = null;
-        }
-    }
-
-
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
-    {
-        // Compact the attributes array.
-        codeAttrInfo.u2attributesCount =
-            shrinkArray(codeAttrInfo.attributes,
-                        codeAttrInfo.u2attributesCount);
-    }
-
-
-    // Small utility methods.
-
-    /**
-     * Removes all entries that are not marked as being used from the given
-     * constant pool.
-     * @return the new number of entries.
-     */
-    private int shrinkConstantPool(CpInfo[] constantPool, int length)
-    {
-        if (cpIndexMap == null ||
-            cpIndexMap.length < length)
-        {
-            cpIndexMap = new int[length];
-        }
-
-        int     counter = 1;
-        boolean isUsed  = false;
-
-        // Shift the used constant pool entries together.
-        for (int index = 1; index < length; index++)
-        {
-            cpIndexMap[index] = counter;
-
-            CpInfo cpInfo = constantPool[index];
-
-            // Don't update the flag if this is the second half of a long entry.
-            if (cpInfo != null)
-            {
-                isUsed = usageMarker.isUsed(cpInfo);
-            }
-
-            if (isUsed)
-            {
-                constantPool[counter++] = cpInfo;
-            }
-        }
-
-        // Clear the remaining constant pool elements.
-        for (int index = counter; index < length; index++)
-        {
-            constantPool[index] = null;
-        }
-
-        return counter;
-    }
-
-
-    /**
-     * Removes all indices that point to unused constant pool entries
-     * from the given array.
-     * @return the new number of indices.
-     */
-    private int shrinkCpIndexArray(CpInfo[] constantPool, int[] array, int length)
-    {
-        int counter = 0;
-
-        // Shift the used objects together.
-        for (int index = 0; index < length; index++)
-        {
-            if (usageMarker.isUsed(constantPool[array[index]]))
-            {
-                array[counter++] = array[index];
-            }
-        }
-
-        // Clear the remaining array elements.
-        for (int index = counter; index < length; index++)
-        {
-            array[index] = 0;
-        }
-
-        return counter;
-    }
-
-
-    /**
-     * Removes all ClassFile objects that are not marked as being used
-     * from the given array and returns the remaining objects in a an array
-     * of the right size.
-     * @return the new array.
-     */
-    private ClassFile[] shrinkToNewArray(ClassFile[] array)
-    {
-        if (array == null)
-        {
-            return null;
-        }
-
-        // Shrink the given array in-place.
-        int length = shrinkArray(array, array.length);
-        if (length == 0)
-        {
-            return null;
-        }
-
-        // Return immediately if the array is of right size already.
-        if (length == array.length)
-        {
-            return array;
-        }
-
-        // Copy the remaining elements into a new array of the right size.
-        ClassFile[] newArray = new ClassFile[length];
-        System.arraycopy(array, 0, newArray, 0, length);
-        return newArray;
-    }
-
-
-    /**
-     * Removes all VisitorAccepter objects that are not marked as being used
-     * from the given array.
-     * @return the new number of VisitorAccepter objects.
-     */
-    private int shrinkArray(VisitorAccepter[] array, int length)
-    {
-        int counter = 0;
-
-        // Shift the used objects together.
-        for (int index = 0; index < length; index++)
-        {
-            if (usageMarker.isUsed(array[index]))
-            {
-                array[counter++] = array[index];
-            }
-        }
-
-        // Clear the remaining array elements.
-        for (int index = counter; index < length; index++)
-        {
-            array[index] = null;
-        }
-
-        return counter;
-    }
-}
diff --git a/src/proguard/shrink/ClassShrinker.java b/src/proguard/shrink/ClassShrinker.java
new file mode 100644
index 0000000..bf49e34
--- /dev/null
+++ b/src/proguard/shrink/ClassShrinker.java
@@ -0,0 +1,375 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.annotation.visitor.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.Constant;
+import proguard.classfile.editor.ConstantPoolRemapper;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.*;
+
+/**
+ * This ClassVisitor removes constant pool entries and class members that
+ * are not marked as being used.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class ClassShrinker
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor,
+             AttributeVisitor,
+             AnnotationVisitor,
+             ElementValueVisitor
+{
+    private final UsageMarker usageMarker;
+
+    private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
+
+    private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
+
+
+    /**
+     * Creates a new ClassShrinker.
+     * @param usageMarker the usage marker that is used to mark the classes
+     *                    and class members.
+     */
+    public ClassShrinker(UsageMarker usageMarker)
+    {
+        this.usageMarker = usageMarker;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        // Shrink the arrays for constant pool, interfaces, fields, methods,
+        // and class attributes.
+        programClass.u2interfacesCount =
+            shrinkConstantIndexArray(programClass.constantPool,
+                                     programClass.u2interfaces,
+                                     programClass.u2interfacesCount);
+
+        // Shrinking the constant pool also sets up an index map.
+        programClass.u2constantPoolCount =
+            shrinkConstantPool(programClass.constantPool,
+                               programClass.u2constantPoolCount);
+
+        programClass.u2fieldsCount =
+            shrinkArray(programClass.fields,
+                        programClass.u2fieldsCount);
+
+        programClass.u2methodsCount =
+            shrinkArray(programClass.methods,
+                        programClass.u2methodsCount);
+
+        programClass.u2attributesCount =
+            shrinkArray(programClass.attributes,
+                        programClass.u2attributesCount);
+
+        // Compact the remaining fields, methods, and attributes,
+        // and remap their references to the constant pool.
+        programClass.fieldsAccept(this);
+        programClass.methodsAccept(this);
+        programClass.attributesAccept(this);
+
+        // Remap all constant pool references.
+        constantPoolRemapper.setConstantIndexMap(constantIndexMap);
+        constantPoolRemapper.visitProgramClass(programClass);
+
+        // Compact the extra field pointing to the subclasses of this class.
+        programClass.subClasses =
+            shrinkToNewArray(programClass.subClasses);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        // Library classes are left unchanged.
+
+        // Compact the extra field pointing to the subclasses of this class.
+        libraryClass.subClasses =
+            shrinkToNewArray(libraryClass.subClasses);
+    }
+
+
+    // Implementations for MemberVisitor.
+
+    public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
+    {
+        // Shrink the attributes array.
+        programMember.u2attributesCount =
+            shrinkArray(programMember.attributes,
+                        programMember.u2attributesCount);
+
+        // Shrink any attributes.
+        programMember.attributesAccept(programClass, this);
+    }
+
+
+    // Implementations for AttributeVisitor.
+
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
+    {
+        // Shrink the array of InnerClassesInfo objects.
+        innerClassesAttribute.u2classesCount =
+            shrinkArray(innerClassesAttribute.classes,
+                        innerClassesAttribute.u2classesCount);
+    }
+
+
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
+    {
+        // Sometimes, a class is still referenced (apparently as a dummy class),
+        // but its enclosing method is not. Then remove the reference to
+        // the enclosing method.
+        // E.g. the anonymous inner class javax.swing.JList$1 is defined inside
+        // a constructor of javax.swing.JList, but it is also referenced as a
+        // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler.
+        if (enclosingMethodAttribute.referencedMethod != null &&
+            !usageMarker.isUsed(enclosingMethodAttribute.referencedMethod))
+        {
+            enclosingMethodAttribute.u2nameAndTypeIndex = 0;
+
+            enclosingMethodAttribute.referencedMethod = null;
+        }
+    }
+
+
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+    {
+        // Shrink the attributes array.
+        codeAttribute.u2attributesCount =
+            shrinkArray(codeAttribute.attributes,
+                        codeAttribute.u2attributesCount);
+    }
+
+
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
+    {
+        // Shrink the annotations array.
+        annotationsAttribute.u2annotationsCount =
+            shrinkArray(annotationsAttribute.annotations,
+                        annotationsAttribute.u2annotationsCount);
+
+        // Shrink the annotations themselves.
+        annotationsAttribute.annotationsAccept(clazz, this);
+    }
+
+
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
+    {
+        // Loop over all parameters.
+        for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++)
+        {
+            // Shrink the parameter annotations array.
+            parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] =
+                shrinkArray(parameterAnnotationsAttribute.parameterAnnotations[parameterIndex],
+                            parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]);
+        }
+
+        // Shrink the annotations themselves.
+        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
+    }
+
+
+    // Implementations for AnnotationVisitor.
+
+    public void visitAnnotation(Clazz clazz, Annotation annotation)
+    {
+        // Shrink the element values array.
+        annotation.u2elementValuesCount =
+            shrinkArray(annotation.elementValues,
+                        annotation.u2elementValuesCount);
+
+        // Shrink the element values themselves.
+        annotation.elementValuesAccept(clazz, this);
+    }
+
+
+    // Implementations for ElementValueVisitor.
+
+    public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {}
+
+
+    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+    {
+        // Shrink the contained annotation.
+        annotationElementValue.annotationAccept(clazz, this);
+    }
+
+
+    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+    {
+        // Shrink the element values array.
+        arrayElementValue.u2elementValuesCount =
+            shrinkArray(arrayElementValue.elementValues,
+                        arrayElementValue.u2elementValuesCount);
+
+        // Shrink the element values themselves.
+        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Removes all entries that are not marked as being used from the given
+     * constant pool.
+     * @return the new number of entries.
+     */
+    private int shrinkConstantPool(Constant[] constantPool, int length)
+    {
+        if (constantIndexMap.length < length)
+        {
+            constantIndexMap = new int[length];
+        }
+
+        int     counter = 1;
+        boolean isUsed  = false;
+
+        // Shift the used constant pool entries together.
+        for (int index = 1; index < length; index++)
+        {
+            constantIndexMap[index] = counter;
+
+            Constant constant = constantPool[index];
+
+            // Don't update the flag if this is the second half of a long entry.
+            if (constant != null)
+            {
+                isUsed = usageMarker.isUsed(constant);
+            }
+
+            if (isUsed)
+            {
+                constantPool[counter++] = constant;
+            }
+        }
+
+        // Clear the remaining constant pool elements.
+        for (int index = counter; index < length; index++)
+        {
+            constantPool[index] = null;
+        }
+
+        return counter;
+    }
+
+
+    /**
+     * Removes all indices that point to unused constant pool entries
+     * from the given array.
+     * @return the new number of indices.
+     */
+    private int shrinkConstantIndexArray(Constant[] constantPool, int[] array, int length)
+    {
+        int counter = 0;
+
+        // Shift the used objects together.
+        for (int index = 0; index < length; index++)
+        {
+            if (usageMarker.isUsed(constantPool[array[index]]))
+            {
+                array[counter++] = array[index];
+            }
+        }
+
+        // Clear the remaining array elements.
+        for (int index = counter; index < length; index++)
+        {
+            array[index] = 0;
+        }
+
+        return counter;
+    }
+
+
+    /**
+     * Removes all Clazz objects that are not marked as being used
+     * from the given array and returns the remaining objects in a an array
+     * of the right size.
+     * @return the new array.
+     */
+    private Clazz[] shrinkToNewArray(Clazz[] array)
+    {
+        if (array == null)
+        {
+            return null;
+        }
+
+        // Shrink the given array in-place.
+        int length = shrinkArray(array, array.length);
+        if (length == 0)
+        {
+            return null;
+        }
+
+        // Return immediately if the array is of right size already.
+        if (length == array.length)
+        {
+            return array;
+        }
+
+        // Copy the remaining elements into a new array of the right size.
+        Clazz[] newArray = new Clazz[length];
+        System.arraycopy(array, 0, newArray, 0, length);
+        return newArray;
+    }
+
+
+    /**
+     * Removes all VisitorAccepter objects that are not marked as being used
+     * from the given array.
+     * @return the new number of VisitorAccepter objects.
+     */
+    private int shrinkArray(VisitorAccepter[] array, int length)
+    {
+        int counter = 0;
+
+        // Shift the used objects together.
+        for (int index = 0; index < length; index++)
+        {
+            if (usageMarker.isUsed(array[index]))
+            {
+                array[counter++] = array[index];
+            }
+        }
+
+        // Clear the remaining array elements.
+        for (int index = counter; index < length; index++)
+        {
+            array[index] = null;
+        }
+
+        return counter;
+    }
+}
diff --git a/src/proguard/shrink/InnerUsageMarker.java b/src/proguard/shrink/InnerUsageMarker.java
index f9187ae..60a1615 100644
--- a/src/proguard/shrink/InnerUsageMarker.java
+++ b/src/proguard/shrink/InnerUsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: InnerUsageMarker.java,v 1.18.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -22,31 +22,32 @@ package proguard.shrink;
 
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.visitor.*;
-
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 /**
- * This ClassFileVisitor recursively marks all inner classes
- * that are being used in the classes it visits.
+ * This AttributeVisitor recursively marks all necessary inner class information
+ * in the attributes that it visits.
  *
  * @see UsageMarker
  *
  * @author Eric Lafortune
  */
 public class InnerUsageMarker
-  implements ClassFileVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor,
-             InnerClassesInfoVisitor
+extends      SimplifiedVisitor
+implements   AttributeVisitor,
+             InnerClassesInfoVisitor,
+             ConstantVisitor,
+             ClassVisitor
 {
-    private UsageMarker usageMarker;
-
-    // A field acting as a parameter for the class file visitor.
-    private boolean markingAttributes = true;
+    private final UsageMarker usageMarker;
 
-    // A field acting as a return parameter for several methods.
-    private boolean used;
+    // Fields acting as a return parameters for several methods.
+    private boolean attributeUsed;
+    private boolean classUsed;
 
 
     /**
@@ -60,153 +61,54 @@ public class InnerUsageMarker
     }
 
 
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        boolean classUsed = usageMarker.isUsed(programClassFile);
-
-        if (markingAttributes && classUsed)
-        {
-            markingAttributes = false;
-
-            // Check the inner class attribute.
-            programClassFile.attributesAccept(this);
-
-            markingAttributes = true;
-        }
-
-        // The return value.
-        used = classUsed;
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        // The return value.
-        used = true;
-    }
-
-
-    // Implementations for CpInfoVisitor.
+    // Implementations for AttributeVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
+    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
     {
-        boolean classUsed = usageMarker.isUsed(classCpInfo);
-
-        if (!classUsed)
-        {
-            // The ClassCpInfo isn't marked as being used yet. But maybe it should
-            // be included as an interface, so check the actual class.
-            classCpInfo.referencedClassAccept(this);
-            classUsed = used;
-
-            if (classUsed)
-            {
-                // The class is being used. Mark the ClassCpInfo as being used
-                // as well.
-                usageMarker.markAsUsed(classCpInfo);
-
-                markCpEntry(classFile, classCpInfo.u2nameIndex);
-            }
-        }
-
-        // The return value.
-        used = classUsed;
-    }
-
-
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
-    {
-        if (!usageMarker.isUsed(utf8CpInfo))
-        {
-            usageMarker.markAsUsed(utf8CpInfo);
-        }
-    }
-
-
-    // Implementations for AttrInfoVisitor.
-
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo) {}
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo) {}
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo) {}
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo) {}
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo) {}
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo) {}
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo) {}
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo) {}
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo) {}
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo) {}
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo) {}
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo) {}
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo) {}
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo) {}
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo) {}
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo) {}
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo) {}
-
-
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
-    {
-        boolean attributeUsed = false;
-
-        // Mark the interfaces that are being used.
-        for (int i = 0; i < innerClassesAttrInfo.u2numberOfClasses; i++)
-        {
-            // Check if the inner class entry is used.
-            visitInnerClassesInfo(classFile, innerClassesAttrInfo.classes[i]);
-            attributeUsed |= used;
-        }
+        // Mark the necessary inner classes information.
+        attributeUsed = false;
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
 
         if (attributeUsed)
         {
             // We got a positive used flag, so some inner class is being used.
             // Mark this attribute as being used as well.
-            usageMarker.markAsUsed(innerClassesAttrInfo);
+            usageMarker.markAsUsed(innerClassesAttribute);
 
-            markCpEntry(classFile, innerClassesAttrInfo.u2attrNameIndex);
+            markConstant(clazz, innerClassesAttribute.u2attributeNameIndex);
         }
     }
 
 
     // Implementations for InnerClassesInfoVisitor.
 
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
     {
         boolean innerClassesInfoUsed = usageMarker.isUsed(innerClassesInfo);
 
         if (!innerClassesInfoUsed)
         {
-            int u2innerClassInfoIndex = innerClassesInfo.u2innerClassInfoIndex;
-            int u2outerClassInfoIndex = innerClassesInfo.u2outerClassInfoIndex;
-            int u2innerNameIndex      = innerClassesInfo.u2innerNameIndex;
+            int u2innerClassIndex = innerClassesInfo.u2innerClassIndex;
+            int u2outerClassIndex = innerClassesInfo.u2outerClassIndex;
+            int u2innerNameIndex  = innerClassesInfo.u2innerNameIndex;
 
             innerClassesInfoUsed = true;
 
-            if (u2innerClassInfoIndex != 0)
+            if (u2innerClassIndex != 0)
             {
                 // Check if the inner class is marked as being used.
-                markCpEntry(classFile, u2innerClassInfoIndex);
-                innerClassesInfoUsed &= used;
+                markConstant(clazz, u2innerClassIndex);
+                innerClassesInfoUsed = classUsed;
             }
 
-            if (u2outerClassInfoIndex != 0)
+            if (u2outerClassIndex != 0)
             {
                 // Check if the outer class is marked as being used.
-                markCpEntry(classFile, u2outerClassInfoIndex);
-                innerClassesInfoUsed &= used;
+                markConstant(clazz, u2outerClassIndex);
+                innerClassesInfoUsed &= classUsed;
             }
 
             // If both the inner class and the outer class are marked as being
@@ -217,13 +119,58 @@ public class InnerUsageMarker
 
                 if (u2innerNameIndex != 0)
                 {
-                    markCpEntry(classFile, u2innerNameIndex);
+                    markConstant(clazz, u2innerNameIndex);
                 }
             }
         }
 
         // The return value.
-        used = innerClassesInfoUsed;
+        attributeUsed |= innerClassesInfoUsed;
+    }
+
+
+    // Implementations for ConstantVisitor.
+
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+    {
+        classUsed = usageMarker.isUsed(classConstant);
+
+        // Is the class constant marked as being used?
+        if (!classUsed)
+        {
+            // Check the referenced class.
+            classUsed = true;
+            classConstant.referencedClassAccept(this);
+
+            // Is the referenced class marked as being used?
+            if (classUsed)
+            {
+                // Mark the class constant and its Utf8 constant.
+                usageMarker.markAsUsed(classConstant);
+
+                markConstant(clazz, classConstant.u2nameIndex);
+            }
+        }
+    }
+
+
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
+    {
+        usageMarker.markAsUsed(utf8Constant);
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        classUsed = usageMarker.isUsed(programClass);
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        classUsed = true;
     }
 
 
@@ -233,8 +180,8 @@ public class InnerUsageMarker
      * Marks the given constant pool entry of the given class. This includes
      * visiting any other referenced constant pool entries.
      */
-    private void markCpEntry(ClassFile classFile, int index)
+    private void markConstant(Clazz clazz, int index)
     {
-         classFile.constantPoolEntryAccept(index, this);
+         clazz.constantPoolEntryAccept(index, this);
     }
 }
diff --git a/src/proguard/shrink/InterfaceUsageMarker.java b/src/proguard/shrink/InterfaceUsageMarker.java
index 041259e..d7afe82 100644
--- a/src/proguard/shrink/InterfaceUsageMarker.java
+++ b/src/proguard/shrink/InterfaceUsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: InterfaceUsageMarker.java,v 1.14.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,11 +21,14 @@
 package proguard.shrink;
 
 import proguard.classfile.*;
-import proguard.classfile.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
+import proguard.classfile.visitor.ClassVisitor;
 
 
 /**
- * This ClassFileVisitor recursively marks all interface
+ * This ClassVisitor recursively marks all interface
  * classes that are being used in the visited class.
  *
  * @see UsageMarker
@@ -33,10 +36,11 @@ import proguard.classfile.visitor.*;
  * @author Eric Lafortune
  */
 public class InterfaceUsageMarker
-  implements ClassFileVisitor,
-             CpInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             ConstantVisitor
 {
-    private UsageMarker usageMarker;
+    private final UsageMarker usageMarker;
 
     // A field acting as a return parameter for several methods.
     private boolean used;
@@ -53,21 +57,21 @@ public class InterfaceUsageMarker
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        boolean classUsed         = usageMarker.isUsed(programClassFile);
-        boolean classPossiblyUsed = usageMarker.isPossiblyUsed(programClassFile);
+        boolean classUsed         = usageMarker.isUsed(programClass);
+        boolean classPossiblyUsed = usageMarker.isPossiblyUsed(programClass);
 
         if (classUsed || classPossiblyUsed)
         {
             // Mark the references to interfaces that are being used.
-            for (int i = 0; i < programClassFile.u2interfacesCount; i++)
+            for (int index = 0; index < programClass.u2interfacesCount; index++)
             {
                 // Check if the interface is used. Mark the constant pool entry
                 // if so.
-                markCpEntry(programClassFile, programClassFile.u2interfaces[i]);
+                markConstant(programClass, programClass.u2interfaces[index]);
                 classUsed |= used;
             }
 
@@ -79,21 +83,21 @@ public class InterfaceUsageMarker
                 {
                     // At least one if this interface's interfaces is being used.
                     // Mark this interface as well.
-                    usageMarker.markAsUsed(programClassFile);
+                    usageMarker.markAsUsed(programClass);
 
                     // Mark this interface's name.
-                    markCpEntry(programClassFile, programClassFile.u2thisClass);
+                    markConstant(programClass, programClass.u2thisClass);
 
                     // Mark the superclass (java/lang/Object).
-                    if (programClassFile.u2superClass != 0)
+                    if (programClass.u2superClass != 0)
                     {
-                        markCpEntry(programClassFile, programClassFile.u2superClass);
+                        markConstant(programClass, programClass.u2superClass);
                     }
                 }
                 else
                 {
                     // Unmark this interface, so we don't bother looking at it again.
-                    usageMarker.markAsUnused(programClassFile);
+                    usageMarker.markAsUnused(programClass);
                 }
             }
         }
@@ -103,44 +107,33 @@ public class InterfaceUsageMarker
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
         // The return value.
         used = true;
     }
 
 
-    // Implementations for CpInfoVisitor.
-
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo) {}
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo) {}
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo) {}
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo) {}
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo) {}
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo) {}
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo) {}
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo) {}
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo) {}
-
+    // Implementations for ConstantVisitor.
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        boolean classUsed = usageMarker.isUsed(classCpInfo);
+        boolean classUsed = usageMarker.isUsed(classConstant);
 
         if (!classUsed)
         {
-            // The ClassCpInfo isn't marked as being used yet. But maybe it should
+            // The ClassConstant isn't marked as being used yet. But maybe it should
             // be included as an interface, so check the actual class.
-            classCpInfo.referencedClassAccept(this);
-            classUsed = used;
+            classConstant.referencedClassAccept(this);
+            classUsed =   used;
 
             if (classUsed)
             {
-                // The class is being used. Mark the ClassCpInfo as being used
+                // The class is being used. Mark the ClassConstant as being used
                 // as well.
-                usageMarker.markAsUsed(classCpInfo);
+                usageMarker.markAsUsed(classConstant);
 
-                markCpEntry(classFile, classCpInfo.u2nameIndex);
+                markConstant(clazz, classConstant.u2nameIndex);
             }
         }
 
@@ -149,11 +142,11 @@ public class InterfaceUsageMarker
     }
 
 
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
     {
-        if (!usageMarker.isUsed(utf8CpInfo))
+        if (!usageMarker.isUsed(utf8Constant))
         {
-            usageMarker.markAsUsed(utf8CpInfo);
+            usageMarker.markAsUsed(utf8Constant);
         }
     }
 
@@ -164,8 +157,8 @@ public class InterfaceUsageMarker
      * Marks the given constant pool entry of the given class. This includes
      * visiting any referenced objects.
      */
-    private void markCpEntry(ClassFile classFile, int index)
+    private void markConstant(Clazz clazz, int index)
     {
-         classFile.constantPoolEntryAccept(index, this);
+         clazz.constantPoolEntryAccept(index, this);
     }
 }
diff --git a/src/proguard/shrink/ShortestUsageMark.java b/src/proguard/shrink/ShortestUsageMark.java
index 85263cf..16aac32 100644
--- a/src/proguard/shrink/ShortestUsageMark.java
+++ b/src/proguard/shrink/ShortestUsageMark.java
@@ -1,6 +1,6 @@
-/* $Id: ShortestUsageMark.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -29,17 +29,17 @@ import proguard.classfile.visitor.*;
  * other elements. It can be certain or preliminary. It also contains additional
  * information about the reasons why an element is being kept.
  *
- * @see ClassFileShrinker
+ * @see ClassShrinker
  *
  * @author Eric Lafortune
  */
-class ShortestUsageMark
+final class ShortestUsageMark
 {
-    private boolean    certain;
-    private String     reason;
-    private int        depth;
-    private ClassFile  classFile;
-    private MethodInfo methodInfo;
+    private final boolean certain;
+    private final String  reason;
+    private final int     depth;
+    private Clazz   clazz;
+    private Method  method;
 
 
     /**
@@ -58,14 +58,14 @@ class ShortestUsageMark
      * Creates a new certain ShortestUsageMark.
      * @param previousUsageMark the previous mark to which this one is linked.
      * @param reason            the reason for this mark.
-     * @param classFile         the class causing this mark.
+     * @param clazz             the class causing this mark.
      */
     public ShortestUsageMark(ShortestUsageMark previousUsageMark,
                              String            reason,
                              int               cost,
-                             ClassFile         classFile)
+                             Clazz             clazz)
     {
-        this(previousUsageMark, reason, cost, classFile, null);
+        this(previousUsageMark, reason, cost, clazz, null);
     }
 
 
@@ -73,21 +73,21 @@ class ShortestUsageMark
      * Creates a new certain ShortestUsageMark.
      * @param previousUsageMark the previous mark to which this one is linked.
      * @param reason            the reason for this mark.
-     * @param classFile         the class causing this mark.
-     * @param methodInfo        the method in the above class causing this mark.
+     * @param clazz             the class causing this mark.
+     * @param method            the method in the above class causing this mark.
      * @param cost              the added cost of following this path.
      */
     public ShortestUsageMark(ShortestUsageMark previousUsageMark,
                              String            reason,
                              int               cost,
-                             ClassFile         classFile,
-                             MethodInfo        methodInfo)
+                             Clazz             clazz,
+                             Method            method)
     {
-        this.certain    = true;
-        this.reason     = reason;
-        this.depth      = previousUsageMark.depth + cost;
-        this.classFile  = classFile;
-        this.methodInfo = methodInfo;
+        this.certain = true;
+        this.reason  = reason;
+        this.depth   = previousUsageMark.depth + cost;
+        this.clazz   = clazz;
+        this.method  = method;
     }
 
 
@@ -99,11 +99,11 @@ class ShortestUsageMark
     public ShortestUsageMark(ShortestUsageMark otherUsageMark,
                              boolean           certain)
     {
-        this.certain    = certain;
-        this.reason     = otherUsageMark.reason;
-        this.depth      = otherUsageMark.depth;
-        this.classFile  = otherUsageMark.classFile;
-        this.methodInfo = otherUsageMark.methodInfo;
+        this.certain = certain;
+        this.reason  = otherUsageMark.reason;
+        this.depth   = otherUsageMark.depth;
+        this.clazz   = otherUsageMark.clazz;
+        this.method  = otherUsageMark.method;
     }
 
 
@@ -136,37 +136,37 @@ class ShortestUsageMark
 
 
     /**
-     * Returns whether this is mark is caused by the given class file.
+     * Returns whether this is mark is caused by the given class.
      */
-    public boolean isCausedBy(ClassFile classFile)
+    public boolean isCausedBy(Clazz clazz)
     {
-        return classFile.equals(this.classFile);
+        return clazz.equals(this.clazz);
     }
 
 
     /**
-     * Applies the given class file visitor to this mark's class, if any,
+     * Applies the given class visitor to this mark's class, if any,
      * and if this mark doesn't have a method.
      */
-    public void acceptClassFileVisitor(ClassFileVisitor classFileVisitor)
+    public void acceptClassVisitor(ClassVisitor classVisitor)
     {
-        if (classFile  != null &&
-            methodInfo == null)
+        if (clazz  != null &&
+            method == null)
         {
-            classFile.accept(classFileVisitor);
+            clazz.accept(classVisitor);
         }
     }
 
 
     /**
-     * Applies the given class file visitor to this mark's method, if any.
+     * Applies the given class visitor to this mark's method, if any.
      */
-    public void acceptMethodInfoVisitor(MemberInfoVisitor memberInfoVisitor)
+    public void acceptMethodVisitor(MemberVisitor memberVisitor)
     {
-        if (classFile  != null &&
-            methodInfo != null)
+        if (clazz  != null &&
+            method != null)
         {
-            methodInfo.accept(classFile, memberInfoVisitor);
+            method.accept(clazz, memberVisitor);
         }
     }
 
@@ -177,7 +177,7 @@ class ShortestUsageMark
     {
         return "certain=" + certain + ", depth="+depth+": " +
                reason +
-               (classFile  != null ? classFile.getName() : "(none)") + ": " +
-               (methodInfo != null ? methodInfo.getName(classFile) : "(none)");
+               (clazz      != null ? clazz.getName() : "(none)") + ": " +
+               (method     != null ? method.getName(clazz) : "(none)");
     }
 }
diff --git a/src/proguard/shrink/ShortestUsageMarker.java b/src/proguard/shrink/ShortestUsageMarker.java
index 1b30498..9a3c524 100644
--- a/src/proguard/shrink/ShortestUsageMarker.java
+++ b/src/proguard/shrink/ShortestUsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: ShortestUsageMarker.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,18 +21,15 @@
 package proguard.shrink;
 
 import proguard.classfile.*;
-import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.annotation.*;
-import proguard.classfile.instruction.*;
 import proguard.classfile.visitor.*;
 
 
 /**
- * This ClassFileVisitor and MemberInfoVisitor recursively marks all classes
+ * This ClassVisitor and MemberVisitor recursively marks all classes
  * and class elements that are being used. For each element, it finds the
  * shortest chain of dependencies.
  *
- * @see ClassFileShrinker
+ * @see ClassShrinker
  *
  * @author Eric Lafortune
  */
@@ -46,53 +43,53 @@ public class ShortestUsageMarker extends UsageMarker
     private ShortestUsageMark currentUsageMark = INITIAL_MARK;
 
     // A utility object to check for recursive causes.
-    private MyRecursiveCauseChecker recursiveCauseChecker = new MyRecursiveCauseChecker();
+    private final MyRecursiveCauseChecker recursiveCauseChecker = new MyRecursiveCauseChecker();
 
 
     // Overriding implementations for UsageMarker.
 
-    protected void markProgramClassBody(ProgramClassFile programClassFile)
+    protected void markProgramClassBody(ProgramClass programClass)
     {
         ShortestUsageMark previousUsageMark = currentUsageMark;
 
-        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programClassFile),
+        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programClass),
                                                  "is extended by ",
                                                  10000,
-                                                 programClassFile);
+                                                 programClass);
 
-        super.markProgramClassBody(programClassFile);
+        super.markProgramClassBody(programClass);
 
         currentUsageMark = previousUsageMark;
     }
 
 
-    protected void markProgramMethodBody(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    protected void markProgramMethodBody(ProgramClass programClass, ProgramMethod programMethod)
     {
         ShortestUsageMark previousUsageMark = currentUsageMark;
 
-        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programMethodInfo),
+        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(programMethod),
                                                  "is invoked by  ",
                                                  1,
-                                                 programClassFile,
-                                                 programMethodInfo);
+                                                 programClass,
+                                                 programMethod);
 
-        super.markProgramMethodBody(programClassFile, programMethodInfo);
+        super.markProgramMethodBody(programClass, programMethod);
 
         currentUsageMark = previousUsageMark;
     }
 
 
-    protected void markMethodHierarchy(ClassFile classFile, MethodInfo methodInfo)
+    protected void markMethodHierarchy(Clazz clazz, Method method)
     {
         ShortestUsageMark previousUsageMark = currentUsageMark;
 
-        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(methodInfo),
+        currentUsageMark = new ShortestUsageMark(getShortestUsageMark(method),
                                                  "implements     ",
                                                  100,
-                                                 classFile,
-                                                 methodInfo);
+                                                 clazz,
+                                                 method);
 
-        super.markMethodHierarchy(classFile, methodInfo);
+        super.markMethodHierarchy(clazz, method);
 
         currentUsageMark = previousUsageMark;
     }
@@ -120,8 +117,8 @@ public class ShortestUsageMarker extends UsageMarker
     {
         Object visitorInfo = visitorAccepter.getVisitorInfo();
 
-        return //!(visitorAccepter instanceof ClassFile &&
-               //  isCausedBy(currentUsageMark, (ClassFile)visitorAccepter)) &&
+        return //!(visitorAccepter instanceof Clazz &&
+               //  isCausedBy(currentUsageMark, (Clazz)visitorAccepter)) &&
                (visitorInfo == null                           ||
                !(visitorInfo instanceof ShortestUsageMark)   ||
                !((ShortestUsageMark)visitorInfo).isCertain() ||
@@ -177,67 +174,67 @@ public class ShortestUsageMarker extends UsageMarker
     // Small utility methods.
 
     private boolean isCausedBy(ShortestUsageMark shortestUsageMark,
-                               ClassFile         classFile)
+                               Clazz             clazz)
     {
-        return recursiveCauseChecker.check(shortestUsageMark, classFile);
+        return recursiveCauseChecker.check(shortestUsageMark, clazz);
     }
 
 
-    private class MyRecursiveCauseChecker implements ClassFileVisitor, MemberInfoVisitor
+    private class MyRecursiveCauseChecker implements ClassVisitor, MemberVisitor
     {
-        private ClassFile checkClassFile;
-        private boolean   isRecursing;
+        private Clazz   checkClass;
+        private boolean isRecursing;
 
 
         public boolean check(ShortestUsageMark shortestUsageMark,
-                             ClassFile         classFile)
+                             Clazz             clazz)
         {
-            checkClassFile = classFile;
-            isRecursing    = false;
+            checkClass  = clazz;
+            isRecursing = false;
 
-            shortestUsageMark.acceptClassFileVisitor(this);
-            shortestUsageMark.acceptMethodInfoVisitor(this);
+            shortestUsageMark.acceptClassVisitor(this);
+            shortestUsageMark.acceptMethodVisitor(this);
 
             return isRecursing;
         }
 
-        // Implementations for ClassFileVisitor.
+        // Implementations for ClassVisitor.
 
-        public void visitProgramClassFile(ProgramClassFile programClassFile)
+        public void visitProgramClass(ProgramClass programClass)
         {
-            checkCause(programClassFile);
+            checkCause(programClass);
         }
 
 
-        public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+        public void visitLibraryClass(LibraryClass libraryClass)
         {
-            checkCause(libraryClassFile);
+            checkCause(libraryClass);
         }
 
 
-        // Implementations for MemberInfoVisitor.
+        // Implementations for MemberVisitor.
 
-        public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+        public void visitProgramField(ProgramClass programClass, ProgramField programField)
         {
-            checkCause(programFieldInfo);
+            checkCause(programField);
         }
 
 
-        public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+        public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
         {
-            checkCause(programMethodInfo);
+            checkCause(programMethod);
         }
 
 
-        public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+        public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
         {
-             checkCause(libraryFieldInfo);
+             checkCause(libraryField);
        }
 
 
-        public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+        public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
         {
-            checkCause(libraryMethodInfo);
+            checkCause(libraryMethod);
         }
 
 
@@ -250,13 +247,13 @@ public class ShortestUsageMarker extends UsageMarker
                 ShortestUsageMark shortestUsageMark = ShortestUsageMarker.this.getShortestUsageMark(visitorAccepter);
 
                 // Check the class of this mark, if any
-                isRecursing = shortestUsageMark.isCausedBy(checkClassFile);
+                isRecursing = shortestUsageMark.isCausedBy(checkClass);
 
                 // Check the causing class or method, if still necessary.
                 if (!isRecursing)
                 {
-                    shortestUsageMark.acceptClassFileVisitor(this);
-                    shortestUsageMark.acceptMethodInfoVisitor(this);
+                    shortestUsageMark.acceptClassVisitor(this);
+                    shortestUsageMark.acceptMethodVisitor(this);
                 }
             }
         }
diff --git a/src/proguard/shrink/ShortestUsagePrinter.java b/src/proguard/shrink/ShortestUsagePrinter.java
index 795bc4d..ecec10c 100644
--- a/src/proguard/shrink/ShortestUsagePrinter.java
+++ b/src/proguard/shrink/ShortestUsagePrinter.java
@@ -1,6 +1,6 @@
-/* $Id: ShortestUsagePrinter.java,v 1.3.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,27 +21,27 @@
 package proguard.shrink;
 
 import proguard.classfile.*;
-import proguard.classfile.util.*;
+import proguard.classfile.util.ClassUtil;
 import proguard.classfile.visitor.*;
 
-import java.io.*;
+import java.io.PrintStream;
 
 
 /**
- * This ClassFileVisitor and MemberInfoVisitor prints out the reasons why
- * class files and class members have been marked as being used.
+ * This ClassVisitor     and MemberVisitor prints out the reasons why
+ * classes and class members have been marked as being used.
  *
  * @see UsageMarker
  *
  * @author Eric Lafortune
  */
 public class ShortestUsagePrinter
-  implements ClassFileVisitor,
-             MemberInfoVisitor
+implements   ClassVisitor,
+             MemberVisitor
 {
-    private ShortestUsageMarker shortestUsageMarker;
-    private boolean             verbose;
-    private PrintStream         ps;
+    private final ShortestUsageMarker shortestUsageMarker;
+    private final boolean             verbose;
+    private final PrintStream         ps;
 
 
     /**
@@ -84,71 +84,71 @@ public class ShortestUsagePrinter
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        // Print the name of this class file.
-        ps.println(ClassUtil.externalClassName(programClassFile.getName()));
+        // Print the name of this class.
+        ps.println(ClassUtil.externalClassName(programClass.getName()));
 
-        // Print the reason for keeping this class file.
-        printReason(programClassFile);
+        // Print the reason for keeping this class.
+        printReason(programClass);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        // Print the name of this class file.
-        ps.println(ClassUtil.externalClassName(libraryClassFile.getName()));
+        // Print the name of this class.
+        ps.println(ClassUtil.externalClassName(libraryClass.getName()));
 
-        // Print the reason for keeping this class file.
+        // Print the reason for keeping this class.
         ps.println("  is a library class.\n");
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
         // Print the name of this field.
-        String name = programFieldInfo.getName(programClassFile);
-        String type = programFieldInfo.getDescriptor(programClassFile);
+        String name = programField.getName(programClass);
+        String type = programField.getDescriptor(programClass);
 
-        ps.println(ClassUtil.externalClassName(programClassFile.getName()) +
+        ps.println(ClassUtil.externalClassName(programClass.getName()) +
                    (verbose ?
                         ": " + ClassUtil.externalFullFieldDescription(0, name, type):
                         "."  + name) +
-                   lineNumberRange(programClassFile, programFieldInfo));
+                   lineNumberRange(programClass, programField));
 
         // Print the reason for keeping this method.
-        printReason(programFieldInfo);
+        printReason(programField);
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
         // Print the name of this method.
-        String name = programMethodInfo.getName(programClassFile);
-        String type = programMethodInfo.getDescriptor(programClassFile);
+        String name = programMethod.getName(programClass);
+        String type = programMethod.getDescriptor(programClass);
 
-        ps.println(ClassUtil.externalClassName(programClassFile.getName()) +
+        ps.println(ClassUtil.externalClassName(programClass.getName()) +
                    (verbose ?
-                        ": " + ClassUtil.externalFullMethodDescription(programClassFile.getName(), 0, name, type):
+                        ": " + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, name, type):
                         "."  + name) +
-                   lineNumberRange(programClassFile, programMethodInfo));
+                   lineNumberRange(programClass, programMethod));
 
         // Print the reason for keeping this method.
-        printReason(programMethodInfo);
+        printReason(programMethod);
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
     {
         // Print the name of this field.
-        String name = libraryFieldInfo.getName(libraryClassFile);
-        String type = libraryFieldInfo.getDescriptor(libraryClassFile);
+        String name = libraryField.getName(libraryClass);
+        String type = libraryField.getDescriptor(libraryClass);
 
-        ps.println(ClassUtil.externalClassName(libraryClassFile.getName()) +
+        ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
                    (verbose ?
                         ": " + ClassUtil.externalFullFieldDescription(0, name, type):
                         "."  + name));
@@ -158,15 +158,15 @@ public class ShortestUsagePrinter
     }
 
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
         // Print the name of this method.
-        String name = libraryMethodInfo.getName(libraryClassFile);
-        String type = libraryMethodInfo.getDescriptor(libraryClassFile);
+        String name = libraryMethod.getName(libraryClass);
+        String type = libraryMethod.getDescriptor(libraryClass);
 
-        ps.println(ClassUtil.externalClassName(libraryClassFile.getName()) +
+        ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
                    (verbose ?
-                        ": " + ClassUtil.externalFullMethodDescription(libraryClassFile.getName(), 0, name, type):
+                        ": " + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, name, type):
                         "."  + name));
 
         // Print the reason for keeping this method.
@@ -182,12 +182,12 @@ public class ShortestUsagePrinter
         {
             ShortestUsageMark shortestUsageMark = shortestUsageMarker.getShortestUsageMark(visitorAccepter);
 
-            // Print the reason for keeping this class file.
+            // Print the reason for keeping this class.
             ps.print("  " + shortestUsageMark.getReason());
 
             // Print the class or method that is responsible, with its reasons.
-            shortestUsageMark.acceptClassFileVisitor(this);
-            shortestUsageMark.acceptMethodInfoVisitor(this);
+            shortestUsageMark.acceptClassVisitor(this);
+            shortestUsageMark.acceptMethodVisitor(this);
         }
         else
         {
@@ -200,9 +200,9 @@ public class ShortestUsagePrinter
      * Returns the line number range of the given class member, followed by a
      * colon, or just an empty String if no range is available.
      */
-    private static String lineNumberRange(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    private static String lineNumberRange(ProgramClass programClass, ProgramMember programMember)
     {
-        String range = programMemberInfo.getLineNumberRange(programClassFile);
+        String range = programMember.getLineNumberRange(programClass);
         return range != null ?
             (" (" + range + ")") :
             "";
diff --git a/src/proguard/shrink/Shrinker.java b/src/proguard/shrink/Shrinker.java
index 087bc65..a1b318e 100644
--- a/src/proguard/shrink/Shrinker.java
+++ b/src/proguard/shrink/Shrinker.java
@@ -1,6 +1,6 @@
-/* $Id: Shrinker.java,v 1.1.2.5 2007/01/25 21:01:01 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,6 +23,7 @@ package proguard.shrink;
 
 import proguard.*;
 import proguard.classfile.ClassPool;
+import proguard.classfile.attribute.visitor.*;
 import proguard.classfile.visitor.*;
 
 import java.io.*;
@@ -34,7 +35,7 @@ import java.io.*;
  */
 public class Shrinker
 {
-    private Configuration configuration;
+    private final Configuration configuration;
 
 
     /**
@@ -52,9 +53,15 @@ public class Shrinker
     public ClassPool execute(ClassPool programClassPool,
                              ClassPool libraryClassPool) throws IOException
     {
+        // Check if we have at least some keep commands.
+        if (configuration.keep == null)
+        {
+            throw new IOException("You have to specify '-keep' options for the shrinking step.");
+        }
+
         // Clean up any old visitor info.
-        programClassPool.classFilesAccept(new ClassFileCleaner());
-        libraryClassPool.classFilesAccept(new ClassFileCleaner());
+        programClassPool.classesAccept(new ClassCleaner());
+        libraryClassPool.classesAccept(new ClassCleaner());
 
         // Create a visitor for marking the seeds.
         UsageMarker usageMarker = configuration.whyAreYouKeeping == null ?
@@ -64,17 +71,28 @@ public class Shrinker
         ClassPoolVisitor classPoolvisitor =
             ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
                                                                     usageMarker,
-                                                                    usageMarker);
+                                                                    usageMarker,
+                                                                    true,
+                                                                    false,
+                                                                    false);
         // Mark the seeds.
         programClassPool.accept(classPoolvisitor);
         libraryClassPool.accept(classPoolvisitor);
 
         // Mark interfaces that have to be kept.
-        programClassPool.classFilesAccept(new InterfaceUsageMarker(usageMarker));
+        programClassPool.classesAccept(new InterfaceUsageMarker(usageMarker));
 
-        // Mark the inner class information that has to be kept.
-        programClassPool.classFilesAccept(new InnerUsageMarker(usageMarker));
+        // Mark the inner class and annotation information that has to be kept.
+        programClassPool.classesAccept(
+            new UsedClassFilter(usageMarker,
+            new AllAttributeVisitor(true,
+            new MultiAttributeVisitor(new AttributeVisitor[]
+            {
+                new InnerUsageMarker(usageMarker),
+                new AnnotationUsageMarker(usageMarker),
+            }))));
 
+        // Should we explain ourselves?
         if (configuration.whyAreYouKeeping != null)
         {
             System.out.println();
@@ -101,7 +119,7 @@ public class Shrinker
                 System.out;
 
             // Print out items that will be removed.
-            programClassPool.classFilesAcceptAlphabetically(
+            programClassPool.classesAcceptAlphabetically(
                 new UsagePrinter(usageMarker, true, ps));
 
             if (ps != System.out)
@@ -111,15 +129,33 @@ public class Shrinker
         }
 
         // Discard unused program classes.
+        int originalProgramClassPoolSize = programClassPool.size();
+
         ClassPool newProgramClassPool = new ClassPool();
-        programClassPool.classFilesAccept(
-            new UsedClassFileFilter(usageMarker,
-            new MultiClassFileVisitor(
-            new ClassFileVisitor[] {
-                new ClassFileShrinker(usageMarker, 1024),
+        programClassPool.classesAccept(
+            new UsedClassFilter(usageMarker,
+            new MultiClassVisitor(
+            new ClassVisitor[] {
+                new ClassShrinker(usageMarker),
                 new ClassPoolFiller(newProgramClassPool)
             })));
 
+        programClassPool.clear();
+
+        // Check if we have at least some output classes.
+        int newProgramClassPoolSize = newProgramClassPool.size();
+        if (newProgramClassPoolSize == 0)
+        {
+            throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
+        }
+
+        if (configuration.verbose)
+        {
+            System.out.println("Removing unused program classes and class elements...");
+            System.out.println("  Original number of program classes: " + originalProgramClassPoolSize);
+            System.out.println("  Final number of program classes:    " + newProgramClassPoolSize);
+        }
+
         return newProgramClassPool;
     }
 
diff --git a/src/proguard/shrink/UsageMarker.java b/src/proguard/shrink/UsageMarker.java
index 52bec8d..3585cc4 100644
--- a/src/proguard/shrink/UsageMarker.java
+++ b/src/proguard/shrink/UsageMarker.java
@@ -1,6 +1,6 @@
-/* $Id: UsageMarker.java,v 1.46.2.4 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -23,43 +23,53 @@ package proguard.shrink;
 import proguard.classfile.*;
 import proguard.classfile.attribute.*;
 import proguard.classfile.attribute.annotation.*;
+import proguard.classfile.attribute.preverification.*;
+import proguard.classfile.attribute.preverification.visitor.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
 import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.SimplifiedVisitor;
 import proguard.classfile.visitor.*;
 
 
 /**
- * This ClassFileVisitor and MemberInfoVisitor recursively marks all
- * classes and class elements that are being used.
+ * This ClassVisitor and MemberVisitor recursively marks all classes and class
+ * elements that are being used.
  *
- * @see ClassFileShrinker
+ * @see ClassShrinker
  *
  * @author Eric Lafortune
  */
-public class UsageMarker
-  implements ClassFileVisitor,
-             MemberInfoVisitor,
-             CpInfoVisitor,
-             AttrInfoVisitor,
-             InnerClassesInfoVisitor,
-             ExceptionInfoVisitor,
-             LocalVariableInfoVisitor,
-             LocalVariableTypeInfoVisitor,
-             AnnotationVisitor,
-             ElementValueVisitor,
-             InstructionVisitor
+class      UsageMarker
+extends    SimplifiedVisitor
+implements ClassVisitor,
+           MemberVisitor,
+           ConstantVisitor,
+           AttributeVisitor,
+           InnerClassesInfoVisitor,
+           ExceptionInfoVisitor,
+           StackMapFrameVisitor,
+           VerificationTypeVisitor,
+           LocalVariableInfoVisitor,
+           LocalVariableTypeInfoVisitor,
+//         AnnotationVisitor,
+//         ElementValueVisitor,
+           InstructionVisitor
 {
-    // A visitor info flag to indicate the ProgramMemberInfo object is being used,
-    // if its ClassFile can be determined as being used as well.
+    // A visitor info flag to indicate the ProgramMember object is being used,
+    // if its Clazz can be determined as being used as well.
     private static final Object POSSIBLY_USED = new Object();
     // A visitor info flag to indicate the visitor accepter is being used.
     private static final Object USED          = new Object();
 
 
-    private MyInterfaceUsageMarker          interfaceUsageMarker          = new MyInterfaceUsageMarker();
-    private MyPossiblyUsedMethodUsageMarker possiblyUsedMethodUsageMarker = new MyPossiblyUsedMethodUsageMarker();
-//    private ClassFileVisitor       dynamicClassMarker   =
-//        new MultiClassFileVisitor(
-//        new ClassFileVisitor[]
+    private final MyInterfaceUsageMarker          interfaceUsageMarker          = new MyInterfaceUsageMarker();
+    private final MyPossiblyUsedMethodUsageMarker possiblyUsedMethodUsageMarker = new MyPossiblyUsedMethodUsageMarker();
+//    private ClassVisitor       dynamicClassMarker   =
+//        new MultiClassVisitor(
+//        new ClassVisitor[]
 //        {
 //            this,
 //            new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT,
@@ -68,129 +78,129 @@ public class UsageMarker
 //        });
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        if (shouldBeMarkedAsUsed(programClassFile))
+        if (shouldBeMarkedAsUsed(programClass))
         {
             // Mark this class.
-            markAsUsed(programClassFile);
+            markAsUsed(programClass);
 
-            markProgramClassBody(programClassFile);
+            markProgramClassBody(programClass);
         }
     }
 
 
-    protected void markProgramClassBody(ProgramClassFile programClassFile)
+    protected void markProgramClassBody(ProgramClass programClass)
     {
         // Mark this class's name.
-        markCpEntry(programClassFile, programClassFile.u2thisClass);
+        markConstant(programClass, programClass.u2thisClass);
 
         // Mark the superclass.
-        if (programClassFile.u2superClass != 0)
+        if (programClass.u2superClass != 0)
         {
-            markCpEntry(programClassFile, programClassFile.u2superClass);
+            markConstant(programClass, programClass.u2superClass);
         }
 
         // Give the interfaces preliminary marks.
-        programClassFile.hierarchyAccept(false, false, true, false,
-                                         interfaceUsageMarker);
+        programClass.hierarchyAccept(false, false, true, false,
+                                     interfaceUsageMarker);
 
         // Explicitly mark the <clinit> method.
-        programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
-                                      ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
-                                      this);
+        programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT,
+                                  ClassConstants.INTERNAL_METHOD_TYPE_CLINIT,
+                                  this);
 
         // Explicitly mark the parameterless <init> method.
-        programClassFile.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT,
-                                      ClassConstants.INTERNAL_METHOD_TYPE_INIT,
-                                      this);
+        programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT,
+                                  ClassConstants.INTERNAL_METHOD_TYPE_INIT,
+                                  this);
 
         // Process all methods that have already been marked as possibly used.
-        programClassFile.methodsAccept(possiblyUsedMethodUsageMarker);
+        programClass.methodsAccept(possiblyUsedMethodUsageMarker);
 
         // Mark the attributes.
-        programClassFile.attributesAccept(this);
+        programClass.attributesAccept(this);
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+    public void visitLibraryClass(LibraryClass libraryClass)
     {
-        if (shouldBeMarkedAsUsed(libraryClassFile))
+        if (shouldBeMarkedAsUsed(libraryClass))
         {
-            markAsUsed(libraryClassFile);
+            markAsUsed(libraryClass);
 
             // We're not going to analyze all library code. We're assuming that
             // if this class is being used, all of its methods will be used as
             // well. We'll mark them as such (here and in all subclasses).
 
             // Mark the superclass.
-            ClassFile superClass = libraryClassFile.superClass;
+            Clazz superClass = libraryClass.superClass;
             if (superClass != null)
             {
                 superClass.accept(this);
             }
 
             // Mark the interfaces.
-            ClassFile[] interfaceClasses = libraryClassFile.interfaceClasses;
+            Clazz[] interfaceClasses = libraryClass.interfaceClasses;
             if (interfaceClasses != null)
             {
-                for (int i = 0; i < interfaceClasses.length; i++)
+                for (int index = 0; index < interfaceClasses.length; index++)
                 {
-                    if (interfaceClasses[i] != null)
+                    if (interfaceClasses[index] != null)
                     {
-                        interfaceClasses[i].accept(this);
+                        interfaceClasses[index].accept(this);
                     }
                 }
             }
 
             // Mark all methods.
-            libraryClassFile.methodsAccept(this);
+            libraryClass.methodsAccept(this);
         }
     }
 
 
     /**
-     * This ClassFileVisitor marks ProgramClassFile objects as possibly used,
-     * and it visits LibraryClassFile objects with its outer UsageMarker.
+     * This ClassVisitor marks ProgramClass objects as possibly used,
+     * and it visits LibraryClass objects with its outer UsageMarker.
      */
-    private class MyInterfaceUsageMarker implements ClassFileVisitor
+    private class MyInterfaceUsageMarker
+    implements    ClassVisitor
     {
-        public void visitProgramClassFile(ProgramClassFile programClassFile)
+        public void visitProgramClass(ProgramClass programClass)
         {
-            if (shouldBeMarkedAsPossiblyUsed(programClassFile))
+            if (shouldBeMarkedAsPossiblyUsed(programClass))
             {
                 // We can't process the interface yet, because it might not
                 // be required. Give it a preliminary mark.
-                markAsPossiblyUsed(programClassFile);
+                markAsPossiblyUsed(programClass);
             }
         }
 
-        public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
+        public void visitLibraryClass(LibraryClass libraryClass)
         {
             // Make sure all library interface methods are marked.
-            UsageMarker.this.visitLibraryClassFile(libraryClassFile);
+            UsageMarker.this.visitLibraryClass(libraryClass);
         }
     }
 
 
-    private class MyPossiblyUsedMethodUsageMarker implements MemberInfoVisitor
+    private class MyPossiblyUsedMethodUsageMarker
+    extends       SimplifiedVisitor
+    implements    MemberVisitor
     {
-        // Implementations for MemberInfoVisitor.
-
-        public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo) {}
-
+        // Implementations for MemberVisitor.
 
-        public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+        public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
         {
             // Has the method already been referenced?
-            if (isPossiblyUsed(programMethodInfo))
+            if (isPossiblyUsed(programMethod))
             {
-                markAsUsed(programMethodInfo);
+                markAsUsed(programMethod);
 
                 // Mark the method body.
-                markProgramMethodBody(programClassFile, programMethodInfo);
+                markProgramMethodBody(programClass, programMethod);
 
                 // Note that, if the method has been marked as possibly used,
                 // the method hierarchy has already been marked (cfr. below).
@@ -198,87 +208,86 @@ public class UsageMarker
         }
 
 
-        public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-        public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
     }
 
 
-    // Implementations for MemberInfoVisitor.
+    // Implementations for MemberVisitor.
 
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        if (shouldBeMarkedAsUsed(programFieldInfo))
+        if (shouldBeMarkedAsUsed(programField))
         {
-            markAsUsed(programFieldInfo);
+            markAsUsed(programField);
 
             // Mark the name and descriptor.
-            markCpEntry(programClassFile, programFieldInfo.u2nameIndex);
-            markCpEntry(programClassFile, programFieldInfo.u2descriptorIndex);
+            markConstant(programClass, programField.u2nameIndex);
+            markConstant(programClass, programField.u2descriptorIndex);
 
             // Mark the attributes.
-            programFieldInfo.attributesAccept(programClassFile, this);
+            programField.attributesAccept(programClass, this);
 
             // Mark the classes referenced in the descriptor string.
-            programFieldInfo.referencedClassesAccept(this);
+            programField.referencedClassesAccept(this);
         }
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        if (shouldBeMarkedAsUsed(programMethodInfo))
+        if (shouldBeMarkedAsUsed(programMethod))
         {
             // Is the method's class used?
-            if (isUsed(programClassFile))
+            if (isUsed(programClass))
             {
-                markAsUsed(programMethodInfo);
+                markAsUsed(programMethod);
 
                 // Mark the method body.
-                markProgramMethodBody(programClassFile, programMethodInfo);
+                markProgramMethodBody(programClass, programMethod);
 
                 // Mark the method hierarchy.
-                markMethodHierarchy(programClassFile, programMethodInfo);
+                markMethodHierarchy(programClass, programMethod);
             }
 
             // Hasn't the method been marked as possibly being used yet?
-            else if (shouldBeMarkedAsPossiblyUsed(programMethodInfo))
+            else if (shouldBeMarkedAsPossiblyUsed(programMethod))
             {
                 // We can't process the method yet, because the class isn't
                 // marked as being used (yet). Give it a preliminary mark.
-                markAsPossiblyUsed(programMethodInfo);
+                markAsPossiblyUsed(programMethod);
 
                 // Mark the method hierarchy.
-                markMethodHierarchy(programClassFile, programMethodInfo);
+                markMethodHierarchy(programClass, programMethod);
             }
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
+    public void visitLibraryField(LibraryClass programClass, LibraryField programField) {}
 
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
     {
-        if (shouldBeMarkedAsUsed(libraryMethodInfo))
+        if (shouldBeMarkedAsUsed(libraryMethod))
         {
-            markAsUsed(libraryMethodInfo);
+            markAsUsed(libraryMethod);
 
             // Mark the method hierarchy.
-            markMethodHierarchy(libraryClassFile, libraryMethodInfo);
+            markMethodHierarchy(libraryClass, libraryMethod);
         }
     }
 
 
-    protected void markProgramMethodBody(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    protected void markProgramMethodBody(ProgramClass programClass, ProgramMethod programMethod)
     {
         // Mark the name and descriptor.
-        markCpEntry(programClassFile, programMethodInfo.u2nameIndex);
-        markCpEntry(programClassFile, programMethodInfo.u2descriptorIndex);
+        markConstant(programClass, programMethod.u2nameIndex);
+        markConstant(programClass, programMethod.u2descriptorIndex);
 
         // Mark the attributes.
-        programMethodInfo.attributesAccept(programClassFile, this);
+        programMethod.attributesAccept(programClass, this);
 
         // Mark the classes referenced in the descriptor string.
-        programMethodInfo.referencedClassesAccept(this);
+        programMethod.referencedClassesAccept(this);
     }
 
 
@@ -286,507 +295,529 @@ public class UsageMarker
      * Marks the hierarchy of implementing or overriding methods corresponding
      * to the given method, if any.
      */
-    protected void markMethodHierarchy(ClassFile classFile, MethodInfo methodInfo)
+    protected void markMethodHierarchy(Clazz clazz, Method method)
     {
-        classFile.methodImplementationsAccept(methodInfo, false, this);
+        clazz.methodImplementationsAccept(method, false, this);
     }
 
 
-    // Implementations for CpInfoVisitor.
+    // Implementations for ConstantVisitor.
 
-    public void visitIntegerCpInfo(ClassFile classFile, IntegerCpInfo integerCpInfo)
+    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
     {
-        if (shouldBeMarkedAsUsed(integerCpInfo))
+        if (shouldBeMarkedAsUsed(integerConstant))
         {
-            markAsUsed(integerCpInfo);
+            markAsUsed(integerConstant);
         }
     }
 
 
-    public void visitLongCpInfo(ClassFile classFile, LongCpInfo longCpInfo)
+    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
     {
-        if (shouldBeMarkedAsUsed(longCpInfo))
+        if (shouldBeMarkedAsUsed(longConstant))
         {
-            markAsUsed(longCpInfo);
+            markAsUsed(longConstant);
         }
     }
 
 
-    public void visitFloatCpInfo(ClassFile classFile, FloatCpInfo floatCpInfo)
+    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
     {
-        if (shouldBeMarkedAsUsed(floatCpInfo))
+        if (shouldBeMarkedAsUsed(floatConstant))
         {
-            markAsUsed(floatCpInfo);
+            markAsUsed(floatConstant);
         }
     }
 
 
-    public void visitDoubleCpInfo(ClassFile classFile, DoubleCpInfo doubleCpInfo)
+    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
     {
-        if (shouldBeMarkedAsUsed(doubleCpInfo))
+        if (shouldBeMarkedAsUsed(doubleConstant))
         {
-            markAsUsed(doubleCpInfo);
+            markAsUsed(doubleConstant);
         }
     }
 
 
-    public void visitStringCpInfo(ClassFile classFile, StringCpInfo stringCpInfo)
+    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     {
-        if (shouldBeMarkedAsUsed(stringCpInfo))
+        if (shouldBeMarkedAsUsed(stringConstant))
         {
-            markAsUsed(stringCpInfo);
+            markAsUsed(stringConstant);
 
-            markCpEntry(classFile, stringCpInfo.u2stringIndex);
+            markConstant(clazz, stringConstant.u2stringIndex);
 
             // Mark the referenced class and its parameterless constructor,
             // if the string is being used in a Class.forName construct.
-            //stringCpInfo.referencedClassAccept(dynamicClassMarker);
+            //stringConstant.referencedClassAccept(dynamicClassMarker);
 
-            // Mark the referenced class.
-            stringCpInfo.referencedClassAccept(this);
+            // Mark the referenced class or class member, if any.
+            stringConstant.referencedClassAccept(this);
+            stringConstant.referencedMemberAccept(this);
         }
     }
 
 
-    public void visitUtf8CpInfo(ClassFile classFile, Utf8CpInfo utf8CpInfo)
+    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
     {
-        if (shouldBeMarkedAsUsed(utf8CpInfo))
+        if (shouldBeMarkedAsUsed(utf8Constant))
         {
-            markAsUsed(utf8CpInfo);
+            markAsUsed(utf8Constant);
         }
     }
 
 
-    public void visitFieldrefCpInfo(ClassFile classFile, FieldrefCpInfo fieldrefCpInfo)
+    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
     {
-        visitRefCpInfo(classFile, fieldrefCpInfo);
+        visitRefConstant(clazz, fieldrefConstant);
     }
 
 
-    public void visitInterfaceMethodrefCpInfo(ClassFile classFile, InterfaceMethodrefCpInfo interfaceMethodrefCpInfo)
+    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
     {
-        visitRefCpInfo(classFile, interfaceMethodrefCpInfo);
+        visitRefConstant(clazz, interfaceMethodrefConstant);
     }
 
 
-    public void visitMethodrefCpInfo(ClassFile classFile, MethodrefCpInfo methodrefCpInfo)
+    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
     {
-        visitRefCpInfo(classFile, methodrefCpInfo);
+        visitRefConstant(clazz, methodrefConstant);
     }
 
 
-    private void visitRefCpInfo(ClassFile classFile, RefCpInfo methodrefCpInfo)
+    private void visitRefConstant(Clazz clazz, RefConstant methodrefConstant)
     {
-        if (shouldBeMarkedAsUsed(methodrefCpInfo))
+        if (shouldBeMarkedAsUsed(methodrefConstant))
         {
-            markAsUsed(methodrefCpInfo);
+            markAsUsed(methodrefConstant);
 
-            markCpEntry(classFile, methodrefCpInfo.u2classIndex);
-            markCpEntry(classFile, methodrefCpInfo.u2nameAndTypeIndex);
+            markConstant(clazz, methodrefConstant.u2classIndex);
+            markConstant(clazz, methodrefConstant.u2nameAndTypeIndex);
 
             // When compiled with "-target 1.2" or higher, the class or
             // interface actually containing the referenced method may be
             // higher up the hierarchy. Make sure it's marked, in case it
             // isn't used elsewhere.
-            methodrefCpInfo.referencedClassAccept(this);
+            methodrefConstant.referencedClassAccept(this);
 
             // Mark the referenced method itself.
-            methodrefCpInfo.referencedMemberInfoAccept(this);
+            methodrefConstant.referencedMemberAccept(this);
         }
     }
 
 
-    public void visitClassCpInfo(ClassFile classFile, ClassCpInfo classCpInfo)
+    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
     {
-        if (shouldBeMarkedAsUsed(classCpInfo))
+        if (shouldBeMarkedAsUsed(classConstant))
         {
-            markAsUsed(classCpInfo);
+            markAsUsed(classConstant);
 
-            markCpEntry(classFile, classCpInfo.u2nameIndex);
+            markConstant(clazz, classConstant.u2nameIndex);
 
             // Mark the referenced class itself.
-            classCpInfo.referencedClassAccept(this);
+            classConstant.referencedClassAccept(this);
         }
     }
 
 
-    public void visitNameAndTypeCpInfo(ClassFile classFile, NameAndTypeCpInfo nameAndTypeCpInfo)
+    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
     {
-        if (shouldBeMarkedAsUsed(nameAndTypeCpInfo))
+        if (shouldBeMarkedAsUsed(nameAndTypeConstant))
         {
-            markAsUsed(nameAndTypeCpInfo);
+            markAsUsed(nameAndTypeConstant);
 
-            markCpEntry(classFile, nameAndTypeCpInfo.u2nameIndex);
-            markCpEntry(classFile, nameAndTypeCpInfo.u2descriptorIndex);
+            markConstant(clazz, nameAndTypeConstant.u2nameIndex);
+            markConstant(clazz, nameAndTypeConstant.u2descriptorIndex);
         }
     }
 
 
-    // Implementations for AttrInfoVisitor.
+    // Implementations for AttributeVisitor.
     // Note that attributes are typically only referenced once, so we don't
     // test if they have been marked already.
 
-    public void visitUnknownAttrInfo(ClassFile classFile, UnknownAttrInfo unknownAttrInfo)
+    public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
     {
         // This is the best we can do for unknown attributes.
-        markAsUsed(unknownAttrInfo);
+        markAsUsed(unknownAttribute);
 
-        markCpEntry(classFile, unknownAttrInfo.u2attrNameIndex);
+        markConstant(clazz, unknownAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitInnerClassesAttrInfo(ClassFile classFile, InnerClassesAttrInfo innerClassesAttrInfo)
+    public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
     {
-        // Don't mark the attribute and its name yet. We may mark it later, in
-        // InnerUsageMarker.
-        //_markAsUsed(innerClassesAttrInfo);
+        markAsUsed(sourceFileAttribute);
 
-        //markCpEntry(classFile, innerClassesAttrInfo.u2attrNameIndex);
-        innerClassesAttrInfo.innerClassEntriesAccept(classFile, this);
+        markConstant(clazz, sourceFileAttribute.u2attributeNameIndex);
+        markConstant(clazz, sourceFileAttribute.u2sourceFileIndex);
     }
 
 
-    public void visitEnclosingMethodAttrInfo(ClassFile classFile, EnclosingMethodAttrInfo enclosingMethodAttrInfo)
+    public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
     {
-        markAsUsed(enclosingMethodAttrInfo);
-
-        markCpEntry(classFile, enclosingMethodAttrInfo.u2attrNameIndex);
-        markCpEntry(classFile, enclosingMethodAttrInfo.u2classIndex);
+        markAsUsed(sourceDirAttribute);
 
-        if (enclosingMethodAttrInfo.u2nameAndTypeIndex != 0)
-        {
-            markCpEntry(classFile, enclosingMethodAttrInfo.u2nameAndTypeIndex);
-        }
+        markConstant(clazz, sourceDirAttribute.u2attributeNameIndex);
+        markConstant(clazz, sourceDirAttribute.u2sourceDirIndex);
     }
 
 
-    public void visitConstantValueAttrInfo(ClassFile classFile, FieldInfo fieldInfo, ConstantValueAttrInfo constantValueAttrInfo)
+    public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
     {
-        markAsUsed(constantValueAttrInfo);
+        // Don't mark the attribute and its name yet. We may mark it later, in
+        // InnerUsageMarker.
+        //markAsUsed(innerClassesAttribute);
+
+        //markConstant(clazz, innerClassesAttribute.u2attrNameIndex);
 
-        markCpEntry(classFile, constantValueAttrInfo.u2attrNameIndex);
-        markCpEntry(classFile, constantValueAttrInfo.u2constantValueIndex);
+        // Do mark the outer class entries.
+        innerClassesAttribute.innerClassEntriesAccept(clazz, this);
     }
 
 
-    public void visitExceptionsAttrInfo(ClassFile classFile, MethodInfo methodInfo, ExceptionsAttrInfo exceptionsAttrInfo)
+    public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
     {
-        markAsUsed(exceptionsAttrInfo);
+        markAsUsed(enclosingMethodAttribute);
 
-        markCpEntry(classFile, exceptionsAttrInfo.u2attrNameIndex);
+        markConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex);
+        markConstant(clazz, enclosingMethodAttribute.u2classIndex);
 
-        // Mark the constant pool entries referenced by the exceptions.
-        exceptionsAttrInfo.exceptionEntriesAccept((ProgramClassFile)classFile, this);
+        if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
+        {
+            markConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex);
+        }
     }
 
 
-    public void visitCodeAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo)
+    public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
     {
-        markAsUsed(codeAttrInfo);
+        markAsUsed(deprecatedAttribute);
 
-        markCpEntry(classFile, codeAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the instructions,
-        // and the exceptions and attributes.
-        codeAttrInfo.instructionsAccept(classFile, methodInfo, this);
-        codeAttrInfo.exceptionsAccept(classFile, methodInfo, this);
-        codeAttrInfo.attributesAccept(classFile, methodInfo, this);
+        markConstant(clazz, deprecatedAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitLineNumberTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LineNumberTableAttrInfo lineNumberTableAttrInfo)
+    public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
     {
-        markAsUsed(lineNumberTableAttrInfo);
+        markAsUsed(syntheticAttribute);
 
-        markCpEntry(classFile, lineNumberTableAttrInfo.u2attrNameIndex);
+        markConstant(clazz, syntheticAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitLocalVariableTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTableAttrInfo localVariableTableAttrInfo)
+    public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
     {
-        markAsUsed(localVariableTableAttrInfo);
+        markAsUsed(signatureAttribute);
 
-        markCpEntry(classFile, localVariableTableAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the local variables.
-        localVariableTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        markConstant(clazz, signatureAttribute.u2attributeNameIndex);
+        markConstant(clazz, signatureAttribute.u2signatureIndex);
     }
 
 
-    public void visitLocalVariableTypeTableAttrInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeTableAttrInfo localVariableTypeTableAttrInfo)
+    public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
     {
-        markAsUsed(localVariableTypeTableAttrInfo);
+        markAsUsed(constantValueAttribute);
 
-        markCpEntry(classFile, localVariableTypeTableAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the local variable types.
-        localVariableTypeTableAttrInfo.localVariablesAccept(classFile, methodInfo, codeAttrInfo, this);
+        markConstant(clazz, constantValueAttribute.u2attributeNameIndex);
+        markConstant(clazz, constantValueAttribute.u2constantValueIndex);
     }
 
 
-    public void visitSourceFileAttrInfo(ClassFile classFile, SourceFileAttrInfo sourceFileAttrInfo)
+    public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
     {
-        markAsUsed(sourceFileAttrInfo);
+        markAsUsed(exceptionsAttribute);
+
+        markConstant(clazz, exceptionsAttribute.u2attributeNameIndex);
 
-        markCpEntry(classFile, sourceFileAttrInfo.u2attrNameIndex);
-        markCpEntry(classFile, sourceFileAttrInfo.u2sourceFileIndex);
+        // Mark the constant pool entries referenced by the exceptions.
+        exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this);
     }
 
 
-    public void visitSourceDirAttrInfo(ClassFile classFile, SourceDirAttrInfo sourceDirAttrInfo)
+    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     {
-        markAsUsed(sourceDirAttrInfo);
+        markAsUsed(codeAttribute);
+
+        markConstant(clazz, codeAttribute.u2attributeNameIndex);
 
-        markCpEntry(classFile, sourceDirAttrInfo.u2attrNameIndex);
-        markCpEntry(classFile, sourceDirAttrInfo.u2sourceDirIndex);
+        // Mark the constant pool entries referenced by the instructions,
+        // by the exceptions, and by the attributes.
+        codeAttribute.instructionsAccept(clazz, method, this);
+        codeAttribute.exceptionsAccept(clazz, method, this);
+        codeAttribute.attributesAccept(clazz, method, this);
     }
 
 
-    public void visitDeprecatedAttrInfo(ClassFile classFile, DeprecatedAttrInfo deprecatedAttrInfo)
+    public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
     {
-        markAsUsed(deprecatedAttrInfo);
+        markAsUsed(stackMapAttribute);
+
+        markConstant(clazz, stackMapAttribute.u2attributeNameIndex);
 
-        markCpEntry(classFile, deprecatedAttrInfo.u2attrNameIndex);
+        // Mark the constant pool entries referenced by the stack map frames.
+        stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitSyntheticAttrInfo(ClassFile classFile, SyntheticAttrInfo syntheticAttrInfo)
+    public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
     {
-        markAsUsed(syntheticAttrInfo);
+        markAsUsed(stackMapTableAttribute);
+
+        markConstant(clazz, stackMapTableAttribute.u2attributeNameIndex);
 
-        markCpEntry(classFile, syntheticAttrInfo.u2attrNameIndex);
+        // Mark the constant pool entries referenced by the stack map frames.
+        stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitSignatureAttrInfo(ClassFile classFile, SignatureAttrInfo signatureAttrInfo)
+    public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
     {
-        markAsUsed(signatureAttrInfo);
+        markAsUsed(lineNumberTableAttribute);
 
-        markCpEntry(classFile, signatureAttrInfo.u2attrNameIndex);
-        markCpEntry(classFile, signatureAttrInfo.u2signatureIndex);
+        markConstant(clazz, lineNumberTableAttribute.u2attributeNameIndex);
     }
 
 
-    public void visitRuntimeVisibleAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleAnnotationsAttrInfo runtimeVisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     {
-        markAsUsed(runtimeVisibleAnnotationsAttrInfo);
+        markAsUsed(localVariableTableAttribute);
 
-        markCpEntry(classFile, runtimeVisibleAnnotationsAttrInfo.u2attrNameIndex);
+        markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex);
 
-        // Mark the constant pool entries referenced by the annotations.
-        runtimeVisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Mark the constant pool entries referenced by the local variables.
+        localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeInvisibleAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleAnnotationsAttrInfo runtimeInvisibleAnnotationsAttrInfo)
+    public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
     {
-        markAsUsed(runtimeInvisibleAnnotationsAttrInfo);
+        markAsUsed(localVariableTypeTableAttribute);
 
-        markCpEntry(classFile, runtimeInvisibleAnnotationsAttrInfo.u2attrNameIndex);
+        markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex);
 
-        // Mark the constant pool entries referenced by the annotations.
-        runtimeInvisibleAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Mark the constant pool entries referenced by the local variable types.
+        localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
     }
 
 
-    public void visitRuntimeVisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeVisibleParameterAnnotationsAttrInfo runtimeVisibleParameterAnnotationsAttrInfo)
+    public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
     {
-        markAsUsed(runtimeVisibleParameterAnnotationsAttrInfo);
-
-        markCpEntry(classFile, runtimeVisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the annotations.
-        runtimeVisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Don't mark the attribute and its contents yet. We may mark them later,
+        // in AnnotationUsageMarker.
+//        markAsUsed(annotationsAttribute);
+//
+//        markConstant(clazz, annotationsAttribute.u2attributeNameIndex);
+//
+//        // Mark the constant pool entries referenced by the annotations.
+//        annotationsAttribute.annotationsAccept(clazz, this);
     }
 
 
-    public void visitRuntimeInvisibleParameterAnnotationAttrInfo(ClassFile classFile, RuntimeInvisibleParameterAnnotationsAttrInfo runtimeInvisibleParameterAnnotationsAttrInfo)
+    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
     {
-        markAsUsed(runtimeInvisibleParameterAnnotationsAttrInfo);
-
-        markCpEntry(classFile, runtimeInvisibleParameterAnnotationsAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the annotations.
-        runtimeInvisibleParameterAnnotationsAttrInfo.annotationsAccept(classFile, this);
+        // Don't mark the attribute and its contents yet. We may mark them later,
+        // in AnnotationUsageMarker.
+//        markAsUsed(parameterAnnotationsAttribute);
+//
+//        markConstant(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
+//
+//        // Mark the constant pool entries referenced by the annotations.
+//        parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
     }
 
 
-    public void visitAnnotationDefaultAttrInfo(ClassFile classFile, AnnotationDefaultAttrInfo annotationDefaultAttrInfo)
+    public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
     {
-        markAsUsed(annotationDefaultAttrInfo);
-
-        markCpEntry(classFile, annotationDefaultAttrInfo.u2attrNameIndex);
-
-        // Mark the constant pool entries referenced by the element value.
-        annotationDefaultAttrInfo.defaultValueAccept(classFile, this);
+        // Don't mark the attribute and its contents yet. We may mark them later,
+        // in AnnotationUsageMarker.
+//        markAsUsed(annotationDefaultAttribute);
+//
+//        markConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex);
+//
+//        // Mark the constant pool entries referenced by the element value.
+//        annotationDefaultAttribute.defaultValueAccept(clazz, this);
     }
 
 
     // Implementations for ExceptionInfoVisitor.
 
-    public void visitExceptionInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, ExceptionInfo exceptionInfo)
+    public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
     {
         markAsUsed(exceptionInfo);
 
         if (exceptionInfo.u2catchType != 0)
         {
-            markCpEntry(classFile, exceptionInfo.u2catchType);
+            markConstant(clazz, exceptionInfo.u2catchType);
         }
     }
 
 
     // Implementations for InnerClassesInfoVisitor.
 
-    public void visitInnerClassesInfo(ClassFile classFile, InnerClassesInfo innerClassesInfo)
+    public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
     {
         // At this point, we only mark outer classes of this class.
         // Inner class can be marked later, by InnerUsageMarker.
-        if (innerClassesInfo.u2innerClassInfoIndex == 0 &&
-            classFile.getName().equals(classFile.getCpClassNameString(innerClassesInfo.u2innerClassInfoIndex)))
+        if (innerClassesInfo.u2innerClassIndex == 0 &&
+            clazz.getName().equals(clazz.getClassName(innerClassesInfo.u2innerClassIndex)))
         {
             markAsUsed(innerClassesInfo);
 
-            if (innerClassesInfo.u2innerClassInfoIndex != 0)
+            if (innerClassesInfo.u2innerClassIndex != 0)
             {
-                markCpEntry(classFile, innerClassesInfo.u2innerClassInfoIndex);
+                markConstant(clazz, innerClassesInfo.u2innerClassIndex);
             }
 
-            if (innerClassesInfo.u2outerClassInfoIndex != 0)
+            if (innerClassesInfo.u2outerClassIndex != 0)
             {
-                markCpEntry(classFile, innerClassesInfo.u2outerClassInfoIndex);
+                markConstant(clazz, innerClassesInfo.u2outerClassIndex);
             }
 
             if (innerClassesInfo.u2innerNameIndex != 0)
             {
-                markCpEntry(classFile, innerClassesInfo.u2innerNameIndex);
+                markConstant(clazz, innerClassesInfo.u2innerNameIndex);
             }
         }
     }
 
 
-    // Implementations for LocalVariableInfoVisitor.
+    // Implementations for StackMapFrameVisitor.
 
-    public void visitLocalVariableInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableInfo localVariableInfo)
-    {
-        markCpEntry(classFile, localVariableInfo.u2nameIndex);
-        markCpEntry(classFile, localVariableInfo.u2descriptorIndex);
-    }
+    public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) {}
 
 
-    // Implementations for LocalVariableTypeInfoVisitor.
-
-    public void visitLocalVariableTypeInfo(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, LocalVariableTypeInfo localVariableTypeInfo)
+    public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
     {
-        markCpEntry(classFile, localVariableTypeInfo.u2nameIndex);
-        markCpEntry(classFile, localVariableTypeInfo.u2signatureIndex);
+        // Mark the constant pool entries referenced by the verification types.
+        sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
     }
 
 
-    // Implementations for AnnotationVisitor.
-
-    public void visitAnnotation(ClassFile classFile, Annotation annotation)
+    public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
     {
-        markCpEntry(classFile, annotation.u2typeIndex);
-
-        // Mark the annotation class.
-        annotation.referencedClassFileAccept(this);
-
-        // Mark the constant pool entries referenced by the element values.
-        annotation.elementValuesAccept(classFile, this);
+        // Mark the constant pool entries referenced by the verification types.
+        moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
     }
 
 
-    // Implementations for ElementValueVisitor.
-
-    public void visitConstantElementValue(ClassFile classFile, Annotation annotation, ConstantElementValue constantElementValue)
+    public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
     {
-        if (constantElementValue.u2elementName != 0)
-        {
-            markCpEntry(classFile, constantElementValue.u2elementName);
-
-            // Mark the annotation method.
-            constantElementValue.referencedMethodInfoAccept(this);
-        }
-
-        markCpEntry(classFile, constantElementValue.u2constantValueIndex);
+        // Mark the constant pool entries referenced by the verification types.
+        fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
+        fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
     }
 
 
-    public void visitEnumConstantElementValue(ClassFile classFile, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
-    {
-        if (enumConstantElementValue.u2elementName != 0)
-        {
-            markCpEntry(classFile, enumConstantElementValue.u2elementName);
+    // Implementations for VerificationTypeVisitor.
 
-            // Mark the annotation method.
-            enumConstantElementValue.referencedMethodInfoAccept(this);
-        }
-
-        markCpEntry(classFile, enumConstantElementValue.u2typeNameIndex);
-        markCpEntry(classFile, enumConstantElementValue.u2constantNameIndex);
-    }
+    public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
 
 
-    public void visitClassElementValue(ClassFile classFile, Annotation annotation, ClassElementValue classElementValue)
+    public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
     {
-        if (classElementValue.u2elementName != 0)
-        {
-            markCpEntry(classFile, classElementValue.u2elementName);
-
-            // Mark the annotation method.
-            classElementValue.referencedMethodInfoAccept(this);
-        }
-
-        // Mark the referenced class constant pool entry.
-        markCpEntry(classFile, classElementValue.u2classInfoIndex);
-
-        // Mark the referenced classes.
-        classElementValue.referencedClassesAccept(this);
+        markConstant(clazz, objectType.u2classIndex);
     }
 
 
-    public void visitAnnotationElementValue(ClassFile classFile, Annotation annotation, AnnotationElementValue annotationElementValue)
-    {
-        if (annotationElementValue.u2elementName != 0)
-        {
-            markCpEntry(classFile, annotationElementValue.u2elementName);
-
-            // Mark the annotation method.
-            annotationElementValue.referencedMethodInfoAccept(this);
-        }
+    // Implementations for LocalVariableInfoVisitor.
 
-        // Mark the constant pool entries referenced by the annotation.
-        annotationElementValue.annotationAccept(classFile, this);
+    public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+    {
+        markConstant(clazz, localVariableInfo.u2nameIndex);
+        markConstant(clazz, localVariableInfo.u2descriptorIndex);
     }
 
 
-    public void visitArrayElementValue(ClassFile classFile, Annotation annotation, ArrayElementValue arrayElementValue)
-    {
-        if (arrayElementValue.u2elementName != 0)
-        {
-            markCpEntry(classFile, arrayElementValue.u2elementName);
-
-            // Mark the annotation method.
-            arrayElementValue.referencedMethodInfoAccept(this);
-        }
+    // Implementations for LocalVariableTypeInfoVisitor.
 
-        // Mark the constant pool entries referenced by the element values.
-        arrayElementValue.elementValuesAccept(classFile, annotation, this);
-    }
+    public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+    {
+        markConstant(clazz, localVariableTypeInfo.u2nameIndex);
+        markConstant(clazz, localVariableTypeInfo.u2signatureIndex);
+    }
+
+
+//    // Implementations for AnnotationVisitor.
+//
+//    public void visitAnnotation(Clazz clazz, Annotation annotation)
+//    {
+//        markConstant(clazz, annotation.u2typeIndex);
+//
+//        // Mark the constant pool entries referenced by the element values.
+//        annotation.elementValuesAccept(clazz, this);
+//    }
+//
+//
+//    // Implementations for ElementValueVisitor.
+//
+//    public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
+//    {
+//        if (constantElementValue.u2elementNameIndex != 0)
+//        {
+//            markConstant(clazz, constantElementValue.u2elementNameIndex);
+//        }
+//
+//        markConstant(clazz, constantElementValue.u2constantValueIndex);
+//    }
+//
+//
+//    public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
+//    {
+//        if (enumConstantElementValue.u2elementNameIndex != 0)
+//        {
+//            markConstant(clazz, enumConstantElementValue.u2elementNameIndex);
+//        }
+//
+//        markConstant(clazz, enumConstantElementValue.u2typeNameIndex);
+//        markConstant(clazz, enumConstantElementValue.u2constantNameIndex);
+//    }
+//
+//
+//    public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
+//    {
+//        if (classElementValue.u2elementNameIndex != 0)
+//        {
+//            markConstant(clazz, classElementValue.u2elementNameIndex);
+//        }
+//
+//        // Mark the referenced class constant pool entry.
+//        markConstant(clazz, classElementValue.u2classInfoIndex);
+//    }
+//
+//
+//    public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
+//    {
+//        if (annotationElementValue.u2elementNameIndex != 0)
+//        {
+//            markConstant(clazz, annotationElementValue.u2elementNameIndex);
+//        }
+//
+//        // Mark the constant pool entries referenced by the annotation.
+//        annotationElementValue.annotationAccept(clazz, this);
+//    }
+//
+//
+//    public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
+//    {
+//        if (arrayElementValue.u2elementNameIndex != 0)
+//        {
+//            markConstant(clazz, arrayElementValue.u2elementNameIndex);
+//        }
+//
+//        // Mark the constant pool entries referenced by the element values.
+//        arrayElementValue.elementValuesAccept(clazz, annotation, this);
+//    }
 
 
     // Implementations for InstructionVisitor.
 
-    public void visitSimpleInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, SimpleInstruction simpleInstruction) {}
-    public void visitVariableInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, VariableInstruction variableInstruction) {}
-    public void visitBranchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, BranchInstruction branchInstruction) {}
-    public void visitTableSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, TableSwitchInstruction tableSwitchInstruction) {}
-    public void visitLookUpSwitchInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) {}
+    public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
 
 
-    public void visitCpInstruction(ClassFile classFile, MethodInfo methodInfo, CodeAttrInfo codeAttrInfo, int offset, CpInstruction cpInstruction)
+    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
     {
-        markCpEntry(classFile, cpInstruction.cpIndex);
+        markConstant(clazz, constantInstruction.constantIndex);
     }
 
 
@@ -863,8 +894,8 @@ public class UsageMarker
      * Marks the given constant pool entry of the given class. This includes
      * visiting any referenced objects.
      */
-    private void markCpEntry(ClassFile classFile, int index)
+    private void markConstant(Clazz clazz, int index)
     {
-         classFile.constantPoolEntryAccept(index, this);
+         clazz.constantPoolEntryAccept(index, this);
     }
 }
diff --git a/src/proguard/shrink/UsagePrinter.java b/src/proguard/shrink/UsagePrinter.java
index 0cda3ef..cff528b 100644
--- a/src/proguard/shrink/UsagePrinter.java
+++ b/src/proguard/shrink/UsagePrinter.java
@@ -1,6 +1,6 @@
-/* $Id: UsagePrinter.java,v 1.19.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -24,11 +24,11 @@ import proguard.classfile.*;
 import proguard.classfile.util.*;
 import proguard.classfile.visitor.*;
 
-import java.io.*;
+import java.io.PrintStream;
 
 
 /**
- * This ClassFileVisitor prints out the class files and class members that have been
+ * This ClassVisitor prints out the classes and class members that have been
  * marked as being used (or not used).
  *
  * @see UsageMarker
@@ -36,12 +36,13 @@ import java.io.*;
  * @author Eric Lafortune
  */
 public class UsagePrinter
-  implements ClassFileVisitor,
-             MemberInfoVisitor
+extends      SimplifiedVisitor
+implements   ClassVisitor,
+             MemberVisitor
 {
-    private UsageMarker usageMarker;
-    private boolean     printUnusedItems;
-    private PrintStream ps;
+    private final UsageMarker usageMarker;
+    private final boolean     printUnusedItems;
+    private final PrintStream ps;
 
     // A field to remember the class name, if a header is needed for class members.
     private String      className;
@@ -49,10 +50,11 @@ public class UsagePrinter
 
     /**
      * Creates a new UsagePrinter that prints to <code>System.out</code>.
-     * @param usageMarker    the usage marker that was used to mark the classes
-     *                       and class members.
-     * @param printUsedItems a flag that indicates whether only unused items
-     *                       should be printed, or alternatively, only used items.
+     * @param usageMarker      the usage marker that was used to mark the
+     *                         classes and class members.
+     * @param printUnusedItems a flag that indicates whether only unused items
+     *                         should be printed, or alternatively, only used
+     *                         items.
      */
     public UsagePrinter(UsageMarker usageMarker,
                         boolean     printUnusedItems)
@@ -63,11 +65,12 @@ public class UsagePrinter
 
     /**
      * Creates a new UsagePrinter that prints to the given stream.
-     * @param usageMarker    the usage marker that was used to mark the classes
-     *                       and class members.
-     * @param printUsedItems a flag that indicates whether only unused items
-     *                       should be printed, or alternatively, only used items.
-     * @param printStream the stream to which to print
+     * @param usageMarker      the usage marker that was used to mark the
+     *                         classes and class members.
+     * @param printUnusedItems a flag that indicates whether only unused items
+     *                         should be printed, or alternatively, only used
+     *                         items.
+     * @param printStream      the stream to which to print.
      */
     public UsagePrinter(UsageMarker usageMarker,
                         boolean     printUnusedItems,
@@ -79,80 +82,71 @@ public class UsagePrinter
     }
 
 
-    // Implementations for ClassFileVisitor.
+    // Implementations for ClassVisitor.
 
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
+    public void visitProgramClass(ProgramClass programClass)
     {
-        if (usageMarker.isUsed(programClassFile))
+        if (usageMarker.isUsed(programClass))
         {
             if (printUnusedItems)
             {
-                className = programClassFile.getName();
+                className = programClass.getName();
 
-                programClassFile.fieldsAccept(this);
-                programClassFile.methodsAccept(this);
+                programClass.fieldsAccept(this);
+                programClass.methodsAccept(this);
 
                 className = null;
             }
             else
             {
-                ps.println(ClassUtil.externalClassName(programClassFile.getName()));
+                ps.println(ClassUtil.externalClassName(programClass.getName()));
             }
         }
         else
         {
             if (printUnusedItems)
             {
-                ps.println(ClassUtil.externalClassName(programClassFile.getName()));
+                ps.println(ClassUtil.externalClassName(programClass.getName()));
             }
         }
     }
 
 
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-    }
-
+    // Implementations for MemberVisitor.
 
-    // Implementations for MemberInfoVisitor.
-
-    public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
     {
-        if (usageMarker.isUsed(programFieldInfo) ^ printUnusedItems)
+        if (usageMarker.isUsed(programField) ^ printUnusedItems)
         {
             printClassNameHeader();
 
             ps.println("    " +
-                       lineNumberRange(programClassFile, programFieldInfo) +
+                       lineNumberRange(programClass, programField) +
                        ClassUtil.externalFullFieldDescription(
-                           programFieldInfo.getAccessFlags(),
-                           programFieldInfo.getName(programClassFile),
-                           programFieldInfo.getDescriptor(programClassFile)));
+                           programField.getAccessFlags(),
+                           programField.getName(programClass),
+                           programField.getDescriptor(programClass)));
         }
     }
 
 
-    public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     {
-        if (usageMarker.isUsed(programMethodInfo) ^ printUnusedItems)
+        if (usageMarker.isUsed(programMethod) ^ printUnusedItems)
         {
             printClassNameHeader();
 
             ps.println("    " +
-                       lineNumberRange(programClassFile, programMethodInfo) +
+                       lineNumberRange(programClass, programMethod) +
                        ClassUtil.externalFullMethodDescription(
-                           programClassFile.getName(),
-                           programMethodInfo.getAccessFlags(),
-                           programMethodInfo.getName(programClassFile),
-                           programMethodInfo.getDescriptor(programClassFile)));
+                           programClass.getName(),
+                           programMethod.getAccessFlags(),
+                           programMethod.getName(programClass),
+                           programMethod.getDescriptor(programClass)));
         }
     }
 
 
-    public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo) {}
-    public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo) {}
-
-
     // Small utility methods.
 
     /**
@@ -173,9 +167,9 @@ public class UsagePrinter
      * Returns the line number range of the given class member, followed by a
      * colon, or just an empty String if no range is available.
      */
-    private static String lineNumberRange(ProgramClassFile programClassFile, ProgramMemberInfo programMemberInfo)
+    private static String lineNumberRange(ProgramClass programClass, ProgramMember programMember)
     {
-        String range = programMemberInfo.getLineNumberRange(programClassFile);
+        String range = programMember.getLineNumberRange(programClass);
         return range != null ?
             (range + ":") :
             "";
diff --git a/src/proguard/shrink/UsedClassFileFilter.java b/src/proguard/shrink/UsedClassFileFilter.java
deleted file mode 100644
index d16fe7f..0000000
--- a/src/proguard/shrink/UsedClassFileFilter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/* $Id: UsedClassFileFilter.java,v 1.11.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package proguard.shrink;
-
-import proguard.classfile.*;
-import proguard.classfile.visitor.*;
-
-
-/**
- * This ClassFileVisitor delegates all its method calls to another
- * ClassFileVisitor, but only for ClassFile objects that are marked as used.
- *
- * @see UsageMarker
- *
- * @author Eric Lafortune
- */
-public class UsedClassFileFilter
-  implements ClassFileVisitor
-{
-    private UsageMarker      usageMarker;
-    private ClassFileVisitor classFileVisitor;
-
-
-    /**
-     * Creates a new UsedClassFileFilter.
-     * @param usageMarker      the usage marker that is used to mark the classes
-     *                         and class members.
-     * @param classFileVisitor the class file visitor to which the visiting
-     *                         will be delegated.
-     */
-    public UsedClassFileFilter(UsageMarker      usageMarker,
-                                ClassFileVisitor classFileVisitor)
-    {
-        this.usageMarker      = usageMarker;
-        this.classFileVisitor = classFileVisitor;
-    }
-
-
-    // Implementations for ClassFileVisitor.
-
-    public void visitProgramClassFile(ProgramClassFile programClassFile)
-    {
-        if (usageMarker.isUsed(programClassFile))
-        {
-            classFileVisitor.visitProgramClassFile(programClassFile);
-        }
-    }
-
-
-    public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
-    {
-        if (usageMarker.isUsed(libraryClassFile))
-        {
-            classFileVisitor.visitLibraryClassFile(libraryClassFile);
-        }
-    }
-}
diff --git a/src/proguard/shrink/UsedClassFilter.java b/src/proguard/shrink/UsedClassFilter.java
new file mode 100644
index 0000000..54f94c7
--- /dev/null
+++ b/src/proguard/shrink/UsedClassFilter.java
@@ -0,0 +1,74 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.ClassVisitor;
+
+/**
+ * This ClassVisitor delegates all its method calls to another ClassVisitor,
+ * but only for Clazz objects that are marked as used.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class UsedClassFilter
+implements   ClassVisitor
+{
+    private final UsageMarker  usageMarker;
+    private final ClassVisitor classVisitor;
+
+
+    /**
+     * Creates a new UsedClassFilter.
+     * @param usageMarker  the usage marker that is used to mark the classes
+     *                     and class members.
+     * @param classVisitor the class visitor to which the visiting will be
+     *                     delegated.
+     */
+    public UsedClassFilter(UsageMarker  usageMarker,
+                           ClassVisitor classVisitor)
+    {
+        this.usageMarker  = usageMarker;
+        this.classVisitor = classVisitor;
+    }
+
+
+    // Implementations for ClassVisitor.
+
+    public void visitProgramClass(ProgramClass programClass)
+    {
+        if (usageMarker.isUsed(programClass))
+        {
+            classVisitor.visitProgramClass(programClass);
+        }
+    }
+
+
+    public void visitLibraryClass(LibraryClass libraryClass)
+    {
+        if (usageMarker.isUsed(libraryClass))
+        {
+            classVisitor.visitLibraryClass(libraryClass);
+        }
+    }
+}
diff --git a/src/proguard/shrink/UsedMemberFilter.java b/src/proguard/shrink/UsedMemberFilter.java
new file mode 100644
index 0000000..c4373ec
--- /dev/null
+++ b/src/proguard/shrink/UsedMemberFilter.java
@@ -0,0 +1,93 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This library is 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 library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.shrink;
+
+import proguard.classfile.*;
+import proguard.classfile.visitor.MemberVisitor;
+
+/**
+ * This MemberVisitor delegates all its method calls to another MemberVisitor,
+ * but only for Member objects that are marked as used.
+ *
+ * @see UsageMarker
+ *
+ * @author Eric Lafortune
+ */
+public class UsedMemberFilter
+implements   MemberVisitor
+{
+    private final UsageMarker   usageMarker;
+    private final MemberVisitor memberVisitor;
+
+
+    /**
+     * Creates a new UsedClassFilter.
+     * @param usageMarker   the usage marker that is used to mark the classes
+     *                      and class members.
+     * @param memberVisitor the member visitor to which the visiting will be
+     *                      delegated.
+     */
+    public UsedMemberFilter(UsageMarker   usageMarker,
+                            MemberVisitor memberVisitor)
+    {
+        this.usageMarker   = usageMarker;
+        this.memberVisitor = memberVisitor;
+    }
+
+
+    // Implementations for MemberVisitor.
+
+
+    public void visitProgramField(ProgramClass programClass, ProgramField programField)
+    {
+        if (usageMarker.isUsed(programField))
+        {
+            memberVisitor.visitProgramField(programClass, programField);
+        }
+    }
+
+
+    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
+    {
+        if (usageMarker.isUsed(programMethod))
+        {
+            memberVisitor.visitProgramMethod(programClass, programMethod);
+        }
+    }
+
+
+    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
+    {
+        if (usageMarker.isUsed(libraryField))
+        {
+            memberVisitor.visitLibraryField(libraryClass, libraryField);
+        }
+    }
+
+
+    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
+    {
+        if (usageMarker.isUsed(libraryMethod))
+        {
+            memberVisitor.visitLibraryMethod(libraryClass, libraryMethod);
+        }
+    }
+}
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/AndMatcher.java
similarity index 63%
copy from src/proguard/util/ExtensionMatcher.java
copy to src/proguard/util/AndMatcher.java
index 12311fa..f3be5d5 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/AndMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,22 +21,21 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher tests whether strings matches both given StringMatcher
+ * instances.
  *
  * @author Eric Lafortune
  */
-public class ExtensionMatcher implements StringMatcher
+public class AndMatcher implements StringMatcher
 {
-    private String extension;
+    private final StringMatcher matcher1;
+    private final StringMatcher matcher2;
 
 
-    /**
-     * Creates a new StringMatcher.
-     * @param extension the extension against which strings will be matched.
-     */
-    public ExtensionMatcher(String extension)
+    public AndMatcher(StringMatcher matcher1, StringMatcher matcher2)
     {
-        this.extension = extension;
+        this.matcher1 = matcher1;
+        this.matcher2 = matcher2;
     }
 
 
@@ -44,6 +43,7 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return matcher1.matches(string) &&
+               matcher2.matches(string);
     }
 }
diff --git a/src/proguard/util/BasicListMatcher.java b/src/proguard/util/BasicListMatcher.java
deleted file mode 100644
index 31035bd..0000000
--- a/src/proguard/util/BasicListMatcher.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/* $Id: BasicListMatcher.java,v 1.8 2005/11/05 19:29:25 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import java.util.*;
-
-/**
- * This StringMatcher tests whether strings match an entry in a given list of
- * regular expressions. The list is given as a comma-separated string or as a
- * List of strings. An exclamation mark preceding a list entry acts as a
- * negator: if the expression matches, a negative match is returned, without
- * considering any subsequent entries. If none of the entries match, a positive
- * match is returned depending on whether the last regular expression had a
- * negator or not.
- * <p>
- * The individual regular expression matching is delegated to a StringMatcher
- * that is created by the {@link #createBasicMatcher(String)} method. If it is
- * not overridden, this method returns a BasicMatcher.
- *
- * @see BasicMatcher
- * @author Eric Lafortune
- */
-public class BasicListMatcher implements StringMatcher
-{
-    private static final char REGULAR_EXPRESSION_SEPARATOR = ',';
-    private static final char REGULAR_EXPRESSION_NEGATOR   = '!';
-
-    private StringMatcher[] regularExpressionMatchers;
-    private boolean[]       negatedRegularExpressions;
-
-
-    /**
-     * Creates a new BasicListMatcher.
-     * @param regularExpression the comma-separated list of regular expressions
-     *                          against which strings will be matched.
-     */
-    public BasicListMatcher(String regularExpression)
-    {
-        this(ListUtil.commaSeparatedList(regularExpression));
-    }
-
-
-    /**
-     * Creates a new BasicListMatcher.
-     * @param regularExpressionList the list of regular expressions against which
-     *                              strings will be matched.
-     */
-    public BasicListMatcher(List regularExpressionList)
-    {
-        // Collect the regular expressions in arrays.
-        int regularExpressionCount = regularExpressionList.size();
-
-        regularExpressionMatchers = new StringMatcher[regularExpressionCount];
-        negatedRegularExpressions = new boolean[regularExpressionCount];
-
-        for (int index = 0; index < regularExpressionCount; index++)
-        {
-            String regularExpression = (String)regularExpressionList.get(index);
-
-            // Does the regular expression start with an exclamation mark?
-            if (regularExpression.length() > 0 &&
-                regularExpression.charAt(0) == REGULAR_EXPRESSION_NEGATOR)
-            {
-                // Trim the regular expression.
-                regularExpression = regularExpression.substring(1);
-
-                // Remember the negator.
-                negatedRegularExpressions[index] = true;
-            }
-
-            regularExpressionMatchers[index] =
-                createBasicMatcher(regularExpression);
-        }
-    }
-
-
-    /**
-     * Creates a new StringMatcher for the given regular expression.
-     */
-    protected StringMatcher createBasicMatcher(String regularExpression)
-    {
-        return new BasicMatcher(regularExpression);
-    }
-
-
-    // Implementations for StringMatcher.
-
-    public boolean matches(String string)
-    {
-        boolean result = true;
-
-        for (int index = 0; index < regularExpressionMatchers.length; index++)
-        {
-            result = negatedRegularExpressions[index];
-
-            if (regularExpressionMatchers[index].matches(string))
-            {
-                return !result;
-            }
-        }
-
-        return result;
-    }
-
-
-    /**
-     * A main method for testing string matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            BasicListMatcher matcher =
-                new BasicListMatcher(args[0]);
-
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/BasicMatcher.java b/src/proguard/util/BasicMatcher.java
deleted file mode 100644
index 50b36f2..0000000
--- a/src/proguard/util/BasicMatcher.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/* $Id: BasicMatcher.java,v 1.8 2005/11/05 19:29:25 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import java.util.*;
-
-/**
- * This StringMatcher tests whether strings match a given regular
- * expression. Supported wildcards are
- * <ul>
- * <li>'?'  for a single Java identifier character or other wildcard
- *          matching character,
- * <li>'*'  for any number of Java identifier characters or other wildcard
- *          matching characters, and
- * <li>'**' for any number of Java identifier characters or extended wildcard
- *          matching characters,
- * <li>'%'  for a single special wildcard matching character.
- * </ul>
- * The sets of wildcard characters, extended wildcard characters, and special
- * wildcard characters can be defined by the user.
- *
- * @author Eric Lafortune
- */
-public class BasicMatcher implements StringMatcher
-{
-    private static final String SINGLE_CHARACTER_WILDCARD     = "?";
-    private static final String MULTIPLE_CHARACTERS_WILDCARD1 = "*";
-    private static final String MULTIPLE_CHARACTERS_WILDCARD2 = "**";
-    private static final String SPECIAL_CHARACTER_WILDCARD    = "%";
-
-    private String[] expressionParts;
-    private char[]   wildcardCharacters;
-    private char[]   extendedWildcardCharacters;
-    private char[]   specialWildcardCharacters;
-
-
-    /**
-     * Creates a new BasicMatcher without extra wildcard matching
-     * characters.
-     * @param regularExpression the regular expression against which strings
-     *                          will be matched.
-     */
-    public BasicMatcher(String regularExpression)
-    {
-        this(regularExpression, null, null, null);
-    }
-
-
-    /**
-     * Creates a new BasicMatcher.
-     * @param regularExpression          the regular expression against which
-     *                                   strings will be matched.
-     * @param wildcardCharacters         an optional extra list of wildcard
-     *                                   matching characters.
-     * @param extendedWildcardCharacters an optional extra list of extended
-     *                                   wildcard matching characters.
-     */
-    public BasicMatcher(String regularExpression,
-                        char[] wildcardCharacters,
-                        char[] extendedWildcardCharacters,
-                        char[] specialWildcardCharacters)
-    {
-        this.wildcardCharacters         = wildcardCharacters;
-        this.extendedWildcardCharacters = extendedWildcardCharacters;
-        this.specialWildcardCharacters  = specialWildcardCharacters;
-
-        // Split the given regular expression into an array of parts: "?",
-        // "*", "**", "%", and simple text strings.
-
-        // A List to collect the subsequent regular expression parts.
-        List expressionPartsList = new ArrayList();
-
-        String wildcard      = null;
-        int    previousIndex = 0;
-        int    index         = 0;
-        int    regularExpressionLength = regularExpression.length();
-        while (index < regularExpressionLength)
-        {
-            wildcard =
-                regularExpression.regionMatches(index, MULTIPLE_CHARACTERS_WILDCARD2, 0, MULTIPLE_CHARACTERS_WILDCARD2.length()) ? MULTIPLE_CHARACTERS_WILDCARD2 :
-                regularExpression.regionMatches(index, MULTIPLE_CHARACTERS_WILDCARD1, 0, MULTIPLE_CHARACTERS_WILDCARD1.length()) ? MULTIPLE_CHARACTERS_WILDCARD1 :
-                regularExpression.regionMatches(index, SINGLE_CHARACTER_WILDCARD,     0, SINGLE_CHARACTER_WILDCARD.length()) ?     SINGLE_CHARACTER_WILDCARD     :
-                regularExpression.regionMatches(index, SPECIAL_CHARACTER_WILDCARD,    0, SINGLE_CHARACTER_WILDCARD.length()) ?     SPECIAL_CHARACTER_WILDCARD    :
-                                                                                                                                   null;
-            if (wildcard != null)
-            {
-                // Add the simple text string that we've skipped.
-                if (previousIndex < index)
-                {
-                    expressionPartsList.add(regularExpression.substring(previousIndex, index));
-                }
-
-                // Add the wildcard that we've found.
-                expressionPartsList.add(wildcard);
-
-                // We'll continue parsing after this wildcard.
-                index += wildcard.length();
-                previousIndex = index;
-            }
-            else
-            {
-                // We'll continue parsing at the next character.
-                index++;
-            }
-        }
-
-        // Add the final simple text string that we've skipped, if any.
-        if (wildcard == null)
-        {
-            expressionPartsList.add(regularExpression.substring(previousIndex));
-        }
-
-        // Copy the List into the array.
-        expressionParts = new String[expressionPartsList.size()];
-        expressionPartsList.toArray(expressionParts);
-    }
-
-
-    // Implementations for StringMatcher.
-
-    public boolean matches(String string)
-    {
-        return matches(string, 0, 0);
-    }
-
-
-    /**
-     * Tries to match the given string, starting at the given index, with the
-     * regular expression parts starting at the given index.
-     */
-    private boolean matches(String string,
-                            int    stringStartIndex,
-                            int    expressionIndex)
-    {
-        // Are we out of expression parts?
-        if (expressionIndex == expressionParts.length)
-        {
-            // There's a match, at least if we're at the end of the string as well.
-            return stringStartIndex == string.length();
-        }
-
-        String expressionPart = expressionParts[expressionIndex];
-
-        // Did we get a wildcard of some sort?
-        if (expressionPart.equals(SINGLE_CHARACTER_WILDCARD))
-        {
-            // Do we have any characters left to match?
-            if (stringStartIndex == string.length())
-            {
-                // We've run out of characters.
-                return false;
-            }
-
-            // Make sure we're matching an allowed character and then check if
-            // the rest of the expression parts match.
-            return
-                matchesWildcard(string.charAt(stringStartIndex)) &&
-                matches(string, stringStartIndex + 1, expressionIndex + 1);
-        }
-        else if (expressionPart.equals(MULTIPLE_CHARACTERS_WILDCARD1))
-        {
-            // Try out all possible matches for '*', not matching the package
-            // separator.
-            for (int stringEndIndex = stringStartIndex;
-                 stringEndIndex <= string.length();
-                 stringEndIndex++)
-            {
-                // Are we matching some characters already?
-                if (stringEndIndex > stringStartIndex)
-                {
-                    // Make sure we don't start matching the wrong characters.
-                    if (!matchesWildcard(string.charAt(stringEndIndex-1)))
-                    {
-                        // We can never get a match.
-                        return false;
-                    }
-                }
-
-                // Continue looking for a match of the next expression part,
-                // starting from the end index.
-                if (matches(string, stringEndIndex, expressionIndex + 1))
-                {
-                    return true;
-                }
-            }
-
-            // We could get a match for '*', but not for the rest of the
-            // expression parts.
-            return false;
-        }
-        else if (expressionPart.equals(MULTIPLE_CHARACTERS_WILDCARD2))
-        {
-            // Try out all possible matches for '**'.
-            for (int stringEndIndex = stringStartIndex;
-                 stringEndIndex <= string.length();
-                 stringEndIndex++)
-            {
-                // Are we matching some characters already?
-                if (stringEndIndex > stringStartIndex)
-                {
-                    // Make sure we don't start matching the wrong characters.
-                    if (!matchesExtendedWildcard(string.charAt(stringEndIndex-1)))
-                    {
-                        // We can never get a match.
-                        return false;
-                    }
-                }
-
-                // Continue looking for a match of the next expression part,
-                // starting from this index.
-                if (matches(string, stringEndIndex, expressionIndex + 1))
-                {
-                    return true;
-                }
-            }
-
-            // We could get a match for '**', but not for the rest of the
-            // expression parts.
-            return stringStartIndex == string.length();
-        }
-        else if (expressionPart.equals(SPECIAL_CHARACTER_WILDCARD))
-        {
-            // Do we have any characters left to match?
-            if (stringStartIndex == string.length())
-            {
-                // We've run out of characters.
-                return false;
-            }
-
-            // Make sure we're matching an allowed character and then check if
-            // the rest of the expression parts match.
-            return
-                matchesSpecialWildcard(string.charAt(stringStartIndex)) &&
-                matches(string, stringStartIndex + 1, expressionIndex + 1);
-        }
-        else
-        {
-            // The expression part is a simple text string. Check if it matches,
-            // and if the rest of the expression parts match.
-            int expressionPartLength = expressionPart.length();
-            return
-                string.regionMatches(stringStartIndex, expressionPart, 0, expressionPartLength) &&
-                matches(string, stringStartIndex + expressionPartLength, expressionIndex + 1);
-        }
-    }
-
-
-    /**
-     * Returns whether the given character matches a simple '?' or '*' wildcard.
-     */
-    private boolean matchesWildcard(char character)
-    {
-        if (Character.isJavaIdentifierPart(character))
-        {
-            return true;
-        }
-
-        if (wildcardCharacters != null)
-        {
-            for (int index = 0; index < wildcardCharacters.length; index++)
-            {
-                if (character == wildcardCharacters[index])
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Returns whether the given character matches an extended '**' wildcard.
-     */
-    private boolean matchesExtendedWildcard(char character)
-    {
-        if (matchesWildcard(character))
-        {
-            return true;
-        }
-
-        if (extendedWildcardCharacters != null)
-        {
-            for (int index = 0; index < extendedWildcardCharacters.length; index++)
-            {
-                if (character == extendedWildcardCharacters[index])
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * Returns whether the given character matches a special '%' wildcard.
-     */
-    private boolean matchesSpecialWildcard(char character)
-    {
-        if (specialWildcardCharacters != null)
-        {
-            for (int index = 0; index < specialWildcardCharacters.length; index++)
-            {
-                if (character == specialWildcardCharacters[index])
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-
-    /**
-     * A main method for testing string matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            BasicMatcher matcher =
-                new BasicMatcher(args[0], null, new char[] {'/'}, null);
-
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/ClassNameListMatcher.java b/src/proguard/util/ClassNameListMatcher.java
deleted file mode 100644
index 3b96c2f..0000000
--- a/src/proguard/util/ClassNameListMatcher.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/* $Id: ClassNameListMatcher.java,v 1.6 2005/11/05 19:29:25 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import java.util.List;
-
-/**
- * This StringMatcher tests whether internal class names match any
- * entry in a given list of regular expressions.
- *
- * @see BasicListMatcher
- * @see ClassNameMatcher
- *
- * @author Eric Lafortune
- */
-public class ClassNameListMatcher extends BasicListMatcher
-{
-    /**
-     * Creates a new ClassNameListMatcher.
-     * @param regularExpression the comma-separated list of regular expressions
-     *                          against which strings will be matched.
-     */
-    public ClassNameListMatcher(String regularExpression)
-    {
-        super(regularExpression);
-    }
-
-
-    /**
-     * Creates a new ClassNameListMatcher.
-     * @param regularExpressionList the list of regular expressions against which
-     *                              strings will be matched.
-     */
-    public ClassNameListMatcher(List regularExpressionList)
-    {
-        super(regularExpressionList);
-    }
-
-
-    // Overridden method of BasicListMatcher
-
-    protected StringMatcher createBasicMatcher(String regularExpression)
-    {
-        return new ClassNameMatcher(regularExpression);
-    }
-
-
-    /**
-     * A main method for testing file name matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            ClassNameListMatcher matcher = new ClassNameListMatcher(args[0]);
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/ClassNameMatcher.java b/src/proguard/util/ClassNameMatcher.java
deleted file mode 100644
index f4a8f67..0000000
--- a/src/proguard/util/ClassNameMatcher.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/* $Id: ClassNameMatcher.java,v 1.6.2.1 2006/10/18 21:12:47 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import proguard.classfile.ClassConstants;
-
-/**
- * This StringMatcher tests whether internal class names match a
- * given regular expression.
- * Supported wildcards are
- * '?'  for a single Java identifier character,
- * '*'  for any number of regular Java identifier characters, and
- * '**' for any number of regular Java identifier characters or package separator
- *      characters.
- * '%'  for a single internal primitive type character (Z, B, C, S, I, F, J, or D),
- *
- * @author Eric Lafortune
- */
-public class ClassNameMatcher extends BasicMatcher
-{
-    private static final char[] CLASS_NAME_CHARACTERS = new char[]
-    {
-        ClassConstants.INTERNAL_SPECIAL_CHARACTER
-    };
-
-    private static final char[] EXTENDED_CLASS_NAME_CHARACTERS = new char[]
-    {
-        ClassConstants.INTERNAL_PACKAGE_SEPARATOR
-    };
-
-    private static final char[] SPECIAL_PRIMITIVE_CHARACTERS = new char[]
-    {
-        ClassConstants.INTERNAL_TYPE_BOOLEAN,
-        ClassConstants.INTERNAL_TYPE_BYTE,
-        ClassConstants.INTERNAL_TYPE_CHAR,
-        ClassConstants.INTERNAL_TYPE_SHORT,
-        ClassConstants.INTERNAL_TYPE_INT,
-        ClassConstants.INTERNAL_TYPE_FLOAT,
-        ClassConstants.INTERNAL_TYPE_LONG,
-        ClassConstants.INTERNAL_TYPE_DOUBLE
-    };
-
-
-    /**
-     * Creates a new ClassNameMatcher.
-     * @param regularExpression the regular expression against which strings
-     *                          will be matched.
-     */
-    public ClassNameMatcher(String regularExpression)
-    {
-        super(regularExpression,
-              CLASS_NAME_CHARACTERS,
-              EXTENDED_CLASS_NAME_CHARACTERS,
-              SPECIAL_PRIMITIVE_CHARACTERS);
-    }
-
-
-    /**
-     * A main method for testing class name matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            ClassNameMatcher matcher = new ClassNameMatcher(args[0]);
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/ClassNameParser.java b/src/proguard/util/ClassNameParser.java
new file mode 100644
index 0000000..16020a0
--- /dev/null
+++ b/src/proguard/util/ClassNameParser.java
@@ -0,0 +1,216 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import proguard.classfile.ClassConstants;
+
+/**
+ * This StringParser can create StringMatcher instances for regular expressions
+ * matching internal class names (or descriptors containing class names).
+ * The regular expressions can contain the following wildcards:
+ * '%'     for a single internal primitive type character (V, Z, B, C, S, I, F,
+ *         J, or D),
+ * '?'     for a single regular class name character,
+ * '*'     for any number of regular class name characters,
+ * '**'    for any number of regular class name characters or package separator
+ *         characters ('/'),
+ * 'L***;' for a single internal type (class name or primitive type,
+ *         array or non-array), and
+ * 'L///;' for any number of internal types (class names and primitive
+ *         types).
+ *
+ * @author Eric Lafortune
+ */
+public class ClassNameParser implements StringParser
+{
+    private static final char[] INTERNAL_PRIMITIVE_TYPES = new char[]
+    {
+        ClassConstants.INTERNAL_TYPE_VOID,
+        ClassConstants.INTERNAL_TYPE_BOOLEAN,
+        ClassConstants.INTERNAL_TYPE_BYTE,
+        ClassConstants.INTERNAL_TYPE_CHAR,
+        ClassConstants.INTERNAL_TYPE_SHORT,
+        ClassConstants.INTERNAL_TYPE_INT,
+        ClassConstants.INTERNAL_TYPE_LONG,
+        ClassConstants.INTERNAL_TYPE_FLOAT,
+        ClassConstants.INTERNAL_TYPE_DOUBLE,
+    };
+
+
+    // Implementations for StringParser.
+
+    public StringMatcher parse(String regularExpression)
+    {
+        int           index;
+        StringMatcher nextMatcher = new EmptyStringMatcher();
+
+        // Look for wildcards.
+        for (index = 0; index < regularExpression.length(); index++)
+        {
+            // Is there an 'L///;' wildcard?
+            if (regularExpression.regionMatches(index, "L///;", 0, 5))
+            {
+                SettableMatcher settableMatcher = new SettableMatcher();
+
+                // Create a matcher, recursively, for the remainder of the
+                // string, optionally preceded by any type.
+                nextMatcher =
+                    new OrMatcher(parse(regularExpression.substring(index + 5)),
+                                  createAnyTypeMatcher(settableMatcher));
+
+                settableMatcher.setMatcher(nextMatcher);
+
+                break;
+            }
+
+            // Is there an 'L***;' wildcard?
+            if (regularExpression.regionMatches(index, "L***;", 0, 5))
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    createAnyTypeMatcher(parse(regularExpression.substring(index + 5)));
+                break;
+            }
+
+            // Is there a '**' wildcard?
+            if (regularExpression.regionMatches(index, "**", 0, 2))
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              new char[] { ClassConstants.INTERNAL_TYPE_CLASS_END },
+                                              0,
+                                              Integer.MAX_VALUE,
+                                              parse(regularExpression.substring(index + 2)));
+                break;
+            }
+
+            // Is there a '*' wildcard?
+            else if (regularExpression.charAt(index) == '*')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              new char[] { ClassConstants.INTERNAL_TYPE_CLASS_END, ClassConstants.INTERNAL_PACKAGE_SEPARATOR },
+                                              0,
+                                              Integer.MAX_VALUE,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+
+            // Is there a '?' wildcard?
+            else if (regularExpression.charAt(index) == '?')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              new char[] { ClassConstants.INTERNAL_TYPE_CLASS_END, ClassConstants.INTERNAL_PACKAGE_SEPARATOR },
+                                              1,
+                                              1,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+
+            // Is there a '%' wildcard?
+            else if (regularExpression.charAt(index) == '%')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(INTERNAL_PRIMITIVE_TYPES,
+                                              null,
+                                              1,
+                                              1,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+        }
+
+        // Return a matcher for the fixed first part of the regular expression,
+        // if any, and the remainder.
+        return index != 0 ?
+            (StringMatcher)new FixedStringMatcher(regularExpression.substring(0, index), nextMatcher) :
+            (StringMatcher)nextMatcher;
+    }
+
+
+    // Small utility methods.
+
+
+    /**
+     * Creates a StringMatcher that matches any type (class or primitive type,
+     * array or non-array) and then the given matcher.
+     */
+    private VariableStringMatcher createAnyTypeMatcher(StringMatcher nextMatcher)
+    {
+        return new VariableStringMatcher(new char[] { ClassConstants.INTERNAL_TYPE_ARRAY },
+                                  null,
+                                  0,
+                                  255,
+        new OrMatcher(
+        new VariableStringMatcher(INTERNAL_PRIMITIVE_TYPES,
+                                  null,
+                                  1,
+                                  1,
+                                  nextMatcher),
+        new VariableStringMatcher(new char[] { ClassConstants.INTERNAL_TYPE_CLASS_START },
+                                  null,
+                                  1,
+                                  1,
+        new VariableStringMatcher(null,
+                                  new char[] { ClassConstants.INTERNAL_TYPE_CLASS_END },
+                                  0,
+                                  Integer.MAX_VALUE,
+        new VariableStringMatcher(new char[] { ClassConstants.INTERNAL_TYPE_CLASS_END },
+                                  null,
+                                  1,
+                                  1,
+                                  nextMatcher)))));
+    }
+
+
+    /**
+     * A main method for testing class name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            ClassNameParser parser  = new ClassNameParser();
+            StringMatcher  matcher = parser.parse(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/util/EmptyStringMatcher.java
similarity index 68%
copy from src/proguard/gui/splash/VariableFont.java
copy to src/proguard/util/EmptyStringMatcher.java
index 3c134cb..cfa0a73 100644
--- a/src/proguard/gui/splash/VariableFont.java
+++ b/src/proguard/util/EmptyStringMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: VariableFont.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,19 +18,19 @@
  * 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.splash;
-
-import java.awt.*;
+package proguard.util;
 
 /**
- * This interface represents a Font that varies with time.
+ * This StringMatcher tests whether strings are empty.
  *
  * @author Eric Lafortune
  */
-interface VariableFont
+public class EmptyStringMatcher implements StringMatcher
 {
-    /**
-     * Returns the Font for the given time.
-     */
-    public Font getFont(long time);
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        return string.length() == 0;
+    }
 }
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/ExtensionMatcher.java
index 12311fa..9e5b795 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/ExtensionMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,13 +21,14 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher tests whether strings end in a given extension, ignoring
+ * its case.
  *
  * @author Eric Lafortune
  */
 public class ExtensionMatcher implements StringMatcher
 {
-    private String extension;
+    private final String extension;
 
 
     /**
@@ -44,6 +45,19 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return endsWithIgnoreCase(string, extension);
+    }
+
+
+    /**
+     * Returns whether the given string ends with the given suffix, ignoring its
+     * case.
+     */
+    private static boolean endsWithIgnoreCase(String string, String suffix)
+    {
+        int stringLength = string.length();
+        int suffixLength = suffix.length();
+
+        return string.regionMatches(true, stringLength - suffixLength, suffix, 0, suffixLength);
     }
 }
diff --git a/src/proguard/util/FileNameListMatcher.java b/src/proguard/util/FileNameListMatcher.java
deleted file mode 100644
index 5efafae..0000000
--- a/src/proguard/util/FileNameListMatcher.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/* $Id: FileNameListMatcher.java,v 1.6 2005/11/05 19:29:25 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import java.util.List;
-
-/**
- * This StringMatcher tests whether file names match any entry in a
- * given list of regular expressions.
- *
- * @see BasicListMatcher
- * @see FileNameMatcher
- *
- * @author Eric Lafortune
- */
-public class FileNameListMatcher extends BasicListMatcher
-{
-    /**
-     * Creates a new FileNameListMatcher.
-     * @param regularExpression the comma-separated list of regular expressions
-     *                          against which strings will be matched.
-     */
-    public FileNameListMatcher(String regularExpression)
-    {
-        super(regularExpression);
-    }
-
-
-    /**
-     * Creates a new FileNameListMatcher.
-     * @param regularExpressionList the list of regular expressions against which
-     *                              strings will be matched.
-     */
-    public FileNameListMatcher(List regularExpressionList)
-    {
-        super(regularExpressionList);
-    }
-
-
-    // Overridden method of BasicListMatcher
-
-    protected StringMatcher createBasicMatcher(String regularExpression)
-    {
-        return new FileNameMatcher(regularExpression);
-    }
-
-
-    /**
-     * A main method for testing file name matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            FileNameListMatcher matcher = new FileNameListMatcher(args[0]);
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/FileNameMatcher.java b/src/proguard/util/FileNameMatcher.java
deleted file mode 100644
index d190e68..0000000
--- a/src/proguard/util/FileNameMatcher.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/* $Id: FileNameMatcher.java,v 1.7.2.1 2006/04/15 21:30:28 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
- *
- * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
- *
- * This program is 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.util;
-
-import proguard.classfile.ClassConstants;
-
-import java.io.*;
-
-/**
- * This StringMatcher tests whether file names match a given regular
- * expression.
- * Supported wildcards are
- * '?'  for a single regular file name character,
- * '*'  for any number of regular file name characters, and
- * '**' for any number of regular file name characters or directory separator
- *      characters (always including '/').
- *
- * @author Eric Lafortune
- */
-public class FileNameMatcher extends BasicMatcher
-{
-    private static final char[] FILE_NAME_CHARACTERS = new char[]
-    {
-        ' ',
-        '-',
-        '+',
-        '.'
-    };
-
-    private static final char[] EXTENDED_FILE_NAME_CHARACTERS = new char[]
-    {
-        ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
-        File.separatorChar
-    };
-
-
-    /**
-     * Creates a new FileNameMatcher.
-     * @param regularExpression the regular expression against which strings
-     *                          will be matched.
-     */
-    public FileNameMatcher(String regularExpression)
-    {
-        super(regularExpression,
-              FILE_NAME_CHARACTERS,
-              EXTENDED_FILE_NAME_CHARACTERS,
-              null);
-    }
-
-
-    /**
-     * A main method for testing file name matching.
-     */
-    public static void main(String[] args)
-    {
-        try
-        {
-            System.out.println("Regular expression ["+args[0]+"]");
-            FileNameMatcher matcher = new FileNameMatcher(args[0]);
-            for (int index = 1; index < args.length; index++)
-            {
-                String string = args[index];
-                System.out.print("String             ["+string+"]");
-                System.out.println(" -> match = "+matcher.matches(args[index]));
-            }
-        }
-        catch (Exception ex)
-        {
-            ex.printStackTrace();
-        }
-    }
-}
diff --git a/src/proguard/util/FileNameParser.java b/src/proguard/util/FileNameParser.java
new file mode 100644
index 0000000..5f67387
--- /dev/null
+++ b/src/proguard/util/FileNameParser.java
@@ -0,0 +1,121 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.io.File;
+
+/**
+ * This StringParser can create StringMatcher instances for regular expressions
+ * matching file names. The regular expressions can contain the following
+ * wildcards:
+ * '?'  for a single regular file name character,
+ * '*'  for any number of regular file name characters, and
+ * '**' for any number of regular file name characters or directory separator
+ *      characters (always including '/').
+ *
+ * @author Eric Lafortune
+ */
+public class FileNameParser implements StringParser
+{
+    // Implementations for StringParser.
+
+    public StringMatcher parse(String regularExpression)
+    {
+        int           index;
+        StringMatcher nextMatcher = new EmptyStringMatcher();
+
+        // Look for wildcards.
+        for (index = 0; index < regularExpression.length(); index++)
+        {
+            // Is there a '**' wildcard?
+            if (regularExpression.regionMatches(index, "**", 0, 2))
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              null,
+                                              0,
+                                              Integer.MAX_VALUE,
+                                              parse(regularExpression.substring(index + 2)));
+                break;
+            }
+
+            // Is there a '*' wildcard?
+            else if (regularExpression.charAt(index) == '*')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              new char[] { File.pathSeparatorChar, '/' },
+                                              0,
+                                              Integer.MAX_VALUE,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+
+            // Is there a '?' wildcard?
+            else if (regularExpression.charAt(index) == '?')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              new char[] { File.pathSeparatorChar, '/' },
+                                              1,
+                                              1,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+        }
+
+        // Return a matcher for the fixed first part of the regular expression,
+        // if any, and the remainder.
+        return index != 0 ?
+            (StringMatcher)new FixedStringMatcher(regularExpression.substring(0, index), nextMatcher) :
+            (StringMatcher)nextMatcher;
+    }
+
+
+    /**
+     * A main method for testing file name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            FileNameParser parser  = new FileNameParser();
+            StringMatcher  matcher = parser.parse(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/FixedStringMatcher.java
similarity index 56%
copy from src/proguard/util/ExtensionMatcher.java
copy to src/proguard/util/FixedStringMatcher.java
index 12311fa..c4eb639 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/FixedStringMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,22 +21,27 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher tests whether strings start with a given fixed string
+ * and then match another given StringMatcher.
  *
  * @author Eric Lafortune
  */
-public class ExtensionMatcher implements StringMatcher
+public class FixedStringMatcher implements StringMatcher
 {
-    private String extension;
+    private final String        fixedString;
+    private final StringMatcher nextMatcher;
+
+
+    public FixedStringMatcher(String fixedString)
+    {
+        this(fixedString, null);
+    }
 
 
-    /**
-     * Creates a new StringMatcher.
-     * @param extension the extension against which strings will be matched.
-     */
-    public ExtensionMatcher(String extension)
+    public FixedStringMatcher(String fixedString, StringMatcher nextMatcher)
     {
-        this.extension = extension;
+        this.fixedString = fixedString;
+        this.nextMatcher = nextMatcher;
     }
 
 
@@ -44,6 +49,7 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return string.startsWith(fixedString) &&
+               nextMatcher.matches(string.substring(fixedString.length()));
     }
 }
diff --git a/src/proguard/util/ListMatcher.java b/src/proguard/util/ListMatcher.java
new file mode 100644
index 0000000..ec170a2
--- /dev/null
+++ b/src/proguard/util/ListMatcher.java
@@ -0,0 +1,69 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+/**
+ * This StringMatcher tests whether strings match a given list of StringMatcher
+ * instances. The instances are considered sequentially. Each instance in the
+ * list can optionally be negated, meaning that a match makes the entire
+ * remaining match fail.
+ *
+ * @author Eric Lafortune
+ */
+public class ListMatcher implements StringMatcher
+{
+    private final StringMatcher[] matchers;
+    private final boolean[]       negate;
+
+
+    public ListMatcher(StringMatcher[] matchers)
+    {
+        this(matchers, null);
+    }
+
+
+    public ListMatcher(StringMatcher[] matchers, boolean[] negate)
+    {
+        this.matchers = matchers;
+        this.negate   = negate;
+    }
+
+
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        // Check the list of matchers.
+        for (int index = 0; index < matchers.length; index++)
+        {
+            StringMatcher matcher = matchers[index];
+            if (matcher.matches(string))
+            {
+                return negate == null ||
+                       !negate[index];
+            }
+        }
+
+        return negate != null &&
+               negate[negate.length - 1];
+
+    }
+}
diff --git a/src/proguard/util/ListParser.java b/src/proguard/util/ListParser.java
new file mode 100644
index 0000000..d93c053
--- /dev/null
+++ b/src/proguard/util/ListParser.java
@@ -0,0 +1,131 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+import java.util.List;
+
+/**
+ * This StringParser can create StringMatcher instances for regular expressions.
+ * The regular expressions are either presented as a list, or they are
+ * interpreted as comma-separated lists, optionally prefixed with '!' negators.
+ * If an entry with a negator matches, a negative match is returned, without
+ * considering any subsequent entries in the list. The creation of StringMatcher
+ * instances  for the entries is delegated to the given StringParser.
+ *
+ * @author Eric Lafortune
+ */
+public class ListParser implements StringParser
+{
+    private final StringParser stringParser;
+
+
+    public ListParser(StringParser stringParser)
+    {
+        this.stringParser = stringParser;
+    }
+
+
+    // Implementations for StringParser.
+
+    public StringMatcher parse(String regularExpression)
+    {
+        // Does the regular expression contain a ',' list separator?
+        return parse(ListUtil.commaSeparatedList(regularExpression));
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Creates a StringMatcher for the given regular expression, which can
+     * be a list of optionally negated simple entries.
+     */
+    public StringMatcher parse(List regularExpressions)
+    {
+        StringMatcher listMatcher = null;
+
+        // Loop over all simple regular expressions, backward, creating a
+        // linked list of matchers.
+        for (int index = regularExpressions.size()-1; index >=0; index--)
+        {
+            String regularExpression = (String)regularExpressions.get(index);
+
+            StringMatcher entryMatcher = parseEntry(regularExpression);
+
+            // Prepend the entry matcher.
+            listMatcher =
+                listMatcher == null ?
+                    (StringMatcher)entryMatcher :
+                isNegated(regularExpression) ?
+                    (StringMatcher)new AndMatcher(entryMatcher, listMatcher) :
+                    (StringMatcher)new OrMatcher(entryMatcher, listMatcher);
+        }
+
+        return listMatcher;
+    }
+
+
+    /**
+     * Creates a StringMatcher for the given regular expression, which is a
+     * an optionally negated simple expression.
+     */
+    private StringMatcher parseEntry(String regularExpression)
+    {
+        // Wrap the matcher if the regular expression starts with a '!' negator.
+        return isNegated(regularExpression) ?
+          new NotMatcher(stringParser.parse(regularExpression.substring(1))) :
+          stringParser.parse(regularExpression);
+    }
+
+
+    /**
+     * Returns whether the given simple regular expression is negated.
+     */
+    private boolean isNegated(String regularExpression)
+    {
+        return regularExpression.length() > 0 &&
+               regularExpression.charAt(0) == '!';
+    }
+
+
+    /**
+     * A main method for testing name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            ListParser parser  = new ListParser(new NameParser());
+            StringMatcher  matcher = parser.parse(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ListUtil.java b/src/proguard/util/ListUtil.java
index e269394..03fbdd0 100644
--- a/src/proguard/util/ListUtil.java
+++ b/src/proguard/util/ListUtil.java
@@ -1,6 +1,6 @@
-/* $Id: ListUtil.java,v 1.6.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/util/NameParser.java b/src/proguard/util/NameParser.java
new file mode 100644
index 0000000..4fdad49
--- /dev/null
+++ b/src/proguard/util/NameParser.java
@@ -0,0 +1,106 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+/**
+ * This StringParser can create StringMatcher instances for regular expressions
+ * matching names. The regular expressions are interpreted as comma-separated
+ * lists of names, optionally prefixed with '!' negators.
+ * If a name with a negator matches, a negative match is returned, without
+ * considering any subsequent entries in the list.
+ * Names can contain the following wildcards:
+ * '?'  for a single character, and
+ * '*'  for any number of characters.
+ *
+ * @author Eric Lafortune
+ */
+public class NameParser implements StringParser
+{
+    // Implementations for StringParser.
+
+    public StringMatcher parse(String regularExpression)
+    {
+        int           index;
+        StringMatcher nextMatcher = new EmptyStringMatcher();
+
+        // Look for wildcards.
+        for (index = 0; index < regularExpression.length(); index++)
+        {
+            // Is there a '*' wildcard?
+            if (regularExpression.charAt(index) == '*')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              null,
+                                              0,
+                                              Integer.MAX_VALUE,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+
+            // Is there a '?' wildcard?
+            else if (regularExpression.charAt(index) == '?')
+            {
+                // Create a matcher for the wildcard and, recursively, for the
+                // remainder of the string.
+                nextMatcher =
+                    new VariableStringMatcher(null,
+                                              null,
+                                              1,
+                                              1,
+                                              parse(regularExpression.substring(index + 1)));
+                break;
+            }
+        }
+
+        // Return a matcher for the fixed first part of the regular expression,
+        // if any, and the remainder.
+        return index != 0 ?
+            (StringMatcher)new FixedStringMatcher(regularExpression.substring(0, index), nextMatcher) :
+            (StringMatcher)nextMatcher;
+    }
+
+
+    /**
+     * A main method for testing name matching.
+     */
+    public static void main(String[] args)
+    {
+        try
+        {
+            System.out.println("Regular expression ["+args[0]+"]");
+            NameParser parser  = new NameParser();
+            StringMatcher  matcher = parser.parse(args[0]);
+            for (int index = 1; index < args.length; index++)
+            {
+                String string = args[index];
+                System.out.print("String             ["+string+"]");
+                System.out.println(" -> match = "+matcher.matches(args[index]));
+            }
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/NotMatcher.java
similarity index 63%
copy from src/proguard/util/ExtensionMatcher.java
copy to src/proguard/util/NotMatcher.java
index 12311fa..19beb88 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/NotMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,22 +21,19 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher tests whether strings does not match the given
+ * StringMatcher.
  *
  * @author Eric Lafortune
  */
-public class ExtensionMatcher implements StringMatcher
+public class NotMatcher implements StringMatcher
 {
-    private String extension;
+    private final StringMatcher matcher;
 
 
-    /**
-     * Creates a new StringMatcher.
-     * @param extension the extension against which strings will be matched.
-     */
-    public ExtensionMatcher(String extension)
+    public NotMatcher(StringMatcher matcher)
     {
-        this.extension = extension;
+        this.matcher = matcher;
     }
 
 
@@ -44,6 +41,6 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return !matcher.matches(string);
     }
 }
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/OrMatcher.java
similarity index 63%
copy from src/proguard/util/ExtensionMatcher.java
copy to src/proguard/util/OrMatcher.java
index 12311fa..dbd8119 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/OrMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,22 +21,21 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher tests whether strings matches either of the given
+ *  StringMatcher instances.
  *
  * @author Eric Lafortune
  */
-public class ExtensionMatcher implements StringMatcher
+public class OrMatcher implements StringMatcher
 {
-    private String extension;
+    private final StringMatcher matcher1;
+    private final StringMatcher matcher2;
 
 
-    /**
-     * Creates a new StringMatcher.
-     * @param extension the extension against which strings will be matched.
-     */
-    public ExtensionMatcher(String extension)
+    public OrMatcher(StringMatcher matcher1, StringMatcher matcher2)
     {
-        this.extension = extension;
+        this.matcher1 = matcher1;
+        this.matcher2 = matcher2;
     }
 
 
@@ -44,6 +43,7 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return matcher1.matches(string) ||
+               matcher2.matches(string);
     }
 }
diff --git a/src/proguard/util/ExtensionMatcher.java b/src/proguard/util/SettableMatcher.java
similarity index 63%
copy from src/proguard/util/ExtensionMatcher.java
copy to src/proguard/util/SettableMatcher.java
index 12311fa..8f0954e 100644
--- a/src/proguard/util/ExtensionMatcher.java
+++ b/src/proguard/util/SettableMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: ExtensionMatcher.java,v 1.4.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -21,22 +21,19 @@
 package proguard.util;
 
 /**
- * This StringMatcher tests whether strings end in a given extension.
+ * This StringMatcher delegates to a another StringMatcher that can be set
+ * after this StringMatcher has been constructed.  
  *
  * @author Eric Lafortune
  */
-public class ExtensionMatcher implements StringMatcher
+public class SettableMatcher implements StringMatcher
 {
-    private String extension;
+    private StringMatcher matcher;
 
 
-    /**
-     * Creates a new StringMatcher.
-     * @param extension the extension against which strings will be matched.
-     */
-    public ExtensionMatcher(String extension)
+    public void setMatcher(StringMatcher matcher)
     {
-        this.extension = extension;
+        this.matcher = matcher;
     }
 
 
@@ -44,6 +41,6 @@ public class ExtensionMatcher implements StringMatcher
 
     public boolean matches(String string)
     {
-        return string.endsWith(extension);
+        return matcher.matches(string);
     }
 }
diff --git a/src/proguard/util/StringMatcher.java b/src/proguard/util/StringMatcher.java
index 92dd665..571ace6 100644
--- a/src/proguard/util/StringMatcher.java
+++ b/src/proguard/util/StringMatcher.java
@@ -1,6 +1,6 @@
-/* $Id: StringMatcher.java,v 1.2 2004/08/15 12:39:30 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002 Eric Lafortune (eric at graphics.cornell.edu)
  *
diff --git a/src/proguard/gui/splash/VariableFont.java b/src/proguard/util/StringParser.java
similarity index 69%
copy from src/proguard/gui/splash/VariableFont.java
copy to src/proguard/util/StringParser.java
index 3c134cb..9a81586 100644
--- a/src/proguard/gui/splash/VariableFont.java
+++ b/src/proguard/util/StringParser.java
@@ -1,6 +1,6 @@
-/* $Id: VariableFont.java,v 1.6.2.2 2007/01/18 21:31:52 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -18,19 +18,18 @@
  * 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.splash;
-
-import java.awt.*;
+package proguard.util;
 
 /**
- * This interface represents a Font that varies with time.
+ * This interface provides a method to create a SringMatcher for a given
+ * regular expression.
  *
  * @author Eric Lafortune
  */
-interface VariableFont
+public interface StringParser
 {
     /**
-     * Returns the Font for the given time.
+     * Creates a StringMatcher for the given regular expression.
      */
-    public Font getFont(long time);
+    public StringMatcher parse(String regularExpression);
 }
diff --git a/src/proguard/util/VariableStringMatcher.java b/src/proguard/util/VariableStringMatcher.java
new file mode 100644
index 0000000..3594156
--- /dev/null
+++ b/src/proguard/util/VariableStringMatcher.java
@@ -0,0 +1,126 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
+ *
+ * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.util;
+
+/**
+ * This StringMatcher tests whether strings start with a specified variable
+ * string and then match another given StringMatcher.
+ *
+ * @author Eric Lafortune
+ */
+public class VariableStringMatcher implements StringMatcher
+{
+    private final char[]        allowedCharacters;
+    private final char[]        disallowedCharacters;
+    private final int           minimumLength;
+    private final int           maximumLength;
+    private final StringMatcher nextMatcher;
+
+
+    public VariableStringMatcher(char[]        allowedCharacters,
+                                 char[]        disallowedCharacters,
+                                 int           minimumLength,
+                                 int           maximumLength,
+                                 StringMatcher nextMatcher)
+    {
+        this.allowedCharacters    = allowedCharacters;
+        this.disallowedCharacters = disallowedCharacters;
+        this.minimumLength        = minimumLength;
+        this.maximumLength        = maximumLength;
+        this.nextMatcher          = nextMatcher;
+    }
+
+    // Implementations for StringMatcher.
+
+    public boolean matches(String string)
+    {
+        if (string.length() < minimumLength)
+        {
+            return false;
+        }
+
+        // Check the first minimum number of characters.
+        for (int index = 0; index < minimumLength; index++)
+        {
+            if (!isAllowedCharacter(string.charAt(index)))
+            {
+                return false;
+            }
+        }
+
+        int maximumLength = Math.min(this.maximumLength, string.length());
+
+        // Check the remaining characters, up to the maximum number.
+        for (int index = minimumLength; index < maximumLength; index++)
+        {
+            if (nextMatcher.matches(string.substring(index)))
+            {
+                return true;
+            }
+
+            if (!isAllowedCharacter(string.charAt(index)))
+            {
+                return false;
+            }
+        }
+
+        // Check the remaining characters in the string.
+        return nextMatcher.matches(string.substring(maximumLength));
+    }
+
+
+    // Small utility methods.
+
+    /**
+     * Returns whether the given character is allowed in the variable string.
+     */
+    private boolean isAllowedCharacter(char character)
+    {
+        // Check the allowed characters.
+        if (allowedCharacters != null)
+        {
+            for (int index = 0; index < allowedCharacters.length; index++)
+            {
+                if (allowedCharacters[index] == character)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        // Check the disallowed characters.
+        if (disallowedCharacters != null)
+        {
+            for (int index = 0; index < disallowedCharacters.length; index++)
+            {
+                if (disallowedCharacters[index] == character)
+                {
+                    return false;
+                }
+            }
+        }
+
+        // Any remaining character is allowed.
+        return true;
+    }
+}
diff --git a/src/proguard/wtk/ProGuardObfuscator.java b/src/proguard/wtk/ProGuardObfuscator.java
index f51958e..062a475 100644
--- a/src/proguard/wtk/ProGuardObfuscator.java
+++ b/src/proguard/wtk/ProGuardObfuscator.java
@@ -1,6 +1,6 @@
-/* $Id: ProGuardObfuscator.java,v 1.15.2.2 2007/01/18 21:31:53 eric Exp $
- *
- * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ *             of Java bytecode.
  *
  * Copyright (c) 2002-2007 Eric Lafortune (eric at graphics.cornell.edu)
  *
@@ -20,12 +20,10 @@
  */
 package proguard.wtk;
 
-import com.sun.kvem.environment.*;
+import com.sun.kvem.environment.Obfuscator;
 import proguard.*;
-import proguard.classfile.*;
 
 import java.io.*;
-import java.util.*;
 
 
 /**
@@ -87,7 +85,7 @@ public class ProGuardObfuscator implements Obfuscator
             configuration.programJars.add(new ClassPathEntry(new File(jarFileName), false));
             configuration.programJars.add(new ClassPathEntry(obfuscatedJarFile, true));
 
-            // The preverify tool seems to unpack the resulting class files,
+            // The preverify tool seems to unpack the resulting classes,
             // so we must not use mixed-case class names on Windows.
             configuration.useMixedCaseClassNames =
                 !System.getProperty("os.name").regionMatches(true, 0, "windows", 0, 7);
diff --git a/src/proguard/wtk/default.pro b/src/proguard/wtk/default.pro
index d5e068c..ca7535e 100644
--- a/src/proguard/wtk/default.pro
+++ b/src/proguard/wtk/default.pro
@@ -1,6 +1,7 @@
 -dontnote
+-microedition
 -overloadaggressively
--defaultpackage ''
+-repackageclasses ''
 -allowaccessmodification
 
 # Keep all extensions of javax.microedition.midlet.MIDlet.

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